首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >IMFTransform::ProcessInput()“缓冲区太小,无法执行请求的操作。

IMFTransform::ProcessInput()“缓冲区太小,无法执行请求的操作。
EN

Stack Overflow用户
提问于 2021-08-01 14:24:50
回答 1查看 287关注 0票数 2

我正在尝试用IMFTransform编码一个纹理到H264。我可以写和编码纹理没有问题的一个文件与SinkWriter和播放视频和一切,工作很好。但是我正在努力学习如何使用IMFTransform,这样我就可以自己访问编码的IMFSamples。

不幸的是,我并没有走得太远,因为ProcessInput在"The buffer was too small to carry out the requested action."作为HRESULT方面失败了。

我不知道它指的是哪个“缓冲器”,对该错误进行搜索就会发现绝对没有结果。除了ProcessInput()之外,没有其他调用返回糟糕的HRESULT,而且SinkWriter工作正常。所以我完全不知道问题出在哪里。

代码语言:javascript
运行
复制
#include "main.h"
#include "WinDesktopDup.h"
#include <iostream>
#include <wmcodecdsp.h>

WinDesktopDup dup;

void SetupDpiAwareness()
{
    if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
        printf("SetProcessDpiAwarenessContext failed\n");
}

const UINT32 VIDEO_WIDTH = 3840;
const UINT32 VIDEO_HEIGHT = 2160;
const UINT32 VIDEO_FPS = 120;
const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;
const UINT32 VIDEO_BIT_RATE = 800000;
const GUID   VIDEO_ENCODING_FORMAT = MFVideoFormat_H264;
const GUID   VIDEO_INPUT_FORMAT = MFVideoFormat_ARGB32;
const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;
const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;

template <class T>
void SafeRelease(T** ppT) {
    if (*ppT) {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

bool usingEncoder;
IMFMediaType* pMediaTypeOut = NULL;
IMFMediaType* pMediaTypeIn = NULL;
HRESULT SetMediaType()
{
    // Set the output media type.
    HRESULT hr = MFCreateMediaType(&pMediaTypeOut);
    if (!SUCCEEDED(hr)) { printf("MFCreateMediaType failed\n"); }
    hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    if (!SUCCEEDED(hr)) { printf("SetGUID failed\n"); }
    hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
    if (!SUCCEEDED(hr)) { printf("SetGUID (2) failed\n"); }
    hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
    if (!SUCCEEDED(hr)) { printf("SetUINT32 (3) failed\n"); }
    hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    if (!SUCCEEDED(hr)) { printf("SetUINT32 (4) failed\n"); }
    hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeSize failed\n"); }
    hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeRatio failed\n"); }
    hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeRatio (2) failed\n"); }
    

    // Set the input media type.
    hr = MFCreateMediaType(&pMediaTypeIn);
    if (!SUCCEEDED(hr)) { printf("MFCreateMediaType failed\n"); }
    hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    if (!SUCCEEDED(hr)) { printf("SetGUID (3) failed\n"); }
    hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
    if (!SUCCEEDED(hr)) { printf("SetGUID (4) failed\n"); }
    hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    if (!SUCCEEDED(hr)) { printf("SetUINT32 (5) failed\n"); }
    hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeSize (2) failed\n"); }
    hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeRatio (3) failed\n"); }
    hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    if (!SUCCEEDED(hr)) { printf("MFSetAttributeRatio (4) failed\n"); }
    
    return hr;
}

HRESULT InitializeSinkWriter(IMFSinkWriter** ppWriter, DWORD* pStreamIndex)
{
    IMFDXGIDeviceManager* pDeviceManager = NULL;
    UINT                  resetToken;
    IMFAttributes* attributes;

    *ppWriter = NULL;
    *pStreamIndex = NULL;

    IMFSinkWriter* pSinkWriter = NULL;
    
    DWORD          streamIndex;

    HRESULT hr = MFCreateDXGIDeviceManager(&resetToken, &pDeviceManager);
    if (!SUCCEEDED(hr)) { printf("MFCreateDXGIDeviceManager failed\n"); }
    hr = pDeviceManager->ResetDevice(dup.D3DDevice, resetToken);
    if (!SUCCEEDED(hr)) { printf("ResetDevice failed\n"); }

    hr = MFCreateAttributes(&attributes, 3);
    if (!SUCCEEDED(hr)) { printf("MFCreateAttributes failed\n"); }
    hr = attributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1);
    if (!SUCCEEDED(hr)) { printf("SetUINT32 failed\n"); }
    hr = attributes->SetUINT32(MF_LOW_LATENCY, 1);
    if (!SUCCEEDED(hr)) { printf("SetUINT32 (2) failed\n"); }
    hr = attributes->SetUnknown(MF_SINK_WRITER_D3D_MANAGER, pDeviceManager);
    if (!SUCCEEDED(hr)) { printf("SetUnknown failed\n"); }
    hr = MFCreateSinkWriterFromURL(L"output.mp4", NULL, attributes, &pSinkWriter);
    if (!SUCCEEDED(hr)) { printf("MFCreateSinkWriterFromURL failed\n"); }

    hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
    if (!SUCCEEDED(hr)) { printf("AddStream failed\n"); }

    hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
    if (!SUCCEEDED(hr)) { printf("SetInputMediaType failed\n"); }

    // Tell the sink writer to start accepting data.
    hr = pSinkWriter->BeginWriting();
    if (!SUCCEEDED(hr)) { printf("BeginWriting failed\n"); }

    // Return the pointer to the caller.
    *ppWriter = pSinkWriter;
    (*ppWriter)->AddRef();
    *pStreamIndex = streamIndex;

    SafeRelease(&pSinkWriter);
    SafeRelease(&pMediaTypeOut);
    SafeRelease(&pMediaTypeIn);
    return hr;
}

IUnknown* _transformUnk;
IMFTransform* pMFTransform;
HRESULT InitializeEncoder(DWORD* pStreamIndex)
{
    HRESULT hr = CoCreateInstance(CLSID_CMSH264EncoderMFT, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&_transformUnk);
    if (!SUCCEEDED(hr)) { printf("CoCreateInstance failed\n"); }
    hr = _transformUnk->QueryInterface(IID_PPV_ARGS(&pMFTransform));
    if (!SUCCEEDED(hr)) { printf("QueryInterface failed\n"); }
    
    hr = pMFTransform->SetOutputType(0, pMediaTypeOut, 0);
    if (!SUCCEEDED(hr)) { printf("SetOutputType failed\n"); }

    hr = pMFTransform->SetInputType(0, pMediaTypeIn, 0);
    if (!SUCCEEDED(hr)) { printf("SetInputType failed\n"); }


    DWORD mftStatus = 0;
    hr = pMFTransform->GetInputStatus(0, &mftStatus);
    if (!SUCCEEDED(hr)) { printf("GetInputStatus failed\n"); }

    if (MFT_INPUT_STATUS_ACCEPT_DATA != mftStatus)
        printf("MFT_INPUT_STATUS_ACCEPT_DATA\n");

    hr = pMFTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
    if (!SUCCEEDED(hr)) { printf("MFT_MESSAGE_NOTIFY_BEGIN_STREAMING failed\n"); }
    hr = pMFTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL);
    if (!SUCCEEDED(hr)) { printf("MFT_MESSAGE_NOTIFY_START_OF_STREAM failed\n"); }

    SafeRelease(&pSinkWriter);
    SafeRelease(&pMediaTypeOut);
    SafeRelease(&pMediaTypeIn);
    return hr;
}

ID3D11Texture2D* texture;

HRESULT WriteFrame(IMFSinkWriter* pWriter, DWORD streamIndex, const LONGLONG& rtStart)
{
    IMFSample* pSample = NULL;
    IMFMediaBuffer* pBuffer = NULL;

    HRESULT hr;
    
    hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), texture, 0, false, &pBuffer);
    if (!SUCCEEDED(hr)) { printf("MFCreateDXGISurfaceBuffer failed\n"); }

    DWORD len;
    hr = ((IMF2DBuffer*)pBuffer)->GetContiguousLength(&len);
    if (!SUCCEEDED(hr)) { printf("GetContiguousLength failed\n"); }

    hr = pBuffer->SetCurrentLength(len);
    if (!SUCCEEDED(hr)) { printf("SetCurrentLength failed\n"); }

    // Create a media sample and add the buffer to the sample.
    hr = MFCreateSample(&pSample);
    if (!SUCCEEDED(hr)) { printf("MFCreateSample failed\n"); }

    hr = pSample->AddBuffer(pBuffer);
    if (!SUCCEEDED(hr)) { printf("AddBuffer failed\n"); }

    // Set the time stamp and the duration.
    hr = pSample->SetSampleTime(rtStart);
    if (!SUCCEEDED(hr)) { printf("SetSampleTime failed\n"); }

    hr = pSample->SetSampleDuration(VIDEO_FRAME_DURATION);
    if (!SUCCEEDED(hr)) { printf("SetSampleDuration failed\n"); }

    // Send the sample to the Sink Writer or Encoder.

    if (!usingEncoder)
    {
        hr = pWriter->WriteSample(streamIndex, pSample);
        if (!SUCCEEDED(hr)) { printf("WriteSample failed\n"); }
    }
    else
    {
        hr = pMFTransform->ProcessInput(0, pSample, 0);
        if (!SUCCEEDED(hr)) { printf("ProcessInput failed\n"); }
    }
    
    SafeRelease(&pSample);
    SafeRelease(&pBuffer);
    return hr;
}

int APIENTRY main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    SetupDpiAwareness();
    auto err = dup.Initialize();

    // Initialize MF
    CoInitializeEx(0, COINIT_APARTMENTTHREADED); // Need to call this once when a thread is using COM or it wont work
    MFStartup(MF_VERSION);                       // Need to call this too for Media Foundation related memes

    IMFSinkWriter* pSinkWriter = NULL;
    DWORD          stream = 0;
    LONGLONG       rtStart = 0;

    usingEncoder = true; // True if we want to encode with IMFTransform, false if we want to write with SinkWriter
    
    HRESULT        hr = SetMediaType();
    if (!SUCCEEDED(hr)) { printf("SetMediaType failed\n"); }

    if (!usingEncoder)
    {
        hr = InitializeSinkWriter(&pSinkWriter, &stream);
        if (!SUCCEEDED(hr)) { printf("InitializeSinkWriter failed\n"); }
    }
    else
    {
        hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_IYUV); // Using MFVideoFormat_ARGB32 causes SetInputType() to fail
        hr = InitializeEncoder(&stream);
        if (!SUCCEEDED(hr)) { printf("InitializeEncoder failed\n"); }
    }
    
    const int CAPTURE_LENGTH = 10;

    int total_frames = VIDEO_FPS * CAPTURE_LENGTH;

    for (int i = 0; i < 1; i++)
    {
        texture = dup.CaptureNext();
        if (texture != nullptr)
        {
            hr = WriteFrame(pSinkWriter, stream, rtStart);
            if (!SUCCEEDED(hr))
                printf("WriteFrame failed\n");
            rtStart += VIDEO_FRAME_DURATION;
            texture->Release();
        }
        else
        {
            i--;
        }
    }

    if (FAILED(hr))
    {
        std::cout << "Failure" << std::endl;
    }

    if (SUCCEEDED(hr)) {
        hr = pSinkWriter->Finalize();
    }

    SafeRelease(&pSinkWriter);
    MFShutdown();
    CoUninitialize();
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-08-01 18:05:34

这是文件的微软的软件CPU为基础的h.264编码器,您正在使用的代码。

它不支持输入的MFVideoFormat_ARGB32。它根本不支持任何RGB格式。该转换只支持输入视频的YUV格式。

顺便说一句,如果你用硬件编码器代替MFT,它们很可能暴露出与微软软件相同的特性,我认为他们不支持RGB。而且,因为所有硬件转换都是异步的,所以您需要稍微不同的工作流来直接驱动它们。

接收器写入器工作正常的原因是,它在引擎盖下创建并托管了2个MFT,从RGB到YUV的格式转换器,另一个是编码器。

你有以下选择。

  1. 在将帧传递到编码器之前,使用另一个MFT将RGBA转换为NV12。
  2. 使用像素着色器(使用两个不同的像素着色器将纹理渲染成两个NV12纹理平面),或者使用单个计算着色器(为视频的每2x2块分配一个线程,为每个块编写6个字节,将4个字节写入R8_UNORM输出的亮度纹理,将其他2个字节分配到带有颜色数据的R8G8_UNORM输出纹理中)。
  3. 使用接收器编写器,但是使用MFCreateSinkWriterFromMediaSink API而不是MFCreateSinkWriterFromURL创建它。实现IMFMediaSink COM接口,也是它的视频流的IMFStreamSink,该框架将调用IMFStreamSink.ProcessSample,在系统内存中为您提供编码的视频示例。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68611367

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档