IMFTransform::ProcessInput() "The buffer was too small to carry out the requested action."
IMFTransform::ProcessInput() "The buffer was too small to carry out the requested action."
我正在尝试使用 IMFTransform 将纹理编码为 H264。我可以使用 SinkWriter 将纹理毫无问题地写入和编码到文件中,并播放视频和所有内容,效果很好。但我正在尝试学习如何使用 IMFTransform 以便我可以自己访问编码的 IMFSamples。
不幸的是,我并没有走得太远,因为 ProcessInput 以 "The buffer was too small to carry out the requested action."
作为 HRESULT 失败。
我不知道它指的是哪个“缓冲区”,搜索该错误绝对没有结果。除了 ProcessInput()
之外,没有其他调用 return 错误的 HRESULT,并且 SinkWriter 工作正常。所以我对问题是什么一无所知。
#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();
}
Here’s documentation 用于您在代码中使用的基于 Microsoft 软件 CPU 的 h.264 编码器。
不支持MFVideoFormat_ARGB32
输入。它根本不支持任何 RGB 格式。该转换仅支持输入视频的 YUV 格式。
顺便说一句,如果你用硬件编码器替换 MFT,它们很可能会暴露出与 Microsoft 软件相同的一组功能,我认为它们不支持 RGB。而且,由于所有硬件转换都是异步的,您需要稍微不同的工作流程才能直接驱动它们。
sink writer 工作正常的原因,它在引擎盖下创建并托管 2 个 MFT,从 RGB 到 YUV 的格式转换器,另一个是编码器。
您有以下选项。
在将帧传递给编码器之前,使用另一个 MFT 将 RGBA 转换为 NV12。
自己与像素着色器进行对话(使用 2 种不同的像素着色器将带纹理的四边形渲染到 NV12 纹理的 2 个平面),或使用单个计算着色器(为每 2x2 块分配 1 个线程)视频,为每个块写入 6 个字节,4 个字节写入 R8_UNORM
具有亮度的输出纹理,其他 2 个字节写入 R8G8_UNORM
具有颜色数据的输出纹理)。
使用接收器编写器,但使用 MFCreateSinkWriterFromMediaSink
API 而不是 MFCreateSinkWriterFromURL
创建它。实施 IMFMediaSink
COM 接口,也 IMFStreamSink
用于它的视频流,框架将调用 IMFStreamSink.ProcessSample
为您提供系统内存中可用的编码视频样本。
我正在尝试使用 IMFTransform 将纹理编码为 H264。我可以使用 SinkWriter 将纹理毫无问题地写入和编码到文件中,并播放视频和所有内容,效果很好。但我正在尝试学习如何使用 IMFTransform 以便我可以自己访问编码的 IMFSamples。
不幸的是,我并没有走得太远,因为 ProcessInput 以 "The buffer was too small to carry out the requested action."
作为 HRESULT 失败。
我不知道它指的是哪个“缓冲区”,搜索该错误绝对没有结果。除了 ProcessInput()
之外,没有其他调用 return 错误的 HRESULT,并且 SinkWriter 工作正常。所以我对问题是什么一无所知。
#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();
}
Here’s documentation 用于您在代码中使用的基于 Microsoft 软件 CPU 的 h.264 编码器。
不支持MFVideoFormat_ARGB32
输入。它根本不支持任何 RGB 格式。该转换仅支持输入视频的 YUV 格式。
顺便说一句,如果你用硬件编码器替换 MFT,它们很可能会暴露出与 Microsoft 软件相同的一组功能,我认为它们不支持 RGB。而且,由于所有硬件转换都是异步的,您需要稍微不同的工作流程才能直接驱动它们。
sink writer 工作正常的原因,它在引擎盖下创建并托管 2 个 MFT,从 RGB 到 YUV 的格式转换器,另一个是编码器。
您有以下选项。
在将帧传递给编码器之前,使用另一个 MFT 将 RGBA 转换为 NV12。
自己与像素着色器进行对话(使用 2 种不同的像素着色器将带纹理的四边形渲染到 NV12 纹理的 2 个平面),或使用单个计算着色器(为每 2x2 块分配 1 个线程)视频,为每个块写入 6 个字节,4 个字节写入
R8_UNORM
具有亮度的输出纹理,其他 2 个字节写入R8G8_UNORM
具有颜色数据的输出纹理)。使用接收器编写器,但使用
MFCreateSinkWriterFromMediaSink
API 而不是MFCreateSinkWriterFromURL
创建它。实施IMFMediaSink
COM 接口,也IMFStreamSink
用于它的视频流,框架将调用IMFStreamSink.ProcessSample
为您提供系统内存中可用的编码视频样本。