Ottenere lo schermo verde in ffplay: Streaming desktop (superficie DirectX) come video H264 su stream RTP utilizzando Live555


9

Sto provando a trasmettere in streaming il desktop (superficie DirectX in formato NV12) come video H264 su stream RTP utilizzando il codificatore hardware Live555 e Windows Media Foundation su Windows10 e mi aspetto che venga riprodotto da ffplay (ffmpeg 4.2). Ma ottenendo solo uno schermo verde come di seguito,

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Ho fatto riferimento a MFWebCamToRTP mediafoundation-sample e codifica della superficie DirectX usando MFT hardware per implementare FramedSource di live555 e cambiando la sorgente di input in superficie DirectX anziché webCam.

Ecco un estratto della mia implementazione per il callback doGetNextFrame di Live555 per alimentare campioni di input dalla superficie 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);
}

Metodo di inizializzazione:

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

comando 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

Non so cosa mi sto perdendo, ho cercato di risolvere questo problema per quasi una settimana senza alcun progresso e ho provato quasi tutto quello che potevo. Inoltre, le risorse online per la codifica di una superficie DirectX come video sono molto limitate.

Qualsiasi aiuto sarebbe apprezzato.


1
Penso che ti aspetti erroneamente che doGetNextFrame venga chiamato di nuovo dopo METransformNeedInput. Forse dovresti eseguire il ciclo al suo interno fino a quando non ricevi una chiamata ProcessOutput valida.
VuVirt,

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } I 2 blocchi sopra si occupano bene di chiamare ProcessInput fino a quando non otteniamo un output dall'encoder. Ho verificato lo stesso. @VuVirt
Ram

Quindi cosa succede quando frameSent è vero? Inneschi un nuovo evento in questo caso? Dopo hai una dichiarazione "return".
VuVirt,

@VuVirt Viene chiamato automaticamente dalla libreria live555 sottostante in un ciclo. "ProcessInput" e "ProcessOutput" vengono in alternativa chiamati in base all'evento nell'istruzione switch. Ricevo un flusso continuo da ProcessOut, ma non riesco solo a visualizzarlo. Sono sicuro di impostare correttamente il tempo e la durata del campione.
Ram

1
Potrebbe essere necessario verificare se si riceve MF_E_TRANSFORM_STREAM_CHANGE da ProcessOutput e gestire il cambio formato di conseguenza.
VuVirt,

Risposte:


6

È più difficile di quanto sembri.

Se si desidera utilizzare l'encoder come si sta facendo, chiamando direttamente l'interfaccia IMFTransform , è necessario convertire i frame RGB in NV12. Se vuoi buone prestazioni, dovresti farlo su GPU. Possibile fare con pixel shader, renderizzare 2 fotogrammi, uno a dimensione intera in DXGI_FORMAT_R8_UNORM render target con luminosità, mezzo formato in DXGI_FORMAT_R8G8_UNORM target con colore, e scrivere due pixel shader per produrre valori NV12. Entrambe le destinazioni di rendering possono essere renderizzate su 2 piani con la stessa trama NV12, ma solo da Windows 8.

Un altro metodo è utilizzare il writer sink . Può ospitare più MFT contemporaneamente in modo da poter fornire trame RGB in VRAM, il writer sink li convertirà prima in NV12 con un MFT (che è probabilmente un hardware proprietario implementato dal driver GPU, proprio come l'encoder), quindi passa all'encoder MFT. È relativamente facile da codificare in un file mp4, utilizzare l' API MFCreateSinkWriterFromURL per creare il writer. È molto più difficile estrarre campioni non elaborati dal writer del sink, tuttavia è necessario implementare un sink multimediale personalizzato, il sink di flusso personalizzato per il suo flusso video e chiamare MFCreateSinkWriterFromMediaSink per creare il writer.

C'è più.

Indipendentemente dai metodi di codifica, non è possibile riutilizzare le trame dei fotogrammi. Ogni fotogramma ottenuto da DD, è necessario creare una nuova trama e passarla a MF.

Gli encoder video prevedono una frequenza di fotogrammi costante. DD non ti dà questo, ti dà una cornice ogni volta che qualcosa cambia sullo schermo. Può essere 144 FPS se si dispone di un monitor da gioco, può essere 2 FPS se l'unica modifica è il cursore lampeggiante. Idealmente, è necessario inviare i frame a MF a frame rate costante, specificato nel tipo di supporto video.

Se si desidera eseguire lo streaming in rete, il più delle volte è necessario fornire anche set di parametri. A meno che non si stia utilizzando l'encoder hardware Intel h265 che non viene interrotto senza commenti da parte di Intel , MF fornisce tali dati nell'attributo MF_MT_MPEG_SEQUENCE_HEADER del tipo di supporto, chiamando SetCurrentMediaType sull'interfaccia IMFMediaTypeHandler. È possibile implementare tale interfaccia per ricevere una notifica. Riceverai quei dati solo dopo aver iniziato la codifica. Questo se usi un writer sink, per IMFTransformmetodo è più semplice, dovresti ottenere il MF_E_TRANSFORM_STREAM_CHANGEcodice dal ProcessOutputmetodo, quindi chiamare GetOutputAvailableTypeper ottenere il tipo di supporto aggiornato con quel BLOB magico.


vuoi dire che DirectX (duplicazione desktop) non consegna frame in formato NV12 anche quando il dispositivo è inizializzato con D3D11_CREATE_DEVICE_VIDEO_SUPPORT e descrittore di superficie su DXGI_FORMAT_NV12 e impostando MFT_MESSAGE_SET_D3D_MANAGER in trasformazione? Anch'io ho pensato che dobbiamo convertire esplicitamente il buffer RGB in NV12 o in qualsiasi formato di input supportato (principalmente varianti di YUV) o utilizzare un SinkWriter. Ma questa persona è stata in grado di raggiungerlo in qualche modo con il mio stesso approccio. stackoverflow.com/questions/43432670/…
Ram


1
La duplicazione @Ram Desktop offre sempre cornici RGB in DXGI_FORMAT_B8G8R8A8_UNORMformato. Gli MFT degli encoder H264 e h265 supportano solo NV12 e ne accoppiano altri, ugualmente strani. Qualcuno deve convertirsi. Si utilizza la duplicazione desktop; non puoi già supportare Windows 7 con esso. Usa uno scrittore di lavelli. Sono abbastanza sicuro che questi MFT hardware di nVidia / Intel per convertire RGB in NV12 siano più efficienti dal punto di vista energetico rispetto agli ALU pixel shader, probabilmente implementati esclusivamente nell'hardware.
Soonts,

Hai ragione. La conversione del colore deve essere effettuata in modo esplicito. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . Sto procedendo in quella direzione.
Ram

1
@Ram Dovrebbe funzionare, l'ho fatto prima. Quando DD rifiuta di darti un nuovo frame perché non ci sono aggiornamenti, puoi salvare molta VRAM inviando di nuovo la stessa trama all'encoder. Crea nuove trame solo quando DD ha un nuovo fotogramma per te. Ma il codice per rilevare quando è necessario inviare frame e per quanto tempo attendere non è banale. Ho usato QueryPerformanceCounter per misurare il tempo e una sorta di media mobile negli ultimi frame per scoprire se devo catturare o dormire. A proposito, il modo giusto di dormire è il metodo IDXGIOutput :: WaitForVBlank.
Soonts,

1

Dato che ffplaysi lamenta dei parametri del flusso, suppongo che non riesca a raccogliere SPS / PPS. Non li hai impostati nel tuo SDP hardcoded - vedi RFC-3984 e cerca sprop-parameter-sets. Un esempio dall'RFC:

m = video 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 id-livello-profilo = 42A01E; set di parametri sprop = Z0IACpZTBYmI, aMljiA ==

Suppongo fortemente che li ffplaystia aspettando nell'SDP. Non ricordo a memoria come ottenere SPS / PPS dal codificatore della base multimediale, ma o sono nel payload di esempio e devi estrarli cercando le unità NAL appropriate o google come estrarre i dati extra dal encoder - il primo colpo che ho avuto sembrava promettente.


È un punto valido. Anch'io ho un sospetto su SPS / PPS. Devo ancora verificarlo però. Grazie per avermi indirizzato al thread MSDN che mi dà qualche speranza.
Ram

@Ram c'è una buona ipotesi che SPS / PPS siano nel payload di esempio, quindi lo controllerei prima.
Rudolfs Bundulis,

Sì, lo capisco. Ho una certa conoscenza del recupero e dell'analisi di SPS / PPS direttamente dai codificatori di basi multimediali quando ho provato a scrivere campioni in un file tramite Mpeg4MediaSink. Mi sposterò in questa direzione.
Ram

1

Soonts ti fornisce tutte le cose necessarie per risolvere il tuo problema.

La prima cosa che devi fare è la conversione del formato tra DXGI_FORMAT_B8G8R8A8_UNORM e MFVideoFormat_NV12:

Conversione del formato

informazioni sulla conversione del formato

Penso che sia meglio usare lo shader per fare la conversione del formato, perché tutte le trame rimarranno nella GPU (migliore per le prestazioni).

È il primo passo che devi fare. Ne avrai altri per migliorare il tuo programma.


1
L'immagine 2x4 occupa 12 byte in NV12 non valori di luminosità 24: 8 che hai lì, ma l'immagine a colori è due volte più piccola, pixel 1x2, quindi solo 4 byte in totale per le informazioni sul colore di quell'immagine 2x4, 2 byte per U e 2 byte per V.
Soonts

Sì, hai ragione, ho omesso il downsampling alla 4.2.0 del formato NV12. Proverò a fare un diagramma più adatto.
mofo77,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.