Obtener pantalla verde en ffplay: Streaming de escritorio (superficie DirectX) como video H264 sobre transmisión RTP usando Live555


9

Estoy tratando de transmitir el escritorio (superficie DirectX en formato NV12) como video H264 sobre transmisión RTP usando el codificador de hardware de Live555 y Windows Media Foundation en Windows10, y espero que sea renderizado por ffplay (ffmpeg 4.2). Pero solo obteniendo una pantalla verde como la siguiente,

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Me he referido mediafoundation-muestra MFWebCamToRTP y Codificación DirectX superficie usando MFT de hardware para la implementación de FramedSource live555 y cambiar la fuente de entrada a DirectX superficie en lugar de cámara web.

Aquí hay un extracto de mi implementación para la devolución de llamada doGetNextFrame de Live555 para alimentar muestras de entrada desde la superficie de 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étodo de inicialización:

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

PARTIDO SOCIALDEMÓCRATA:

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

No sé qué me estoy perdiendo, he estado tratando de arreglar esto durante casi una semana sin ningún progreso, e intenté casi todo lo que pude. Además, los recursos en línea para codificar una superficie DirectX como video son muy limitados.

Cualquier ayuda sería apreciada.


1
Creo que espera incorrectamente que se vuelva a llamar al doGetNextFrame después de METransformNeedInput. Tal vez deberías recorrerlo hasta que recibas una llamada válida de ProcessOutput.
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } Los 2 bloques anteriores se encargan de llamar a ProcessInput hasta que obtengamos una salida del codificador. He verificado lo mismo. @VuVirt
Ram

Entonces, ¿qué sucede cuando frameSent es verdadero? ¿Se desencadena un nuevo evento en este caso? Tiene una declaración de "devolución" después de eso.
VuVirt

@VuVirt Se llama automáticamente por la biblioteca live555 subyacente en un bucle. El "ProcessInput" y "ProcessOutput" se llaman alternativamente en función del evento en la instrucción switch. Recibo un flujo continuo de ProcessOut, pero no solo puedo verlo. Estoy seguro de que estoy configurando correctamente el tiempo y la duración de la muestra.
Ram

1
Es posible que deba verificar si recibe MF_E_TRANSFORM_STREAM_CHANGE de ProcessOutput y manejar el cambio de formato en consecuencia.
VuVirt

Respuestas:


6

Es más difícil de lo que parece.

Si desea utilizar el codificador como lo está haciendo, llame a IMFTransform directamente a la interfaz , debe convertir los marcos RGB a NV12. Si desea un buen rendimiento, debe hacerlo en la GPU. Posible hacer con sombreadores de píxeles, renderizar 2 cuadros, uno de tamaño completo en DXGI_FORMAT_R8_UNORM objetivo de renderizado con brillo, de tamaño medio en DXGI_FORMAT_R8G8_UNORM objetivo con color, y escribir dos sombreadores de píxeles para producir valores NV12. Ambos objetivos de renderizado pueden renderizarse en 2 planos de la misma textura NV12, pero solo desde Windows 8.

Otro método es usar escritor de sumidero . Puede alojar múltiples MFT al mismo tiempo para que pueda suministrar texturas RGB en VRAM, el escritor de sumideros primero las convertirá en NV12 con una MFT (es probable que sea un hardware patentado implementado por un controlador de GPU, al igual que el codificador), luego pasar al codificador MFT. Es relativamente fácil codificar en un archivo mp4, use la API MFCreateSinkWriterFromURL para crear el escritor. Sin embargo, es mucho más difícil obtener muestras sin procesar del escritor de sumidero, debe implementar un sumidero de medios personalizado, un sumidero de transmisión personalizado para su transmisión de video y llamar a MFCreateSinkWriterFromMediaSink para crear el escritor.

Hay más.

Independientemente de los métodos de codificación, no puede reutilizar texturas de cuadros. Cada cuadro que obtenga de DD, debe crear una nueva textura y pasarla a MF.

Los codificadores de video esperan una velocidad de cuadro constante. DD no te da eso, te da un marco cada vez que algo cambia en la pantalla. Puede tener 144 FPS si tiene un monitor de juegos, puede tener 2 FPS si el único cambio es el cursor parpadeante. Idealmente, debe enviar fotogramas a MF a una velocidad de fotogramas constante, especificada en su tipo de medio de video.

Si desea transmitir a la red, la mayoría de las veces también debe proporcionar conjuntos de parámetros. A menos que esté utilizando el codificador h265 de hardware Intel que no funciona con comentarios de Intel , MF le proporciona esos datos en el atributo MF_MT_MPEG_SEQUENCE_HEADER del tipo de medio, llamando a SetCurrentMediaType en la interfaz IMFMediaTypeHandler. Puede implementar esa interfaz para recibir notificaciones. Solo obtendrá esos datos después de comenzar a codificar. Eso si usa un escritor de sumidero, para el IMFTransformmétodo es más fácil, debe obtener el MF_E_TRANSFORM_STREAM_CHANGEcódigo del ProcessOutputmétodo y luego llamar GetOutputAvailableTypepara obtener el tipo de medio actualizado con esa burbuja mágica.


¿quiere decir que DirectX (duplicación de escritorio) no entrega marcos en formato NV12 incluso cuando el dispositivo está inicializado con D3D11_CREATE_DEVICE_VIDEO_SUPPORT y el descriptor de superficie en DXGI_FORMAT_NV12 y configurando MFT_MESSAGE_SET_D3D_MANAGER en la transformación? También pensé que teníamos que convertir explícitamente el búfer RGB a NV12 o cualquier formato de entrada compatible (principalmente variantes de YUV) o usar un SinkWriter. Pero, esta persona fue capaz de lograr eso de alguna manera con mi enfoque mismo. stackoverflow.com/questions/43432670/…
Ram


1
@Ram Desktop duplication siempre entrega cuadros RGB en DXGI_FORMAT_B8G8R8A8_UNORMformato. Las MFT de codificador H264 y h265 solo admiten NV12 y otras más, igualmente extrañas. Alguien tiene que convertir. Utiliza la duplicación de escritorio; ya no puedes soportar Windows 7 con él. Usa un escritor de fregadero. Estoy bastante seguro de que estas MFT de hardware nVidia / Intel para convertir RGB a NV12 son más eficientes que las ALU de sombreador de píxeles, probablemente se implementaron exclusivamente en hardware.
Pronto el

Tienes razón. La conversión de color debe hacerse explícitamente. github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 . Estoy avanzando en esa dirección.
Ram

1
@ Ram Debería funcionar, lo hice antes. Cuando DD se niega a darle un nuevo marco porque no hubo actualizaciones, puede guardar una gran cantidad de VRAM enviando la misma textura al codificador nuevamente. Solo crea nuevas texturas cuando DD tenga un nuevo marco para ti. Pero el código para detectar cuándo debe enviar marcos y cuánto tiempo esperar no es trivial. He usado QueryPerformanceCounter para medir el tiempo, y algún tipo de promedio móvil en los últimos cuadros para averiguar si debo capturar o dormir. Por cierto, la forma correcta de dormir es el método IDXGIOutput :: WaitForVBlank.
Pronto el

1

Como ffplayse queja de los parámetros de transmisión, supongo que no puede recoger SPS / PPS. No los ha configurado en su SDP codificado; consulte RFC-3984 y busque sprop-parameter-sets. Un ejemplo del RFC:

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

Asumo firmemente que ffplayestá esperando esto en el SDP. No recuerdo de memoria cómo obtener SPS / PPS del codificador de la base de medios, pero o están en la carga útil de muestra y necesita extraerlos buscando las unidades NAL adecuadas o google cómo extraer los datos adicionales del codificador: el primer golpe que obtuve parecía prometedor.


Es un punto valido. Yo también tengo un sospechoso en SPS / PPS. Sin embargo, todavía tengo que verificarlo. Gracias por dirigirme al hilo de MSDN que me da algo de esperanza.
Ram

@ Ram hay un buen cambio de que SPS / PPS están en la carga útil de muestra, así que lo comprobaré primero.
Rudolfs Bundulis el

Sí, entiendo eso. Tengo algunos conocimientos sobre cómo recuperar y analizar SPS / PPS directamente de los codificadores de la base de medios cuando intenté escribir muestras en un archivo a través de Mpeg4MediaSink. Avanzaré en esta dirección.
Ram

1

Pronto le daremos todas las cosas necesarias para resolver su problema.

Lo primero que debe hacer es la conversión de formato entre DXGI_FORMAT_B8G8R8A8_UNORM y MFVideoFormat_NV12:

Conversión de formato

información de conversión de formato

Creo que es mejor usar el sombreador para realizar la conversión de formato, porque todas las texturas permanecerán en la GPU (mejor para el rendimiento).

Es el primer paso que debes hacer. Tendrás otros para mejorar tu programa.


1
La imagen 2x4 toma 12 bytes en NV12, no 24: 8 valores de brillo que tiene allí, pero la imagen en color es dos veces más pequeña, 1x2 píxeles, por lo que solo 4 bytes en total para la información de color de esa imagen 2x4, 2 bytes para U y 2 bytes para V.
Pronto el

Sí, tienes razón, omití la disminución de resolución a 4.2.0 del formato NV12. Trataré de hacer un diagrama más apropiado.
mofo77
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.