Grüner Bildschirm in ffplay: Streaming-Desktop (DirectX-Oberfläche) als H264-Video über RTP-Stream mit Live555


9

Ich versuche, den Desktop (DirectX-Oberfläche im NV12-Format) als H264-Video über den RTP-Stream mit dem Hardware-Encoder von Live555 & Windows Media Foundation unter Windows 10 zu streamen, und erwarte, dass er von ffplay (ffmpeg 4.2) gerendert wird. Aber nur einen grünen Bildschirm wie unten bekommen,

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Ich verwies auf MFWebCamToRTP MediaFoundation-Sample & Encoding DirectX-Oberfläche unter Verwendung von Hardware-MFT, um die FramedSource von live555 zu implementieren und die Eingabequelle auf DirectX-Oberfläche anstelle von webCam zu ändern.

Hier ist ein Auszug meiner Implementierung für den doGetNextFrame-Rückruf von Live555, um Eingabebeispiele von der directX-Oberfläche zu speisen:

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

Initialisierungsmethode:

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 Befehl:

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

Ich weiß nicht, was ich vermisse, ich habe fast eine Woche lang versucht, dies ohne Fortschritte zu beheben, und fast alles versucht, was ich konnte. Außerdem sind die Online-Ressourcen zum Codieren einer DirectX-Oberfläche als Video sehr begrenzt.

Jede Hilfe wäre dankbar.


1
Ich denke, dass Sie fälschlicherweise erwarten, dass der doGetNextFrame nach METransformNeedInput erneut aufgerufen wird. Möglicherweise sollten Sie eine Schleife ausführen, bis Sie einen gültigen ProcessOutput-Aufruf erhalten.
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } Die obigen 2 Blöcke kümmern sich gut darum, ProcessInput aufzurufen, bis wir eine Ausgabe vom Encoder erhalten. Ich habe das gleiche überprüft. @VuVirt
Ram

Was passiert also, wenn frameSent wahr ist? Lösen Sie in diesem Fall ein neues Ereignis aus? Danach haben Sie eine "return" -Anweisung.
VuVirt

@VuVirt Es wird automatisch von der zugrunde liegenden live555-Bibliothek in einer Schleife aufgerufen. Die "ProcessInput" und "ProcessOutput" werden alternativ basierend auf dem Ereignis in der switch-Anweisung aufgerufen. Ich erhalte einen kontinuierlichen Stream von ProcessOut, kann ihn aber nicht nur anzeigen. Ich bin sicher, dass ich die Abtastzeit und -dauer richtig einstelle.
Ram

1
Möglicherweise müssen Sie überprüfen, ob Sie MF_E_TRANSFORM_STREAM_CHANGE von ProcessOutput erhalten, und die Formatänderung entsprechend behandeln.
VuVirt

Antworten:


6

Es ist schwieriger als es scheint.

Wenn Sie den Encoder verwenden möchten, indem Sie die IMFTransform- Schnittstelle direkt aufrufen , müssen Sie RGB-Frames in NV12 konvertieren. Wenn Sie eine gute Leistung wünschen, sollten Sie dies auf der GPU tun. Mit Pixel-Shadern möglich, 2 Frames rendern, eins in voller Größe in DXGI_FORMAT_R8_UNORM Renderziel mit Helligkeit, halbe Größe in DXGI_FORMAT_R8G8_UNORM Ziel mit Farbe rendern und zwei Pixel-Shader schreiben, um NV12-Werte zu erzeugen. Beide Renderziele können in zwei Ebenen derselben NV12-Textur gerendert werden, jedoch erst seit Windows 8.

Eine andere Methode ist die Verwendung von Sink Writer . Es kann mehrere MFTs gleichzeitig hosten, sodass Sie RGB-Texturen im VRAM bereitstellen können. Der Sink Writer konvertiert sie dann zuerst mit einer MFT in NV12 (dies ist wahrscheinlich proprietäre Hardware, die vom GPU-Treiber implementiert wird, genau wie der Encoder) Übergabe an Encoder MFT. Es ist relativ einfach, in eine mp4-Datei zu codieren. Verwenden Sie die MFCreateSinkWriterFromURL- API, um den Writer zu erstellen. Es ist viel schwieriger, Rohproben aus dem Sink-Writer zu holen. Sie müssen jedoch eine benutzerdefinierte Mediensenke und eine benutzerdefinierte Stream-Senke für den Videostream implementieren und MFCreateSinkWriterFromMediaSink aufrufen , um den Writer zu erstellen.

Es gibt mehr.

Unabhängig von den Codierungsmethoden können Sie Frame-Texturen nicht wiederverwenden. Für jeden Frame, den Sie von DD erhalten, sollten Sie eine neue Textur erstellen und an MF übergeben.

Video-Encoder erwarten eine konstante Bildrate. DD gibt Ihnen das nicht, es gibt Ihnen jedes Mal einen Rahmen, wenn sich etwas auf dem Bildschirm ändert. Kann 144 FPS betragen, wenn Sie einen Gaming-Monitor haben, kann 2 FPS sein, wenn die einzige Änderung das Blinken des Cursors ist. Idealerweise sollten Sie Frames mit einer konstanten Framerate an MF senden, die in Ihrem Videomedientyp angegeben ist.

Wenn Sie in ein Netzwerk streamen möchten, müssen Sie häufig auch Parametersätze angeben. Sofern Sie keinen Intel Hardware h265-Encoder verwenden, der ohne Kommentare von Intel defekt ist , gibt MF Ihnen diese Daten im Attribut MF_MT_MPEG_SEQUENCE_HEADER des Medientyps, indem Sie SetCurrentMediaType auf der IMFMediaTypeHandler-Schnittstelle aufrufen. Sie können diese Schnittstelle implementieren, um benachrichtigt zu werden. Sie erhalten diese Daten erst, nachdem Sie mit der Codierung begonnen haben. Wenn Sie einen Sink-Writer verwenden, ist es für die IMFTransformMethode einfacher. Sie sollten MF_E_TRANSFORM_STREAM_CHANGECode von der ProcessOutputMethode abrufen und dann aufrufen GetOutputAvailableType, um den aktualisierten Medientyp mit diesem magischen Blob abzurufen.


Sie meinen, DirectX (Desktop-Duplizierung) liefert keine Frames im NV12-Format, selbst wenn das Gerät mit D3D11_CREATE_DEVICE_VIDEO_SUPPORT & Oberflächendeskriptor für DXGI_FORMAT_NV12 initialisiert ist und MFT_MESSAGE_SET_D3D_MANAGER in transform gesetzt ist? Ich dachte auch, dass wir den RGB-Puffer explizit in NV12 oder ein unterstütztes Eingabeformat (meistens Varianten von YUV) konvertieren oder einen SinkWriter verwenden müssen. Aber diese Person konnte das irgendwie mit meinem Ansatz selbst erreichen. stackoverflow.com/questions/43432670/…
Ram


1
Die @ Ram Desktop-Duplizierung liefert immer RGB-Frames im DXGI_FORMAT_B8G8R8A8_UNORMFormat. H264- und h265-Encoder-MFTs unterstützen nur NV12 und koppeln andere, ebenso seltsame. Jemand muss konvertieren. Sie verwenden Desktop-Duplizierung. Sie können Windows 7 damit bereits nicht unterstützen. Verwenden Sie einen Spülenschreiber. Ich bin mir ziemlich sicher, dass diese Hardware-MFTs von nVidia / Intel zur Konvertierung von RGB in NV12 energieeffizienter sind als Pixel-Shader-ALUs. Sie sind wahrscheinlich nur in Hardware implementiert.
Bald

Du hast recht. Die Farbkonvertierung muss explizit erfolgen. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . Ich gehe in diese Richtung.
Ram

1
@ Ram Es sollte funktionieren, ich habe es schon mal gemacht. Wenn DD sich weigert, Ihnen einen neuen Frame zu geben, weil keine Aktualisierungen vorgenommen wurden, können Sie viel VRAM speichern, indem Sie dieselbe Textur erneut an den Encoder senden. Erstelle neue Texturen nur, wenn DD einen neuen Rahmen für dich hat. Der Code, der erkennt, wann Sie Frames senden sollten und wie lange Sie warten müssen, ist jedoch nicht trivial. Ich habe QueryPerformanceCounter verwendet, um die Zeit und eine Art gleitenden Durchschnitt in den letzten Frames zu messen, um herauszufinden, ob ich erfassen oder schlafen sollte. Übrigens ist die IDXGIOutput :: WaitForVBlank-Methode der richtige Weg, um zu schlafen.
Bald

1

Da ffplayich mich über die Stream-Parameter beschwere, würde ich davon ausgehen, dass es SPS / PPS nicht aufnehmen kann. Sie haben sie nicht in Ihrem fest codierten SDP festgelegt - siehe RFC-3984 und suchen Sie nach sprop-parameter-sets. Ein Beispiel aus dem RFC:

m = Video 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 Profil-Level-ID = 42A01E; Sprop-Parametersätze = Z0IACpZTBYmI, aMljiA ==

Ich gehe davon aus, ffplaydass dies im SDP erwartet wird. Ich erinnere mich nicht auswendig, wie man SPS / PPS vom Media Foundation-Encoder erhält, aber entweder sind sie in der Beispielnutzlast enthalten, und Sie müssen sie extrahieren, indem Sie die richtigen NAL-Einheiten nachschlagen, oder Google, wie Sie die zusätzlichen Daten aus dem extrahieren Encoder - der erste Treffer, den ich bekam, sah vielversprechend aus.


Es ist ein gültiger Punkt. Ich habe auch einen Verdächtigen auf SPS / PPS. Ich muss es jedoch noch überprüfen. Vielen Dank, dass Sie mich zum MSDN-Thread geleitet haben, der mir Hoffnung gibt.
Ram

@Ram gibt es eine gute Chance, dass SPS / PPS in der Beispielnutzlast sind, also würde ich das zuerst überprüfen.
Rudolfs Bundulis

Ja, das verstehe ich. Ich habe einige Kenntnisse über das Abrufen und Parsen von SPS / PPS direkt aus Media Foundation-Encodern, als ich versuchte, über Mpeg4MediaSink Beispiele in eine Datei zu schreiben. Ich werde in diese Richtung vorwärts gehen.
Ram

1

Bald gibt Ihnen alle notwendigen Dinge, um Ihr Problem zu lösen.

Als erstes müssen Sie die Formatkonvertierung zwischen DXGI_FORMAT_B8G8R8A8_UNORM und MFVideoFormat_NV12 durchführen:

Formatkonvertierung

Formatkonvertierungsinformationen

Ich denke, es ist besser, Shader für die Formatkonvertierung zu verwenden, da alle Texturen in der GPU bleiben (besser für die Leistung).

Es ist der erste Schritt, den Sie tun müssen. Sie werden andere haben, um Ihr Programm zu verbessern.


1
2x4-Bilder benötigen in NV12 12 Bytes, nicht 24: 8 Helligkeitswerte, die Sie dort haben, aber das Farbbild ist doppelt so klein, 1x2 Pixel, also insgesamt nur 4 Bytes für die Farbinformationen dieses 2x4-Bildes, 2 Bytes für U und 2 Bytes für V.
Soonts

Ja, Sie haben Recht, ich habe das Downsampling auf 4.2.0 des NV12-Formats weggelassen. Ich werde versuchen, ein passenderes Diagramm zu erstellen.
mofo77
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.