ProcessInput 时 IMFSample 是否有最小大小?

问题描述 投票:0回答:1
  • 现在我正在使用媒体基础来处理h264数据,并且它是实时数据。
  • 通过socket接收数据
  • MFCreateMemoryBuffer 创建 IMFMediaBuffer 并将数据复制到缓冲区
  • MF创建样本
  • ProcessInput 传递创建的示例。
  • 获取输出数据。

现在我的问题是当我第一次接收数据和 ProcessInput 时一切正常。 但随后我会收到一些小数据(可能只有3kb?)并且图像不正确并且会缺少一些东西(仅整个正确图像的一部分)。

所以我认为它没有收到 IMFTransform 所需的全部数据,所以我将保留数据直到它达到 30kb(或其他更大),然后转到 MFCreateMemoryBuffer。

是的,现在图像帧直到最后一帧都是正确的。

所以我想我没有得到一帧的全部正确数据,对吧? 如何获得一个完整的 IMFSample(Frame) 的正确尺寸?

video h.264 ms-media-foundation
1个回答
0
投票

这里是使用 Media Foundation H.264 视频解码器 转换解码 H264 字节流的示例代码。

它使用文件模拟各种大小的字节块的流,但原理是相同的。

要点:

  • 您可以提供任意数量字节的块作为转换的输入
  • 变换需要固定大小的输出样本
  • 变换无法自行分配样本
  • 最初没有人知道样本所需的大小,因此我们必须向转换提供字节,直到它告诉我们样本大小必须是多少。
#include <windows.h>
#include <atlbase.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mftransform.h>
#include <tuple>
#include <cstdlib>

#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib")

#define HRCHECK(__expr) {auto __hr=(__expr);if(FAILED(__hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" _CRT_WIDE(#__expr) L"'\n",__hr,__hr,__LINE__,_CRT_WIDE(__FILE__));_CrtDbgBreak();}}
#define WIN32CHECK(__expr) {if(!(__expr)){auto __hr=HRESULT_FROM_WIN32(GetLastError());{wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" _CRT_WIDE(#__expr) L"'\n",__hr,__hr,__LINE__,_CRT_WIDE(__FILE__));_CrtDbgBreak();}}}

static HRESULT SetOutputType(IMFTransform* transform, GUID format)
{
    DWORD index = 0;
    do
    {
        CComPtr<IMFMediaType> outputType;
        auto hr = transform->GetOutputAvailableType(0, index++, &outputType);
        if (FAILED(hr))
            return hr;

        GUID guid;
        if (SUCCEEDED(outputType->GetGUID(MF_MT_SUBTYPE, &guid)) && guid == format)
        {
            HRCHECK(transform->SetOutputType(0, outputType, 0));
            return S_OK;
        }
    } while (true);
}

int main()
{
    HRCHECK(CoInitialize(nullptr));
    {
        HRCHECK(MFStartup(MF_VERSION));
        // open some raw H264 file
        auto file = CreateFile(L"h264.h264", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
        WIN32CHECK(file != INVALID_HANDLE_VALUE);

        // create H264 transform https://learn.microsoft.com/en-us/windows/win32/medfound/h-264-video-decoder
        CComPtr<IMFTransform> decoder;
        HRCHECK(decoder.CoCreateInstance(CLSID_MSH264DecoderMFT));

        // You can check in decoder attributes that MF_MT_FIXED_SIZE_SAMPLES is set to TRUE.
        // Calling GetOutputStreamInfo this will tell you the MFT cannot provide samples
        // as MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES and MFT_OUTPUT_STREAM_PROVIDES_SAMPLES are not set

        // So we don't know enough information yet, we'll feed input samples until we get MF_E_TRANSFORM_STREAM_CHANGE and then we'll provide a sample as per doc:
        //   "If the input type contains only these two attributes, the decoder will offer a default output type, which acts as a placeholder."
        //   "When the decoder receives enough input samples to produce an output frame, it signals a format change by returning MF_E_TRANSFORM_STREAM_CHANGE"
        UINT32 sampleSize = 0;

        // input type is H264
        CComPtr<IMFMediaType> inputType;
        HRCHECK(MFCreateMediaType(&inputType));
        HRCHECK(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
        HRCHECK(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
        HRCHECK(decoder->SetInputType(0, inputType, 0)); // video is id 0

        // get (and set) NV12 output type (could be I420, IYUV, YUY2, YV12)
        HRCHECK(SetOutputType(decoder, MFVideoFormat_NV12));

        do
        {
            // get a random chunk size between 500 and 1500
            DWORD chunkSize = 500 + (1000 * (RAND_MAX - std::rand())) / RAND_MAX;

            // create an MF input buffer & read into it
            CComPtr<IMFMediaBuffer> inputBuffer;
            HRCHECK(MFCreateMemoryBuffer(chunkSize, &inputBuffer));
            BYTE* chunk;
            HRCHECK(inputBuffer->Lock(&chunk, nullptr, nullptr));
            DWORD read;
            WIN32CHECK(ReadFile(file, chunk, chunkSize, &read, nullptr));
            HRCHECK(inputBuffer->SetCurrentLength(read));
            HRCHECK(inputBuffer->Unlock());
            if (!read)
                break; // end of file

            CComPtr<IMFSample> inputSample;
            HRCHECK(MFCreateSample(&inputSample));
            HRCHECK(inputSample->AddBuffer(inputBuffer));

            auto hr = decoder->ProcessInput(0, inputSample, 0);
            if (hr != MF_E_NOTACCEPTING) // just go on
            {
                HRCHECK(hr);
            }

            CComPtr<IMFSample> outputSample;
            HRCHECK(MFCreateSample(&outputSample));
            MFT_OUTPUT_DATA_BUFFER outputBuffer{};
            outputBuffer.pSample = outputSample;

            // do we know sample size yet?
            if (sampleSize)
            {
                CComPtr<IMFMediaBuffer> outputBuffer;
                HRCHECK(MFCreateMemoryBuffer(sampleSize, &outputBuffer));
                HRCHECK(outputSample->AddBuffer(outputBuffer));
            }

            DWORD status = 0;
            hr = decoder->ProcessOutput(0, 1, &outputBuffer, &status);
            if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) // just go on
                continue;

            // https://learn.microsoft.com/en-us/windows/win32/medfound/handling-stream-changes
            if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
            {
                // get (and set) NV12 output type (could be I420, IYUV, YUY2, YV12)
                HRCHECK(SetOutputType(decoder, MFVideoFormat_NV12));

                // now get sample size
                CComPtr<IMFMediaType> type;
                HRCHECK(decoder->GetOutputCurrentType(0, &type));
                HRCHECK(type->GetUINT32(MF_MT_SAMPLE_SIZE, &sampleSize));
                continue;
            }
            HRCHECK(hr);

            LONGLONG time, duration;
            HRCHECK(outputSample->GetSampleTime(&time));
            HRCHECK(outputSample->GetSampleDuration(&duration));
            wprintf(L"Sample ready! time: %I64u ms duration: %I64u ms\n", time / 10000, duration / 10000);
        } while (true);

        // close file
        CloseHandle(file);
        HRCHECK(MFShutdown());
    }
    CoUninitialize();
    return 0;
}

完整项目可在此处获取https://github.com/smourier/MFDecodeH264

© www.soinside.com 2019 - 2024. All rights reserved.