Obtenir un écran vert dans ffplay: Streaming desktop (surface DirectX) en tant que vidéo H264 sur flux RTP en utilisant Live555


9

J'essaie de diffuser le bureau (surface DirectX au format NV12) en tant que vidéo H264 sur le flux RTP en utilisant l'encodeur matériel Live555 et Windows Media Foundation sur Windows10, et je m'attends à ce qu'il soit rendu par ffplay (ffmpeg 4.2). Mais obtenir un écran vert comme ci-dessous,

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

J'ai référé MFWebCamToRTP mediafoundation-sample & Encoding DirectX surface en utilisant le matériel MFT pour implémenter FramedSource de live555 et changer la source d'entrée en surface DirectX au lieu de webCam.

Voici un extrait de mon implémentation du rappel doGetNextFrame de Live555 pour alimenter les échantillons d'entrée à partir de la surface 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);
}

Méthode d'initialisation:

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

Commande 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

Je ne sais pas ce qui me manque, j'essaie de résoudre ce problème depuis près d'une semaine sans aucun progrès, et j'ai essayé presque tout ce que je pouvais. De plus, les ressources en ligne pour encoder une surface DirectX en vidéo sont très limitées.

Toute aide serait appréciée.


1
Je pense que vous vous attendez à tort que doGetNextFrame soit appelé à nouveau après METransformNeedInput. Vous devriez peut-être faire une boucle à l'intérieur jusqu'à ce que vous obteniez un appel ProcessOutput valide.
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } Les 2 blocs ci-dessus prennent bien soin d'appeler ProcessInput jusqu'à ce que nous obtenions une sortie de l'encodeur. J'ai vérifié la même chose. @VuVirt
Ram

Alors, que se passe-t-il lorsque frameSent est vrai? Déclenchez-vous un nouvel événement dans ce cas? Vous avez une déclaration "retour" après cela.
VuVirt

@VuVirt Il est automatiquement appelé par la bibliothèque live555 sous-jacente dans une boucle. Les "ProcessInput" et "ProcessOutput" sont également appelés en fonction de l'événement dans l'instruction switch. Je reçois un flux continu de ProcessOut, mais je ne peux pas simplement le voir. Je suis sûr que je règle correctement la durée et la durée de l'échantillon.
Ram

1
Vous devrez peut-être vérifier si vous recevez MF_E_TRANSFORM_STREAM_CHANGE de ProcessOutput et gérer le changement de format en conséquence.
VuVirt

Réponses:


6

C'est plus difficile qu'il n'y paraît.

Si vous souhaitez utiliser l'encodeur comme vous le faites, en appelant directement l'interface IMFTransform , vous devez convertir les images RVB en NV12. Si vous voulez de bonnes performances, vous devez le faire sur GPU. Il est possible de le faire avec des pixels shaders, de rendre 2 images, une taille réelle dans la cible de rendu DXGI_FORMAT_R8_UNORM avec luminosité, une demi-taille dans la cible DXGI_FORMAT_R8G8_UNORM avec couleur et écrire deux shaders de pixels pour produire des valeurs NV12. Les deux cibles de rendu peuvent effectuer le rendu sur 2 plans de la même texture NV12, mais uniquement depuis Windows 8.

Une autre méthode consiste à utiliser un rédacteur de récepteur . Il peut héberger plusieurs MFT en même temps afin que vous puissiez fournir des textures RVB en VRAM, l'écrivain récepteur les convertira d'abord en NV12 avec un MFT (il s'agit probablement d'un matériel propriétaire implémenté par un pilote GPU, tout comme l'encodeur), puis passer à l'encodeur MFT. Il est relativement facile de coder dans un fichier mp4, utilisez l' API MFCreateSinkWriterFromURL pour créer le rédacteur. Il est beaucoup plus difficile d'obtenir des échantillons bruts de l'écrivain récepteur, mais vous devez implémenter un récepteur multimédia personnalisé, un récepteur de flux personnalisé pour son flux vidéo et appeler MFCreateSinkWriterFromMediaSink pour créer le rédacteur.

Il y a plus.

Indépendamment des méthodes d'encodage, vous ne pouvez pas réutiliser les textures de cadre. Chaque image que vous obtenez de DD, vous devez créer une nouvelle texture et la transmettre à MF.

Les encodeurs vidéo attendent une fréquence d'images constante. DD ne vous donne pas cela, il vous donne un cadre à chaque fois que quelque chose change à l'écran. Peut être de 144 FPS si vous avez un moniteur de jeu, peut être de 2 FPS si le seul changement est le curseur clignotant. Idéalement, vous devez soumettre des images à MF à une fréquence d'images constante, spécifiée dans votre type de support vidéo.

Si vous souhaitez diffuser sur le réseau, le plus souvent, vous devez également fournir des ensembles de paramètres. À moins que vous n'utilisiez l'encodeur matériel h265 d'Intel qui est cassé sans commentaires d'Intel , MF vous donne ces données dans l' attribut MF_MT_MPEG_SEQUENCE_HEADER de type de support, en appelant SetCurrentMediaType sur l'interface IMFMediaTypeHandler. Vous pouvez implémenter cette interface pour être averti. Vous n'obtiendrez ces données qu'après avoir commencé l'encodage. C'est si vous utilisez un rédacteur de récepteur, pour la IMFTransformméthode c'est plus facile, vous devriez obtenir le MF_E_TRANSFORM_STREAM_CHANGEcode de la ProcessOutputméthode, puis appeler GetOutputAvailableTypepour obtenir le type de média mis à jour avec ce blob magique.


vous voulez dire que DirectX (duplication de bureau) ne fournit pas de trames au format NV12 même lorsque le périphérique est initialisé avec D3D11_CREATE_DEVICE_VIDEO_SUPPORT et le descripteur de surface sur DXGI_FORMAT_NV12 et que le paramètre MFT_MESSAGE_SET_D3D_MANAGER est transformé? Moi aussi, je pensais que nous devons convertir explicitement le tampon RVB en NV12 ou tout format d'entrée pris en charge (principalement des variantes de YUV) ou utiliser un SinkWriter. Mais, cette personne a pu y parvenir d'une manière ou d'une autre avec mon approche elle-même. stackoverflow.com/questions/43432670/...
Ram


1
La duplication @Ram Desktop fournit toujours des images RVB au DXGI_FORMAT_B8G8R8A8_UNORMformat. Les MFT des encodeurs H264 et h265 prennent uniquement en charge NV12 et en couplent d'autres, tout aussi étranges. Quelqu'un doit se convertir. Vous utilisez la duplication de bureau; vous ne pouvez déjà pas prendre en charge Windows 7 avec. Utilisez un graveur d'évier. Je suis presque sûr que les MFT matériels de ces nVidia / Intel pour convertir RVB en NV12 sont plus économes en énergie que les ALU pixel shader, ils ont probablement été implémentés uniquement en matériel.
Soonts

Tu as raison. La conversion des couleurs doit être effectuée explicitement. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . Je vais dans cette direction.
Ram

1
@Ram Cela devrait fonctionner, je l'ai déjà fait. Lorsque DD refuse de vous donner un nouveau cadre car il n'y a pas eu de mises à jour, vous pouvez enregistrer beaucoup de VRAM en soumettant à nouveau la même texture à l'encodeur. Ne créez de nouvelles textures que lorsque DD a un nouveau cadre pour vous. Mais le code pour détecter quand vous devez soumettre des trames et combien de temps attendre n'est pas anodin. J'ai utilisé QueryPerformanceCounter pour mesurer le temps et une sorte de moyenne mobile sur les dernières images pour savoir si je dois capturer ou si je dois dormir. BTW, la bonne façon de dormir est la méthode IDXGIOutput :: WaitForVBlank.
Soonts

1

Puisque ffplayse plaint des paramètres de flux, je suppose qu'il ne peut pas capter SPS / PPS. Vous ne les avez pas définis dans votre SDP codé en dur - voir RFC-3984 et recherchez sprop-parameter-sets. Un exemple du RFC:

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

Je suppose fortement que cela ffplayest prévu dans le SDP. Je ne me souviens pas par cœur comment obtenir SPS / PPS à partir de l'encodeur Media Foundation, mais soit ils sont dans l'exemple de charge utile et vous devez les extraire en recherchant les unités NAL appropriées ou Google comment extraire les données supplémentaires du encodeur - le premier hit que j'ai eu semblait prometteur.


C'est un point valable. J'ai moi aussi un suspect sur SPS / PPS. Je ne l'ai pas encore vérifié. Merci de m'avoir dirigé vers le fil MSDN qui me donne un peu d'espoir.
Ram

@Ram, il y a de bonnes raisons que SPS / PPS soient dans l'échantillon de charge utile, donc je vérifierais d'abord cela.
Rudolfs Bundulis

Ouais, je comprends ça. J'ai acquis une certaine connaissance de la récupération et de l'analyse de SPS / PPS directement à partir des encodeurs de la fondation multimédia lorsque j'ai essayé d'écrire des échantillons dans un fichier via Mpeg4MediaSink. Je vais avancer dans cette direction.
Ram

1

Soonts vous donne toutes les choses nécessaires pour résoudre votre problème.

La première chose que vous devez faire est la conversion de format entre DXGI_FORMAT_B8G8R8A8_UNORM et MFVideoFormat_NV12:

Conversion de format

informations de conversion de format

Je pense qu'il est préférable d'utiliser un shader pour faire la conversion de format, car toutes les textures resteront en GPU (mieux pour les performances).

C'est la première étape que vous devez faire. Vous en aurez d'autres pour améliorer votre programme.


1
L'image 2x4 prend 12 octets en NV12 et non les valeurs de luminosité 24: 8 que vous avez là, mais l'image couleur est deux fois plus petite, 1x2 pixels, donc seulement 4 octets au total pour les informations de couleur de cette image 2x4, 2 octets pour U et 2 octets pour V.
Soonts

Oui, vous avez raison, j'ai omis le sous-échantillonnage à 4.2.0 du format NV12. Je vais essayer de faire un diagramme plus approprié.
mofo77
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.