Mendapatkan layar hijau di ffplay: Streaming desktop (permukaan DirectX) sebagai video H264 melalui aliran RTP menggunakan Live555

9

Saya mencoba untuk melakukan streaming desktop (permukaan DirectX dalam format NV12) sebagai video H264 melalui aliran RTP menggunakan encoder perangkat keras Live555 & Windows media foundation di Windows10, dan berharap itu akan di-render oleh ffplay (ffmpeg 4.2). Tetapi hanya mendapatkan layar hijau seperti di bawah ini,

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Saya merujuk MFWebCamToRTP mediafoundation-sample & Encoding permukaan DirectX menggunakan perangkat keras MFT untuk mengimplementasikan framedource Live555 dan mengubah sumber input ke permukaan DirectX alih-alih webCam.

Berikut adalah kutipan implementasi saya untuk callback doGetNextFrame Live555 untuk memberi makan sampel input dari permukaan directX:

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);
}

Metode inisialisasi:

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;
    }

perintah 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

Saya tidak tahu apa yang saya lewatkan, saya telah mencoba untuk memperbaikinya selama hampir satu minggu tanpa kemajuan, dan mencoba hampir semua yang saya bisa. Juga, sumber daya online untuk penyandian permukaan DirectX sebagai video sangat terbatas.

Bantuan apa pun akan dihargai.

Ram
sumber
1
Saya pikir Anda salah berharap doGetNextFrame dipanggil lagi setelah METransformNeedInput. Mungkin Anda harus mengulang di dalamnya sampai Anda mendapatkan panggilan ProcessOutput yang valid.
VuVirt
jam = acara-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } 2 blok di atas dengan baik menangani memanggil ProcessInput sampai kita mendapatkan output dari encoder. Saya telah memverifikasi yang sama. @VuVirt
Ram
Jadi apa yang terjadi ketika frameSent benar? Apakah Anda memicu acara baru dalam kasus ini? Anda memiliki pernyataan "kembali" setelah itu.
VuVirt
@VuVirt Secara otomatis dipanggil oleh pustaka live555 yang mendasarinya dalam satu lingkaran. "ProcessInput" & "ProcessOutput" secara alternatif dipanggil berdasarkan peristiwa dalam pernyataan switch. Saya mendapatkan aliran berkelanjutan dari ProcessOut, tetapi tidak hanya bisa melihatnya. Saya yakin bahwa saya mengatur waktu dan durasi sampel dengan benar.
Ram
1
Anda mungkin perlu memeriksa apakah Anda menerima MF_E_TRANSFORM_STREAM_CHANGE dari ProcessOutput dan menangani perubahan format yang sesuai.
VuVirt

Jawaban:

6

Ini lebih sulit daripada yang terlihat.

Jika Anda ingin menggunakan encoder seperti yang Anda lakukan, dengan memanggil antarmuka IMFTransform secara langsung, Anda harus mengonversi frame RGB ke NV12. Jika Anda ingin kinerja yang baik, Anda harus melakukannya di GPU. Mungkin dilakukan dengan pixel shaders, render 2 frame, satu ukuran penuh menjadi DXGI_FORMAT_R8_UNORM render target dengan kecerahan, setengah-ukuran menjadi DXGI_FORMAT_R8G8_UNORM target dengan warna, dan menulis dua pixel shader untuk menghasilkan nilai NV12. Kedua target render dapat merender menjadi 2 pesawat dengan tekstur NV12 yang sama, tetapi hanya sejak Windows 8.

Metode lain adalah menggunakan wastafel penulis . Ini dapat meng-host beberapa MFT pada saat yang sama sehingga Anda dapat memasok tekstur RGB di VRAM, penulis wastafel pertama-tama akan mengubahnya menjadi NV12 dengan satu MFT (itu kemungkinan merupakan perangkat keras berpemilik yang diimplementasikan oleh driver GPU, seperti halnya enkoder), kemudian lolos ke encoder MFT. Ini relatif mudah untuk disandikan ke file mp4, gunakan API MFCreateSinkWriterFromURL untuk membuat penulis. Akan jauh lebih sulit untuk mendapatkan sampel mentah dari wastafel penulis, Anda harus menerapkan wastafel media kustom, wastafel aliran kustom untuk streaming video itu, dan memanggil MFCreateSinkWriterFromMediaSink untuk membuat penulis.

Masih ada lagi.

Terlepas dari metode pengkodean, Anda tidak dapat menggunakan kembali tekstur bingkai. Setiap frame yang Anda dapatkan dari DD, Anda harus membuat tekstur baru dan meneruskannya ke MF.

Pembuat enkode video mengharapkan frame rate konstan. DD tidak memberi Anda itu, itu memberi Anda bingkai setiap kali ada sesuatu yang berubah di layar. Bisa 144 FPS jika Anda memiliki monitor gaming, bisa 2 FPS jika satu-satunya perubahan adalah kursor yang berkedip. Idealnya, Anda harus mengirim bingkai ke MF dengan laju bingkai konstan, yang ditentukan dalam jenis media video Anda.

Jika Anda ingin melakukan streaming ke jaringan, lebih sering Anda harus menyediakan set parameter. Kecuali Anda menggunakan enkoder perangkat keras h265 Intel yang rusak tanpa komentar dari Intel , MF memberi Anda data dalam atribut tipe media MF_MT_MPEG_SEQUENCE_HEADER , dengan memanggil SetCurrentMediaType pada antarmuka IMFMediaTypeHandler. Anda dapat mengimplementasikan antarmuka itu untuk mendapatkan pemberitahuan. Anda hanya akan mendapatkan data itu setelah mulai encoding. Itu jika Anda menggunakan penulis wastafel, untuk IMFTransformmetode lebih mudah, Anda harus mendapatkan MF_E_TRANSFORM_STREAM_CHANGEkode dari ProcessOutputmetode, lalu menelepon GetOutputAvailableTypeuntuk mendapatkan jenis media yang diperbarui dengan gumpalan ajaib itu.

Soonts
sumber
maksud Anda DirectX (duplikasi Desktop) tidak memberikan bingkai dalam format NV12 bahkan ketika perangkat diinisialisasi dengan D3D11_CREATE_DEVICE_VIDEO_SUPPORT & deskriptor permukaan ke DXGI_FORMAT_NV12 dan mengatur MFT_MESSAGE_SET_D3D_MANAGER dalam transformasi? Saya juga berpikir bahwa kita harus secara eksplisit mengkonversi buffer RGB ke NV12 atau format input yang didukung (Sebagian besar varian YUV) atau menggunakan SinkWriter. Tapi, orang ini bisa mencapai itu entah bagaimana dengan pendekatan saya sendiri. stackoverflow.com/questions/43432670/…
Ram
1
Duplikasi @Ram Desktop selalu memberikan bingkai RGB dalam DXGI_FORMAT_B8G8R8A8_UNORMformat. MFT encoder H264 dan h265 hanya mendukung NV12 dan pasangan lainnya, yang sama-sama aneh. Seseorang harus mengonversi. Anda menggunakan duplikasi desktop; Anda sudah tidak dapat mendukung Windows 7 dengan itu. Gunakan penulis wastafel. Saya cukup yakin MFT perangkat keras nVidia / Intel ini untuk mengkonversi RGB ke NV12 lebih hemat daya daripada pixel shader ALU, mungkin diimplementasikan sepenuhnya dalam perangkat keras.
Soonts
Kamu benar. Konversi warna harus dilakukan secara eksplisit. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . Saya melanjutkan ke arah itu.
Ram
1
@ Ram Ini harusnya berhasil, saya pernah melakukannya sebelumnya. Ketika DD menolak memberi Anda bingkai baru karena tidak ada pembaruan, Anda dapat menyimpan banyak VRAM dengan mengirimkan tekstur yang sama ke pembuat enkode lagi. Hanya buat tekstur baru saat DD memiliki bingkai baru untuk Anda. Tetapi kode untuk mendeteksi kapan Anda harus mengirim bingkai dan berapa lama menunggu bukanlah hal sepele. Saya telah menggunakan QueryPerformanceCounter untuk mengukur waktu, dan beberapa jenis rata-rata bergulir selama beberapa frame terakhir untuk mengetahui apakah saya harus menangkap, atau saya harus tidur. BTW, cara yang tepat untuk tidur adalah metode IDXGIOutput :: WaitForVBlank.
Soonts
1

Karena ffplaymengeluh tentang parameter stream, saya akan menganggap itu tidak dapat mengambil SPS / PPS. Anda belum menyetelnya di SDP hardcoded Anda - lihat RFC-3984 dan cari sprop-parameter-sets. Sebuah contoh dari RFC:

m = video 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 profil-level-id = 42A01E; sprop-parameter-sets = Z0IACpZTBYmI, aMljiA ==

Saya berasumsi ffplaysangat mengharapkan ini di SDP. Saya tidak ingat dengan hati bagaimana cara mendapatkan SPS / PPS dari media foundation encoder, tetapi apakah itu ada dalam sampel payload dan Anda perlu mengekstraknya dengan mencari unit NAL yang tepat atau google cara mengekstrak data tambahan dari encoder - hit pertama saya tampak menjanjikan.

Rudolfs Bundulis
sumber
Itu poin yang valid. Saya juga punya tersangka di SPS / PPS. Saya belum memverifikasinya. Terima kasih telah mengarahkan saya ke utas MSDN yang memberi saya harapan.
Ram
@ Ram ada chanche yang bagus bahwa SPS / PPS ada di payload sampel, jadi saya akan periksa dulu.
Rudolfs Bundulis
Ya, saya mengerti itu. Saya telah mendapatkan beberapa pengetahuan tentang mengambil dan mem-parsing SPS / PPS langsung dari media encoders yayasan ketika saya mencoba menulis sampel ke file melalui Mpeg4MediaSink. Saya akan bergerak maju ke arah ini.
Ram
1

Soonts memberi Anda semua hal yang diperlukan untuk menyelesaikan masalah Anda.

Hal pertama yang perlu Anda lakukan, adalah konversi format antara DXGI_FORMAT_B8G8R8A8_UNORM dan MFVideoFormat_NV12:

Konversi format

format informasi konversi

Saya pikir lebih baik menggunakan shader untuk melakukan konversi format, karena semua tekstur akan tetap dalam GPU (lebih baik untuk kinerja).

Ini langkah pertama yang perlu Anda lakukan. Anda akan memiliki orang lain untuk meningkatkan program Anda.

mofo77
sumber
1
Gambar 2x4 mengambil 12 byte dalam NV12 bukan nilai kecerahan 24: 8 yang Anda miliki di sana, tetapi gambar berwarna dua kali lebih kecil, 1x2 piksel, jadi totalnya hanya 4 byte untuk informasi warna gambar 2x4 itu, 2 byte untuk U dan 2 byte untuk V.
Soonts
Ya Anda benar, saya menghilangkan downsampling ke 4.2.0 dari format NV12. Saya akan mencoba membuat diagram yang lebih pas.
mofo77