You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Media Foundation MFT处理视频时输出画面损坏问题

视频画面损坏问题排查(基于Media Foundation MFT)

问题描述

使用Media Foundation及Media Foundation Transform(MFT)处理视频,将视频帧转换格式时,输出视频画面完全损坏,无法正常渲染,呈现扭曲且无法使用的状态。

相关代码

HRESULT capture_video_to_mp4(IMFSourceReader* pReader, int videoTimeSeconds) {
    IMFMediaType* pMediaTypeIn = NULL;
    IMFMediaType* pMediaTypeOut = NULL;
    IMFMediaType* pMediaTypeTransformIn = NULL;
    IMFMediaType* pMediaTypeTransformOut = NULL;
    IMFSinkWriter* pSinkWriter = NULL;
    IMFTransform* pTransform = NULL;
    IMFSample* pSample = NULL;
    DWORD streamIndex = MF_SOURCE_READER_FIRST_VIDEO_STREAM, flags = 0;
    LONGLONG llTimeStamp = 0;
    HRESULT hr = S_OK;

    
    hr = pReader->lpVtbl->GetCurrentMediaType(pReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pMediaTypeIn);
    CHECK_HR(hr);

    
    hr = MFCreateSinkWriterFromURL(L"output.mp4", NULL, NULL, &pSinkWriter);
    CHECK_HR(hr);

    
    hr = CoCreateInstance(&CLSID_CMSH264EncoderMFT, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void**)&pTransform);
    CHECK_HR(hr);
   
    UINT32 customBitrate = 1600000;
    hr = pMediaTypeIn->lpVtbl->SetUINT32(pMediaTypeIn, &MF_MT_AVG_BITRATE, customBitrate);
    CHECK_HR(hr);


    hr = MFCreateMediaType(&pMediaTypeTransformOut);

    pMediaTypeIn->lpVtbl->CopyAllItems(pMediaTypeIn, pMediaTypeTransformOut);

    CHECK_HR(hr);
    hr = pMediaTypeTransformOut->lpVtbl->SetGUID(pMediaTypeTransformOut, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
    CHECK_HR(hr);
    hr = pMediaTypeTransformOut->lpVtbl->SetGUID(pMediaTypeTransformOut, &MF_MT_SUBTYPE, &MFVideoFormat_H264);
    CHECK_HR(hr);

    hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_AVG_BITRATE, customBitrate);
    CHECK_HR(hr);

    hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);
    CHECK_HR(hr);

    hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    CHECK_HR(hr);


    hr = pTransform->lpVtbl->SetOutputType(pTransform, 0, pMediaTypeTransformOut, 0);
    CHECK_HR(hr);


    hr = MFCreateMediaType(&pMediaTypeTransformIn);
    pMediaTypeTransformOut->lpVtbl->CopyAllItems(pMediaTypeTransformOut,pMediaTypeTransformIn);
    CHECK_HR(hr);

    hr = pMediaTypeTransformIn->lpVtbl->SetGUID(pMediaTypeTransformIn, &MF_MT_SUBTYPE, &MFVideoFormat_YUY2);
    CHECK_HR(hr);
    
    hr = pTransform->lpVtbl->SetInputType(pTransform, 0, pMediaTypeTransformIn, 0);
    CHECK_HR(hr);



    DWORD streamIndexOut;
    hr = pSinkWriter->lpVtbl->AddStream(pSinkWriter, pMediaTypeTransformOut, &streamIndexOut);
    CHECK_HR(hr);

    
    hr = pSinkWriter->lpVtbl->SetInputMediaType(pSinkWriter, streamIndexOut, pMediaTypeIn, NULL);
    CHECK_HR(hr);
    
    IMFSample* data[240];
    memset(data, 0, 240);

    DWORD processOutputStatus = 0, m_dwOutputID = 0;
    MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
    IMFMediaBuffer* pBuffer = NULL;
    videoTimeSeconds = 10;
    int frame = 0;
    int time_ = 0;
    bool first = true;

    IMFAttributes* pAttributes = NULL;
    hr = MFCreateAttributes(&pAttributes, 1);

    hr = pTransform->lpVtbl->QueryInterface(pTransform, &IID_IMFAttributes, (void**)&pAttributes);
    if (SUCCEEDED(hr) && pAttributes != NULL) {
        hr = pAttributes->lpVtbl->SetUINT32(pAttributes, &MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
        CHECK_HR(hr);

        pAttributes->lpVtbl->Release(pAttributes);
    }
    else {
        wprintf(L"IMFTransform does not support IMFAttributes.\n");
    }



    
    const LONGLONG frameDuration = 416700;
    LONGLONG baseTime = 0; 

    while ((videoTimeSeconds * 24) > frame) {
        while (true) {
            hr = pReader->lpVtbl->ReadSample(pReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);

            if (FAILED(hr) || pSample == NULL) {
                continue;
            }

            hr = pTransform->lpVtbl->ProcessInput(pTransform, 0, pSample, 0);
            pSample->lpVtbl->Release(pSample);
            CHECK_HR(hr);
            break;

            hr = pSample->lpVtbl->ConvertToContiguousBuffer(pSample, &pBuffer);
            if (FAILED(hr)) {
                pSample->lpVtbl->Release(pSample);
                continue;
            }

            BYTE* pData = NULL;
            DWORD bufferLength = 0;
            hr = pBuffer->lpVtbl->Lock(pBuffer, &pData, NULL, &bufferLength);
            if (SUCCEEDED(hr)) {
                BYTE* pNV12Data = (BYTE*)malloc(bufferLength / 2 * 3);
                

                IMFSample* pNV12Sample = NULL;
                hr = MFCreateSample(&pNV12Sample);
                if (SUCCEEDED(hr)) {
                    IMFMediaBuffer* pNV12Buffer = NULL;
                    hr = MFCreateMemoryBuffer(bufferLength / 2 * 3, &pNV12Buffer);
                    if (SUCCEEDED(hr)) {
                        BYTE* pDest = NULL;
                        hr = pNV12Buffer->lpVtbl->Lock(pNV12Buffer, &pDest, NULL, NULL);
                        if (SUCCEEDED(hr)) {
                            memcpy(pDest, pNV12Data, bufferLength / 2 * 3);
                            pNV12Buffer->lpVtbl->Unlock(pNV12Buffer);
                            pNV12Buffer->lpVtbl->SetCurrentLength(pNV12Buffer, bufferLength / 2 * 3);
                            pNV12Sample->lpVtbl->AddBuffer(pNV12Sample, pNV12Buffer);

                            
                            if (first) {
                                baseTime = llTimeStamp; 
                                first = false;
                            }

                            LONGLONG adjustedTimeStamp = baseTime + (frame * frameDuration);
                            pNV12Sample->lpVtbl->SetSampleTime(pNV12Sample, adjustedTimeStamp); 
                            pNV12Sample->lpVtbl->SetSampleDuration(pNV12Sample, frameDuration); 

                            hr = pTransform->lpVtbl->ProcessInput(pTransform, 0, pNV12Sample, 0);
                        }
                        pNV12Buffer->lpVtbl->Release(pNV12Buffer);
                    }
                    pNV12Sample->lpVtbl->Release(pNV12Sample);
                }
                free(pNV12Data);
                pBuffer->lpVtbl->Unlock(pBuffer);
                pBuffer->lpVtbl->Release(pBuffer);
                pSample->lpVtbl->Release(pSample);
                break;
            }
            pBuffer->lpVtbl->Release(pBuffer);
            pSample->lpVtbl->Release(pSample);
        }

        while (true) {
            MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
            ZeroMemory(&outputDataBuffer, sizeof(outputDataBuffer));
            outputDataBuffer.dwStreamID = m_dwOutputID;

            hr = MFCreateSample(&outputDataBuffer.pSample);
            if (FAILED(hr)) {
                break;
            }

            MFT_OUTPUT_STREAM_INFO streamInfo;
            hr = pTransform->lpVtbl->GetOutputStreamInfo(pTransform, m_dwOutputID, &streamInfo);
            if (FAILED(hr)) {
                outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
                break;
            }

            IMFMediaBuffer* pBufferOut = NULL;
            DWORD outputBufferSize = streamInfo.cbSize > 614400 ? streamInfo.cbSize : 614400;
            hr = MFCreateMemoryBuffer(outputBufferSize, &pBufferOut);
            if (FAILED(hr)) {
                outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
                break;
            }

            hr = outputDataBuffer.pSample->lpVtbl->AddBuffer(outputDataBuffer.pSample, pBufferOut);
            if (FAILED(hr)) {
                pBufferOut->lpVtbl->Release(pBufferOut);
                outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
                break;
            }

            DWORD processOutputStatus = 0;
            hr = pTransform->lpVtbl->ProcessOutput(pTransform, 0, 1, &outputDataBuffer, &processOutputStatus);
            if (SUCCEEDED(hr)) {
                data[frame] = outputDataBuffer.pSample;
                frame++;
            }
            else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
                outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
                pBufferOut->lpVtbl->Release(pBufferOut);
                break;
            }
            else {
                outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
                pBufferOut->lpVtbl->Release(pBufferOut);
                break;
            }

            pBufferOut->lpVtbl->Release(pBufferOut);
            break;
        }
    }

    hr = pSinkWriter->lpVtbl->BeginWriting(pSinkWriter);

    for (int i = 0; i < 240; i++) {
        Sleep(10);

        
        data[i]->lpVtbl->SetSampleTime(data[i], baseTime + (i * frameDuration));
        data[i]->lpVtbl->SetSampleDuration(data[i], frameDuration);

        hr = pSinkWriter->lpVtbl->WriteSample(pSinkWriter, streamIndexOut, data[i]);
    }

    goto done;

done:
    if (pSinkWriter) {
        pSinkWriter->lpVtbl->Finalize(pSinkWriter);
        pSinkWriter->lpVtbl->Release(pSinkWriter);
    }
    if (pTransform) {
        pTransform->lpVtbl->Release(pTransform);
    }
    if (pMediaTypeIn) {
        pMediaTypeIn->lpVtbl->Release(pMediaTypeIn);
    }
    if (pMediaTypeOut) {
        pMediaTypeOut->lpVtbl->Release(pMediaTypeOut);
    }
    if (pMediaTypeTransformIn) {
        pMediaTypeTransformIn->lpVtbl->Release(pMediaTypeTransformIn);
    }
    if (pMediaTypeTransformOut) {
        pMediaTypeTransformOut->lpVtbl->Release(pMediaTypeTransformOut);
    }

    return 0;
}

运行输出

Transform needs more input.
Transform needs more input.
Transform needs more input.
Transform needs more input.
Transform needs more input.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.
Processing output sample successful.
Sample written successfully to sink writer.

finish ....

损坏画面

损坏的输出画面1
损坏的输出画面2

问题原因分析

  • SinkWriter输入媒体类型不匹配:调用SetInputMediaType时传入原始输入类型pMediaTypeIn,但SinkWriter需接收MFT编码后的H.264格式数据,类型不匹配导致解码错误,画面扭曲。
  • MFT类型配置顺序错误:先设置输出类型再设置输入类型,违反MFT协商流程,导致内部参数不一致。
  • 时间戳处理逻辑混乱:重复设置时间戳,且冗余的格式转换代码块被跳过,逻辑冲突。
  • MFT输出处理不完整:未循环调用ProcessOutput直到需要更多输入,可能丢失部分编码帧。
  • SinkWriter初始化时机错误:BeginWriting调用过晚,不符合Media Foundation的流程规范。

解决方案

1. 修正SinkWriter输入媒体类型

将SetInputMediaType的参数改为MFT输出类型,确保接收H.264数据流:

hr = pSinkWriter->lpVtbl->SetInputMediaType(pSinkWriter, streamIndexOut, pMediaTypeTransformOut, NULL);
CHECK_HR(hr);

2. 调整MFT类型配置顺序

先设置输入类型,再协商输出类型,允许MFT调整参数:

// 创建并设置输入类型
hr = MFCreateMediaType(&pMediaTypeTransformIn);
CHECK_HR(hr);
pMediaTypeIn->lpVtbl->CopyAllItems(pMediaTypeIn, pMediaTypeTransformIn);
hr = pMediaTypeTransformIn->lpVtbl->SetGUID(pMediaTypeTransformIn, &MF_MT_SUBTYPE, &MFVideoFormat_YUY2);
CHECK_HR(hr);
hr = pTransform->lpVtbl->SetInputType(pTransform, 0, pMediaTypeTransformIn, 0);
CHECK_HR(hr);

// 创建并设置输出类型,允许MFT调整参数
hr = MFCreateMediaType(&pMediaTypeTransformOut);
CHECK_HR(hr);
pMediaTypeIn->lpVtbl->CopyAllItems(pMediaTypeIn, pMediaTypeTransformOut);
hr = pMediaTypeTransformOut->lpVtbl->SetGUID(pMediaTypeTransformOut, &MF_MT_SUBTYPE, &MFVideoFormat_H264);
hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_AVG_BITRATE, customBitrate);
hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);
hr = pMediaTypeTransformOut->lpVtbl->SetUINT32(pMediaTypeTransformOut, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
CHECK_HR(hr);

// 先测试类型是否兼容,再正式设置
hr = pTransform->lpVtbl->SetOutputType(pTransform, 0, pMediaTypeTransformOut, MFT_SET_TYPE_TEST_ONLY);
if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) {
    hr = pTransform->lpVtbl->SetOutputType(pTransform, 0, pMediaTypeTransformOut, 0);
}
CHECK_HR(hr);

3. 简化时间戳处理

直接使用原始帧的时间戳,避免重复设置:

// 在ProcessInput前设置采样时间戳
hr = pSample->lpVtbl->SetSampleTime(pSample, llTimeStamp);
hr = pSample->lpVtbl->SetSampleDuration(pSample, frameDuration);
hr = pTransform->lpVtbl->ProcessInput(pTransform, 0, pSample, 0);

4. 完善MFT输出处理逻辑

循环调用ProcessOutput直到返回MF_E_TRANSFORM_NEED_MORE_INPUT,确保所有输出帧被处理:

while (true) {
    MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
    ZeroMemory(&outputDataBuffer, sizeof(outputDataBuffer));
    outputDataBuffer.dwStreamID = m_dwOutputID;

    hr = MFCreateSample(&outputDataBuffer.pSample);
    if (FAILED(hr)) break;

    MFT_OUTPUT_STREAM_INFO streamInfo;
    hr = pTransform->lpVtbl->GetOutputStreamInfo(pTransform, m_dwOutputID, &streamInfo);
    if (FAILED(hr)) {
        outputDataBuffer.pSample->lpVtbl->Release(outputDataBuffer.pSample);
        break;
    }

    IMFMediaBuffer* pBufferOut = NULL;
    DWORD outputBufferSize = streamInfo.cb
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:54:11