MFVideoFormat_H264可以直接支持MFVideoFormat_RGB32的输入媒体类型吗?
Can MFVideoFormat_H264 directly support the input media type of MFVideoFormat_RGB32?
我正在写一个基于D3D11 + MediaFoundation的mp4(H264 + AAC)录音程序。基本流程是线程A使用D3D11API生成各种图形,然后将swap chain的back buffer复制到共享ID3D11Texture2D中,而记录线程B作为消费者将共享纹理复制到本地_capture_texture2d 并将其推入 MediaFoundation 的视频帧。
我有一个问题:
https://docs.microsoft.com/en-us/windows/win32/medfound/h-264-video-encoder 要求
"输入媒体类型必须具有以下子类型之一:
##MFVideoFormat_I420
##MFVideoFormat_IYUV
##MFVideoFormat_NV12
##MFVideoFormat_YUY2
##MFVideoFormat_YV12"
但实际上为了和GDI/D2D互操作,我使用DXGI_FORMAT_B8G8R8A8_UNORM的格式
CreateSwapChain,对应MFVideoFormat_RGB32。在我的Win10和公司的其他几台win7/10机器上都很好用,不知道在用户真机环境下能不能正常使用?
所以我不确定是否必须将格式从MFVideoFormat_RGB32转换为MFVideoFormat_NV12?以及如何进行这种转换?用户的win7/8/10系统可以内置支持转换吗? (另外要求是尽量使用硬件加速)
HRESULT InitVideoStreamHelper(IMFSinkWriter *ppWriter, DWORD *pStreamIndex, const UINT32 uiWidth, const UINT32 uiHeight, const UINT32& fps, IMFDXGIDeviceManager* dxgi_device_manager)
{
HRESULT hr = S_OK;
// Set the output media type.
CComPtr<IMFMediaType> pMediaTypeOut = NULL;
hr = MFCreateMediaTypeWrapper(&pMediaTypeOut);
RETURN_ON_FAIL(hr);
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_FAIL(hr);
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
RETURN_ON_FAIL(hr);
hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
RETURN_ON_FAIL(hr);
hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, uiWidth, uiHeight);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, fps, 1);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
RETURN_ON_FAIL(hr);
hr = ppWriter->AddStream(pMediaTypeOut, pStreamIndex);
RETURN_ON_FAIL(hr);
// Set the input media type.
CComPtr<IMFMediaType> pMediaTypeIn = NULL;
hr = MFCreateMediaTypeWrapper(&pMediaTypeIn);
RETURN_ON_FAIL(hr);
hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_FAIL(hr);
hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
RETURN_ON_FAIL(hr);
hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, uiWidth, uiHeight);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, fps, 1);
RETURN_ON_FAIL(hr);
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
RETURN_ON_FAIL(hr);
hr = ppWriter->SetInputMediaType(*pStreamIndex, pMediaTypeIn, NULL);
RETURN_ON_FAIL(hr);
// Tell the sink _writer to start accepting data.
#if 1
hr = MFTRegisterLocalByCLSID(
__uuidof(CColorConvertDMO),
MFT_CATEGORY_VIDEO_PROCESSOR,
L"",
MFT_ENUM_FLAG_SYNCMFT,
0,
NULL,
0,
NULL
);
#endif
RETURN_ON_FAIL(hr);
return hr;
}
HRESULT CMF_MP4FileWriter::MakeVideoFrame(IMFSample** sample, ID3D11Texture2D* texture)
{
if (!_d3d_device || !_capture_texture2d)
{
return E_NOTIMPL;
}
HRESULT hr = E_NOTIMPL;
CComPtr<IMFMediaBuffer> media_buffer;
if (IsWindows8OrGreater())
{
hr = MFCreateDXGISurfaceBufferWrapper(__uuidof(ID3D11Texture2D), _capture_texture2d, 0, FALSE, &media_buffer);
CComPtr<IMF2DBuffer> twod_buffer;
hr = media_buffer->QueryInterface(&twod_buffer);
RETURN_ON_FAIL(hr);
DWORD length = 0;
hr = twod_buffer->GetContiguousLength(&length);
RETURN_ON_FAIL(hr);
hr = media_buffer->SetCurrentLength(length);
RETURN_ON_FAIL(hr);
}
else
{
CComPtr<ID3D11DeviceContext> immediate_context;
_d3d_device->GetImmediateContext(&immediate_context);
D3D11_TEXTURE2D_DESC desc;
_capture_texture2d->GetDesc(&desc);
UINT32 img_size = 0;
GUID dst_format = MFVideoFormat_RGB32;
hr = MFCalculateImageSize(dst_format, desc.Width, desc.Height, &img_size);
RETURN_ON_FAIL(hr);
LONG dst_stride = 0;
hr = MFGetStrideForBitmapInfoHeader(dst_format.Data1, desc.Width, &dst_stride);
RETURN_ON_FAIL(hr);
D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
hr = immediate_context->Map(_capture_texture2d, subresource, D3D11_MAP_READ, 0, &resource);
RETURN_ON_FAIL(hr);
hr = MFCreateMemoryBuffer(img_size, &media_buffer);
if (SUCCEEDED(hr))
{
BYTE *dst_data = NULL;
hr = media_buffer->Lock(&dst_data, NULL, NULL);
if (SUCCEEDED(hr))
{
if (dst_stride >= 0)
{
hr = MFCopyImage(dst_data, dst_stride, (BYTE*)resource.pData, resource.RowPitch, desc.Width * 4, desc.Height);
}
else
{
BYTE *src_ptr = (BYTE*)resource.pData;
BYTE *dst_ptr = dst_data - dst_stride * (desc.Height - 1);
size_t copy_bytes = min(resource.RowPitch, (UINT)-dst_stride);
for (size_t i = 0; i < desc.Height; i++)
{
memcpy(dst_ptr, src_ptr, copy_bytes);
src_ptr += resource.RowPitch;
dst_ptr += dst_stride;
}
}
media_buffer->SetCurrentLength(img_size);
media_buffer->Unlock();
}
}
immediate_context->Unmap(_capture_texture2d, subresource);
}
RETURN_ON_FAIL(hr);
hr = MFCreateSampleWrapper(sample);
RETURN_ON_FAIL(hr);
hr = (*sample)->AddBuffer(media_buffer);
RETURN_ON_FAIL(hr);
return hr;
}
HRESULT CMF_MP4FileWriter::WriteVideoFrame(IMFSample* sample)
{
if (sample && _writer)
{
_video_frame_count++;
HRESULT hr = sample->SetSampleTime(_video_frame_count * 10000000 / _video_fps);
RETURN_ON_FAIL(hr);
hr = sample->SetSampleDuration(10000000 / _video_fps);
RETURN_ON_FAIL(hr);
hr = _writer->WriteSample(_video_sink_stream, sample);
RETURN_ON_FAIL(hr);
}
return S_OK;
}
所有 H.264 编码器都能够接受 MFVideoFormat_NV12
输入。其中一些采用其他格式的帧。如果您需要转换,Video Processor MFT可以为您转换格式。
请参阅此处的使用示例:
我正在写一个基于D3D11 + MediaFoundation的mp4(H264 + AAC)录音程序。基本流程是线程A使用D3D11API生成各种图形,然后将swap chain的back buffer复制到共享ID3D11Texture2D中,而记录线程B作为消费者将共享纹理复制到本地_capture_texture2d 并将其推入 MediaFoundation 的视频帧。
我有一个问题: https://docs.microsoft.com/en-us/windows/win32/medfound/h-264-video-encoder 要求 "输入媒体类型必须具有以下子类型之一:
##MFVideoFormat_I420
##MFVideoFormat_IYUV
##MFVideoFormat_NV12
##MFVideoFormat_YUY2
##MFVideoFormat_YV12"
但实际上为了和GDI/D2D互操作,我使用DXGI_FORMAT_B8G8R8A8_UNORM的格式 CreateSwapChain,对应MFVideoFormat_RGB32。在我的Win10和公司的其他几台win7/10机器上都很好用,不知道在用户真机环境下能不能正常使用?
所以我不确定是否必须将格式从MFVideoFormat_RGB32转换为MFVideoFormat_NV12?以及如何进行这种转换?用户的win7/8/10系统可以内置支持转换吗? (另外要求是尽量使用硬件加速)
HRESULT InitVideoStreamHelper(IMFSinkWriter *ppWriter, DWORD *pStreamIndex, const UINT32 uiWidth, const UINT32 uiHeight, const UINT32& fps, IMFDXGIDeviceManager* dxgi_device_manager) { HRESULT hr = S_OK; // Set the output media type. CComPtr<IMFMediaType> pMediaTypeOut = NULL; hr = MFCreateMediaTypeWrapper(&pMediaTypeOut); RETURN_ON_FAIL(hr); hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); RETURN_ON_FAIL(hr); hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); RETURN_ON_FAIL(hr); hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, 1000000); RETURN_ON_FAIL(hr); hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); RETURN_ON_FAIL(hr); hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, uiWidth, uiHeight); RETURN_ON_FAIL(hr); hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, fps, 1); RETURN_ON_FAIL(hr); hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); RETURN_ON_FAIL(hr); hr = ppWriter->AddStream(pMediaTypeOut, pStreamIndex); RETURN_ON_FAIL(hr); // Set the input media type. CComPtr<IMFMediaType> pMediaTypeIn = NULL; hr = MFCreateMediaTypeWrapper(&pMediaTypeIn); RETURN_ON_FAIL(hr); hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); RETURN_ON_FAIL(hr); hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); RETURN_ON_FAIL(hr); hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); RETURN_ON_FAIL(hr); hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, uiWidth, uiHeight); RETURN_ON_FAIL(hr); hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, fps, 1); RETURN_ON_FAIL(hr); hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); RETURN_ON_FAIL(hr); hr = ppWriter->SetInputMediaType(*pStreamIndex, pMediaTypeIn, NULL); RETURN_ON_FAIL(hr); // Tell the sink _writer to start accepting data. #if 1 hr = MFTRegisterLocalByCLSID( __uuidof(CColorConvertDMO), MFT_CATEGORY_VIDEO_PROCESSOR, L"", MFT_ENUM_FLAG_SYNCMFT, 0, NULL, 0, NULL ); #endif RETURN_ON_FAIL(hr); return hr; } HRESULT CMF_MP4FileWriter::MakeVideoFrame(IMFSample** sample, ID3D11Texture2D* texture) { if (!_d3d_device || !_capture_texture2d) { return E_NOTIMPL; } HRESULT hr = E_NOTIMPL; CComPtr<IMFMediaBuffer> media_buffer; if (IsWindows8OrGreater()) { hr = MFCreateDXGISurfaceBufferWrapper(__uuidof(ID3D11Texture2D), _capture_texture2d, 0, FALSE, &media_buffer); CComPtr<IMF2DBuffer> twod_buffer; hr = media_buffer->QueryInterface(&twod_buffer); RETURN_ON_FAIL(hr); DWORD length = 0; hr = twod_buffer->GetContiguousLength(&length); RETURN_ON_FAIL(hr); hr = media_buffer->SetCurrentLength(length); RETURN_ON_FAIL(hr); } else { CComPtr<ID3D11DeviceContext> immediate_context; _d3d_device->GetImmediateContext(&immediate_context); D3D11_TEXTURE2D_DESC desc; _capture_texture2d->GetDesc(&desc); UINT32 img_size = 0; GUID dst_format = MFVideoFormat_RGB32; hr = MFCalculateImageSize(dst_format, desc.Width, desc.Height, &img_size); RETURN_ON_FAIL(hr); LONG dst_stride = 0; hr = MFGetStrideForBitmapInfoHeader(dst_format.Data1, desc.Width, &dst_stride); RETURN_ON_FAIL(hr); D3D11_MAPPED_SUBRESOURCE resource; UINT subresource = D3D11CalcSubresource(0, 0, 0); hr = immediate_context->Map(_capture_texture2d, subresource, D3D11_MAP_READ, 0, &resource); RETURN_ON_FAIL(hr); hr = MFCreateMemoryBuffer(img_size, &media_buffer); if (SUCCEEDED(hr)) { BYTE *dst_data = NULL; hr = media_buffer->Lock(&dst_data, NULL, NULL); if (SUCCEEDED(hr)) { if (dst_stride >= 0) { hr = MFCopyImage(dst_data, dst_stride, (BYTE*)resource.pData, resource.RowPitch, desc.Width * 4, desc.Height); } else { BYTE *src_ptr = (BYTE*)resource.pData; BYTE *dst_ptr = dst_data - dst_stride * (desc.Height - 1); size_t copy_bytes = min(resource.RowPitch, (UINT)-dst_stride); for (size_t i = 0; i < desc.Height; i++) { memcpy(dst_ptr, src_ptr, copy_bytes); src_ptr += resource.RowPitch; dst_ptr += dst_stride; } } media_buffer->SetCurrentLength(img_size); media_buffer->Unlock(); } } immediate_context->Unmap(_capture_texture2d, subresource); } RETURN_ON_FAIL(hr); hr = MFCreateSampleWrapper(sample); RETURN_ON_FAIL(hr); hr = (*sample)->AddBuffer(media_buffer); RETURN_ON_FAIL(hr); return hr; } HRESULT CMF_MP4FileWriter::WriteVideoFrame(IMFSample* sample) { if (sample && _writer) { _video_frame_count++; HRESULT hr = sample->SetSampleTime(_video_frame_count * 10000000 / _video_fps); RETURN_ON_FAIL(hr); hr = sample->SetSampleDuration(10000000 / _video_fps); RETURN_ON_FAIL(hr); hr = _writer->WriteSample(_video_sink_stream, sample); RETURN_ON_FAIL(hr); } return S_OK; }
所有 H.264 编码器都能够接受 MFVideoFormat_NV12
输入。其中一些采用其他格式的帧。如果您需要转换,Video Processor MFT可以为您转换格式。
请参阅此处的使用示例: