ffplay에서 녹색 화면 표시 : Live555를 사용하여 RTP 스트림을 통해 H264 비디오로 스트리밍 데스크탑 (DirectX 표면)


9

Windows10에서 Live555 및 Windows 미디어 기반의 하드웨어 인코더를 사용하여 RTP 스트림을 통해 H264 비디오로 데스크톱 (NV12 형식의 DirectX 표면)을 스트리밍하려고하며 ffplay (ffmpeg 4.2)로 렌더링 될 것으로 기대합니다. 하지만 아래와 같은 녹색 화면 만 표시됩니다.

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

I는 언급 MFWebCamToRTP 미디어 파운데이션 샘플하드웨어를 사용하여 인코딩 MFT 다이렉트 표면 대신 웹캠 다이렉트 표면에 대한 입력 소스 live555 FramedSource의 구현 및 변경.

다음은 DirectX 표면에서 입력 샘플을 공급하기 위해 Live555의 doGetNextFrame 콜백을 구현 한 것입니다.

virtual void doGetNextFrame()
{
    if (!_isInitialised)
    {
        if (!initialise()) {
            printf("Video device initialisation failed, stopping.");
            return;
        }
        else {
            _isInitialised = true;
        }
    }

    //if (!isCurrentlyAwaitingData()) return;

    DWORD processOutputStatus = 0;
    HRESULT mftProcessOutput = S_OK;
    MFT_OUTPUT_STREAM_INFO StreamInfo;
    IMFMediaBuffer *pBuffer = NULL;
    IMFSample *mftOutSample = NULL;
    DWORD mftOutFlags;
    bool frameSent = false;
    bool bTimeout = false;

    // Create sample
    CComPtr<IMFSample> videoSample = NULL;

    // Create buffer
    CComPtr<IMFMediaBuffer> inputBuffer;
    // Get next event
    CComPtr<IMFMediaEvent> event;
    HRESULT hr = eventGen->GetEvent(0, &event);
    CHECK_HR(hr, "Failed to get next event");

    MediaEventType eventType;
    hr = event->GetType(&eventType);
    CHECK_HR(hr, "Failed to get event type");


    switch (eventType)
    {
    case METransformNeedInput:
        {
            hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), surface, 0, FALSE, &inputBuffer);
            CHECK_HR(hr, "Failed to create IMFMediaBuffer");

            hr = MFCreateSample(&videoSample);
            CHECK_HR(hr, "Failed to create IMFSample");
            hr = videoSample->AddBuffer(inputBuffer);
            CHECK_HR(hr, "Failed to add buffer to IMFSample");

            if (videoSample)
            {
                _frameCount++;

                CHECK_HR(videoSample->SetSampleTime(mTimeStamp), "Error setting the video sample time.\n");
                CHECK_HR(videoSample->SetSampleDuration(VIDEO_FRAME_DURATION), "Error getting video sample duration.\n");

                // Pass the video sample to the H.264 transform.

                hr = _pTransform->ProcessInput(inputStreamID, videoSample, 0);
                CHECK_HR(hr, "The resampler H264 ProcessInput call failed.\n");

                mTimeStamp += VIDEO_FRAME_DURATION;
            }
        }

        break;

    case METransformHaveOutput:

        {
            CHECK_HR(_pTransform->GetOutputStatus(&mftOutFlags), "H264 MFT GetOutputStatus failed.\n");

            if (mftOutFlags == MFT_OUTPUT_STATUS_SAMPLE_READY)
            {
                MFT_OUTPUT_DATA_BUFFER _outputDataBuffer;
                memset(&_outputDataBuffer, 0, sizeof _outputDataBuffer);
                _outputDataBuffer.dwStreamID = outputStreamID;
                _outputDataBuffer.dwStatus = 0;
                _outputDataBuffer.pEvents = NULL;
                _outputDataBuffer.pSample = nullptr;

                mftProcessOutput = _pTransform->ProcessOutput(0, 1, &_outputDataBuffer, &processOutputStatus);

                if (mftProcessOutput != MF_E_TRANSFORM_NEED_MORE_INPUT)
                {
                    if (_outputDataBuffer.pSample) {

                        //CHECK_HR(_outputDataBuffer.pSample->SetSampleTime(mTimeStamp), "Error setting MFT sample time.\n");
                        //CHECK_HR(_outputDataBuffer.pSample->SetSampleDuration(VIDEO_FRAME_DURATION), "Error setting MFT sample duration.\n");

                        IMFMediaBuffer *buf = NULL;
                        DWORD bufLength;
                        CHECK_HR(_outputDataBuffer.pSample->ConvertToContiguousBuffer(&buf), "ConvertToContiguousBuffer failed.\n");
                        CHECK_HR(buf->GetCurrentLength(&bufLength), "Get buffer length failed.\n");
                        BYTE * rawBuffer = NULL;

                        fFrameSize = bufLength;
                        fDurationInMicroseconds = 0;
                        gettimeofday(&fPresentationTime, NULL);

                        buf->Lock(&rawBuffer, NULL, NULL);
                        memmove(fTo, rawBuffer, fFrameSize);

                        FramedSource::afterGetting(this);

                        buf->Unlock();
                        SafeRelease(&buf);

                        frameSent = true;
                        _lastSendAt = GetTickCount();

                        _outputDataBuffer.pSample->Release();
                    }

                    if (_outputDataBuffer.pEvents)
                        _outputDataBuffer.pEvents->Release();
                }

                //SafeRelease(&pBuffer);
                //SafeRelease(&mftOutSample);

                break;
            }
        }

        break;
    }

    if (!frameSent)
    {
        envir().taskScheduler().triggerEvent(eventTriggerId, this);
    }

    return;

done:

    printf("MediaFoundationH264LiveSource doGetNextFrame failed.\n");
    envir().taskScheduler().triggerEvent(eventTriggerId, this);
}

초기화 방법 :

bool initialise()
{
    HRESULT hr;
    D3D11_TEXTURE2D_DESC desc = { 0 };

    HDESK CurrentDesktop = nullptr;
    CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);
    if (!CurrentDesktop)
    {
        // We do not have access to the desktop so request a retry
        return false;
    }

    // Attach desktop to this thread
    bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0;
    CloseDesktop(CurrentDesktop);
    CurrentDesktop = nullptr;
    if (!DesktopAttached)
    {
        printf("SetThreadDesktop failed\n");
    }

    UINT32 activateCount = 0;

    // h264 output
    MFT_REGISTER_TYPE_INFO info = { MFMediaType_Video, MFVideoFormat_H264 };

    UINT32 flags =
        MFT_ENUM_FLAG_HARDWARE |
        MFT_ENUM_FLAG_SORTANDFILTER;

    // ------------------------------------------------------------------------
    // Initialize D3D11
    // ------------------------------------------------------------------------

    // Driver types supported
    D3D_DRIVER_TYPE DriverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT NumDriverTypes = ARRAYSIZE(DriverTypes);

    // Feature levels supported
    D3D_FEATURE_LEVEL FeatureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };
    UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);

    D3D_FEATURE_LEVEL FeatureLevel;

    // Create device
    for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
    {
        hr = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr,
            D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
            FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &device, &FeatureLevel, &context);
        if (SUCCEEDED(hr))
        {
            // Device creation success, no need to loop anymore
            break;
        }
    }

    CHECK_HR(hr, "Failed to create device");

    // Create device manager
    UINT resetToken;
    hr = MFCreateDXGIDeviceManager(&resetToken, &deviceManager);
    CHECK_HR(hr, "Failed to create DXGIDeviceManager");

    hr = deviceManager->ResetDevice(device, resetToken);
    CHECK_HR(hr, "Failed to assign D3D device to device manager");


    // ------------------------------------------------------------------------
    // Create surface
    // ------------------------------------------------------------------------
    desc.Format = DXGI_FORMAT_NV12;
    desc.Width = surfaceWidth;
    desc.Height = surfaceHeight;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.SampleDesc.Count = 1;

    hr = device->CreateTexture2D(&desc, NULL, &surface);
    CHECK_HR(hr, "Could not create surface");

    hr = MFTEnumEx(
        MFT_CATEGORY_VIDEO_ENCODER,
        flags,
        NULL,
        &info,
        &activateRaw,
        &activateCount
    );
    CHECK_HR(hr, "Failed to enumerate MFTs");

    CHECK(activateCount, "No MFTs found");

    // Choose the first available encoder
    activate = activateRaw[0];

    for (UINT32 i = 0; i < activateCount; i++)
        activateRaw[i]->Release();

    // Activate
    hr = activate->ActivateObject(IID_PPV_ARGS(&_pTransform));
    CHECK_HR(hr, "Failed to activate MFT");

    // Get attributes
    hr = _pTransform->GetAttributes(&attributes);
    CHECK_HR(hr, "Failed to get MFT attributes");

    // Unlock the transform for async use and get event generator
    hr = attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
    CHECK_HR(hr, "Failed to unlock MFT");

    eventGen = _pTransform;
    CHECK(eventGen, "Failed to QI for event generator");

    // Get stream IDs (expect 1 input and 1 output stream)
    hr = _pTransform->GetStreamIDs(1, &inputStreamID, 1, &outputStreamID);
    if (hr == E_NOTIMPL)
    {
        inputStreamID = 0;
        outputStreamID = 0;
        hr = S_OK;
    }
    CHECK_HR(hr, "Failed to get stream IDs");

     // ------------------------------------------------------------------------
    // Configure hardware encoder MFT
   // ------------------------------------------------------------------------
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager.p)), "Failed to set device manager.\n");

    // Set low latency hint
    hr = attributes->SetUINT32(MF_LOW_LATENCY, TRUE);
    CHECK_HR(hr, "Failed to set MF_LOW_LATENCY");

    hr = MFCreateMediaType(&outputType);
    CHECK_HR(hr, "Failed to create media type");

    hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    CHECK_HR(hr, "Failed to set MF_MT_MAJOR_TYPE on H264 output media type");

    hr = outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
    CHECK_HR(hr, "Failed to set MF_MT_SUBTYPE on H264 output media type");

    hr = outputType->SetUINT32(MF_MT_AVG_BITRATE, TARGET_AVERAGE_BIT_RATE);
    CHECK_HR(hr, "Failed to set average bit rate on H264 output media type");

    hr = MFSetAttributeSize(outputType, MF_MT_FRAME_SIZE, desc.Width, desc.Height);
    CHECK_HR(hr, "Failed to set frame size on H264 MFT out type");

    hr = MFSetAttributeRatio(outputType, MF_MT_FRAME_RATE, TARGET_FRAME_RATE, 1);
    CHECK_HR(hr, "Failed to set frame rate on H264 MFT out type");

    hr = outputType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
    CHECK_HR(hr, "Failed to set MF_MT_INTERLACE_MODE on H.264 encoder MFT");

    hr = outputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
    CHECK_HR(hr, "Failed to set MF_MT_ALL_SAMPLES_INDEPENDENT on H.264 encoder MFT");

    hr = _pTransform->SetOutputType(outputStreamID, outputType, 0);
    CHECK_HR(hr, "Failed to set output media type on H.264 encoder MFT");

    hr = MFCreateMediaType(&inputType);
    CHECK_HR(hr, "Failed to create media type");

    for (DWORD i = 0;; i++)
    {
        inputType = nullptr;
        hr = _pTransform->GetInputAvailableType(inputStreamID, i, &inputType);
        CHECK_HR(hr, "Failed to get input type");

        hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
        CHECK_HR(hr, "Failed to set MF_MT_MAJOR_TYPE on H264 MFT input type");

        hr = inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
        CHECK_HR(hr, "Failed to set MF_MT_SUBTYPE on H264 MFT input type");

        hr = MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, desc.Width, desc.Height);
        CHECK_HR(hr, "Failed to set MF_MT_FRAME_SIZE on H264 MFT input type");

        hr = MFSetAttributeRatio(inputType, MF_MT_FRAME_RATE, TARGET_FRAME_RATE, 1);
        CHECK_HR(hr, "Failed to set MF_MT_FRAME_RATE on H264 MFT input type");

        hr = _pTransform->SetInputType(inputStreamID, inputType, 0);
        CHECK_HR(hr, "Failed to set input type");

        break;
    }

    CheckHardwareSupport();

    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL), "Failed to process FLUSH command on H.264 MFT.\n");
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL), "Failed to process BEGIN_STREAMING command on H.264 MFT.\n");
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL), "Failed to process START_OF_STREAM command on H.264 MFT.\n");

    return true;

done:

    printf("MediaFoundationH264LiveSource initialisation failed.\n");
    return false;
}


    HRESULT CheckHardwareSupport()
    {
        IMFAttributes *attributes;
        HRESULT hr = _pTransform->GetAttributes(&attributes);
        UINT32 dxva = 0;

        if (SUCCEEDED(hr))
        {
            hr = attributes->GetUINT32(MF_SA_D3D11_AWARE, &dxva);
        }

        if (SUCCEEDED(hr))
        {
            hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
        }

#if defined(CODECAPI_AVLowLatencyMode) // Win8 only

        hr = _pTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI));

        if (SUCCEEDED(hr))
        {
            VARIANT var = { 0 };

            // FIXME: encoder only
            var.vt = VT_UI4;
            var.ulVal = 0;

            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncMPVDefaultBPictureCount, &var);

            var.vt = VT_BOOL;
            var.boolVal = VARIANT_TRUE;
            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonLowLatency, &var);
            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRealTime, &var);

            hr = attributes->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);

            if (SUCCEEDED(hr))
            {
                var.vt = VT_UI4;
                var.ulVal = eAVEncCommonRateControlMode_Quality;
                hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var);

                // This property controls the quality level when the encoder is not using a constrained bit rate. The AVEncCommonRateControlMode property determines whether the bit rate is constrained.
                VARIANT quality;
                InitVariantFromUInt32(50, &quality);
                hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQuality, &quality);
            }
        }
#endif

        return hr;
    }

ffplay 명령 :

ffplay -protocol_whitelist file,udp,rtp -i test.sdp -x 800 -y 600 -profile:v baseline

SDP :

v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
t=0 0
c=IN IP4 127.0.0.1
m=video 1234 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1

나는 내가 무엇을 놓치고 있는지 알지 못하고 거의 일주일 동안이 문제를 해결하려고 노력했지만 거의 모든 것을 시도했습니다. 또한 비디오로 DirectX 표면을 인코딩하기위한 온라인 리소스는 매우 제한적입니다.

도움을 주시면 감사하겠습니다.


1
METransformNeedInput 후에 doGetNextFrame이 다시 호출 될 것으로 잘못 생각합니다. 유효한 ProcessOutput 호출을 얻을 때까지 내부에서 반복해야 할 수도 있습니다.
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } 위의 두 블록은 인코더에서 출력을 얻을 때까지 ProcessInput 호출을 잘 처리합니다. 나는 같은 것을 확인했다. @VuVirt
Ram

frameSent가 참이면 어떻게됩니까? 이 경우 새로운 이벤트를 트리거합니까? 그 후에 "반환"진술이 있습니다.
VuVirt

@VuVirt 루프에서 기본 live555 라이브러리에 의해 자동으로 호출됩니다. "ProcessInput"및 "ProcessOutput"은 switch in 이벤트의 이벤트에 따라 다르게 호출됩니다. ProcessOut에서 연속 스트림을 얻었지만 볼 수는 없습니다. 샘플 시간과 지속 시간을 올바르게 설정하고 있다고 확신합니다.
Ram

1
ProcessOutput에서 MF_E_TRANSFORM_STREAM_CHANGE를 수신하는지 여부를 확인하고 그에 따라 형식 변경을 처리해야합니다.
VuVirt

답변:


6

생각보다 어렵습니다.

IMFTransform 인터페이스를 직접 호출하여 인코더를 사용하려는 경우 RGB 프레임을 NV12로 변환해야합니다. 좋은 성능을 원한다면 GPU에서 수행해야합니다. 픽셀 쉐이더를 사용하고, 2 개의 프레임을 렌더링하고, 풀 사이즈 1을 DXGI_FORMAT_R8_UNORM 렌더 대상에 밝기로, 절반 크기를 DXGI_FORMAT_R8G8_UNORM 타겟에 컬러로 렌더링하고, 2 개의 픽셀 쉐이더를 작성하여 NV12 값을 생성 할 수 있습니다. 두 렌더링 대상은 모두 동일한 NV12 텍스처의 평면으로 렌더링 할 수 있지만 Windows 8 이후에만 가능합니다.

다른 방법은 싱크 라이터를 사용하는 것 입니다. 동시에 여러 MFT를 호스팅 할 수 있으므로 VRAM에서 RGB 텍스처를 제공 할 수 있습니다. 싱크 라이터는 먼저 하나의 MFT (엔코더처럼 GPU 드라이버로 구현 된 독점 하드웨어 일 수 있음)로 NV12로 변환합니다. 인코더 MFT로 전달하십시오. mp4 파일로 인코딩하는 것은 비교적 쉽습니다. MFCreateSinkWriterFromURL API를 사용하여 작성기를 작성하십시오. 싱크 라이터에서 원시 샘플을 가져 오는 것은 훨씬 어렵지만 사용자 지정 미디어 싱크, 비디오 스트림에 대한 사용자 지정 스트림 싱크를 구현하고 MFCreateSinkWriterFromMediaSink 를 호출 하여 작성기를 만들어야합니다.

더있다.

인코딩 방법에 관계없이 프레임 텍스처를 재사용 할 수 없습니다. DD에서 얻을 수있는 각 프레임마다 새 텍스처를 만들어 MF에 전달해야합니다.

비디오 인코더는 일정한 프레임 속도를 기대합니다. DD는 화면에서 무언가가 바뀔 때마다 프레임을 제공합니다. 게임 모니터가있는 경우 144FPS가 될 수 있고 커서 만 깜박이는 경우 2FPS가 될 수 있습니다. 비디오 미디어 유형에 지정된 일정한 프레임 속도로 MF에 프레임을 제출하는 것이 이상적입니다.

네트워크로 스트리밍하려면 파라미터 세트를 제공하지 않아도됩니다. 당신이 인텔 하드웨어 H265 인코더 사용하지 않는 인텔의 댓글 없음으로 깨진을 , MF 당신이 제공하는 데이터 MF_MT_MPEG_SEQUENCE_HEADER 속성 IMFMediaTypeHandler 인터페이스에 SetCurrentMediaType를 호출하여 미디어 유형의. 해당 인터페이스를 구현하여 알림을받을 수 있습니다. 인코딩을 시작한 후에 만 ​​해당 데이터를 얻을 수 있습니다. 그것은 싱크 라이터를 사용하는 경우 IMFTransform더 쉬운 방법이기 때문에 메소드에서 MF_E_TRANSFORM_STREAM_CHANGE코드를 가져온 ProcessOutput다음 GetOutputAvailableType해당 매직 블롭으로 업데이트 된 미디어 유형을 가져 오기 위해 호출 해야합니다.


D3D11_CREATE_DEVICE_VIDEO_SUPPORT 및 표면 설명자를 사용하여 장치를 DXGI_FORMAT_NV12로 초기화하고 변환에서 MFT_MESSAGE_SET_D3D_MANAGER를 설정 한 경우에도 DirectX (데스크탑 복제)가 NV12 형식의 프레임을 제공하지 않습니까? 나도 RGB 버퍼를 NV12 또는 지원되는 입력 형식 (대부분 YUV 변형)으로 명시 적으로 변환하거나 SinkWriter를 사용해야한다고 생각했다. 그러나이 사람은 어떻게 든 내 접근 방식으로 그것을 달성 할 수있었습니다. stackoverflow.com/questions/43432670/…
Ram


1
@Ram Desktop 이중화는 항상 RGB 프레임 DXGI_FORMAT_B8G8R8A8_UNORM형식을 제공 합니다. H264 및 h265 엔코더 MFT는 NV12 만 지원하고 다른 이상한 커플도 지원합니다. 누군가 변환해야합니다. 데스크탑 복제를 사용합니다. 이미 Windows 7을 지원할 수 없습니다. 싱크 라이터를 사용하십시오. RGB를 NV12로 변환하는이 nVidia / Intel의 하드웨어 MFT가 픽셀 쉐이더 ALU보다 전력 효율이 더 높다고 확신합니다. 하드웨어에서 순전히 구현되었을 것입니다.
Soonts

네 말이 맞아 색상 변환은 명시 적으로 수행해야합니다. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . 나는 그 방향으로 진행하고 있습니다.
Ram

1
@Ram 작동해야합니다. 전에 해봤습니다. DD가 업데이트가 없어서 새 프레임을 제공하지 않으면 동일한 텍스처를 인코더에 다시 제출하여 많은 VRAM을 절약 할 수 있습니다. DD에 나중에 새 프레임이있는 경우에만 새 텍스처를 만듭니다. 그러나 언제 프레임을 제출해야하는지와 대기 시간을 감지하는 코드는 간단하지 않습니다. QueryPerformanceCounter를 사용하여 시간을 측정했으며 마지막 몇 프레임에서 롤링 평균이 캡처 또는 잠자기 상태인지 확인했습니다. BTW, 올바른 수면 방법은 IDXGIOutput :: WaitForVBlank 방법입니다.
Soonts

1

ffplay스트림 매개 변수에 대해 불평하고 있기 때문에 SPS / PPS를 선택할 수 없다고 가정합니다. 당신은 당신의 하드 SDP에서 그들을 설정하지 않은 - 참조 RFC-3984 및 대한 모습 sprop-parameter-sets. RFC의 예 :

m = 비디오 49170 RTP / AVP 98
a = rtpmap : 98 H264 / 90000
a = fmtp : 98 프로파일 수준 id = 42A01E; 스 프롭 매개 변수 세트 = Z0IACpZTBYmI, aMljiA ==

나는 ffplaySDP에서 이것을 기대하고 있다고 강력하게 생각 합니다. 미디어 기반 인코더에서 SPS / PPS를 얻는 방법을 기억하지 못하지만 샘플 페이로드에 있으며 적절한 NAL 단위를 찾거나 Google에서 추가 데이터를 추출하는 방법을 Google에서 추출해야합니다. 인코더- 내가 기대 했던 첫 번째 히트 .


유효한 포인트입니다. 나도 SPS / PPS에 용의자를 가지고 있습니다. 그래도 아직 확인하지 않았습니다. 희망을주는 MSDN 스레드로 안내해 주셔서 감사합니다.
Ram

@ Ram은 SPS / PPS가 샘플 페이로드에 있다는 좋은 변화가 있으므로 먼저 확인해야합니다.
Rudolfs Bundulis

네, 이해합니다 Mpeg4MediaSink를 통해 파일에 샘플을 쓰려고 할 때 미디어 기반 인코더에서 직접 SPS / PPS를 검색하고 구문 분석하는 데 대한 지식이 있습니다. 나는이 방향으로 앞으로 나아갈 것이다.
Ram

1

Soonts는 문제 해결에 필요한 모든 것을 제공합니다.

가장 먼저해야 할 일은 DXGI_FORMAT_B8G8R8A8_UNORM과 MFVideoFormat_NV12 사이의 형식 변환입니다.

형식 변환

형식 변환 정보

모든 텍스처가 GPU에 유지되므로 성능이 향상되므로 셰이더를 사용하여 형식 변환을 수행하는 것이 좋습니다.

첫 번째 단계입니다. 당신은 다른 사람들에게 당신의 프로그램을 향상시킬 것입니다.


1
2x4 이미지는 NV12에서 24 바이트가 아닌 12 바이트를 사용합니다. 여기에는 8 개의 밝기 값이 있지만 컬러 이미지는 두 배 작은 1x2 픽셀이므로 2x4 이미지의 색상 정보의 경우 총 4 바이트, U의 경우 2 바이트, V의 경우 2 바이트
Soonts

네 맞습니다. 다운 샘플링을 NV12 형식의 4.2.0으로 생략했습니다. 더 적합한 다이어그램을 만들려고 노력할 것입니다.
mofo77
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.