我試圖撰寫一些代碼,將Windows::Graphics::Capture API連接到IMFSinkWriter,以便將桌面捕獲為一個MP4檔案。我發現IMFSinkWriter的WriteSample函式總是回傳0x80070057,我正在試圖了解原因。我懷疑有一個有點明顯的錯誤,因為我對COM、WinRT、DirectX等不是非常熟悉。有什么想法嗎?
#include <iostream>
#include <Windows.h>
//XXX解決了平臺頭檔案中存在的回圈宣告的錯誤。
#include "winrt/base.h"
namespace winrt::implication
{
template <typename Async>
auto wait_for(Async const& async, Windows: :Foundation::TimeSpan const& timeout)。
}
//XXX; }
#include <dxgi.h>>
#include < inspectable.h>
#include <dxgi1_2.h>/span>
#include <d3d11.h>/span>
#include <mfapi.h>/span>
#include <mfidl.h>
#include <mfreadwrite.h>/span>
#include <codecapi.h>
#include <strmif.h>
#include <winrt/Windows.Foundation.h>/span>
#include <winrt/Windows.System.h>/span>
#include <winrt/Windows.Graphics.Capture.h>/span>
#include <windows.graphics.capture.interop.h>/span>
#include <windows.graphics.directx.direct3d11.interop.h>/span>
#pragma comment(lib, "Mfuuid.lib")
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "Mf.lib")
winrt::com_ptr<IMFSinkWriter> sinkWriter。
std::Chrono::stepid_clock::time_point firstFrameTime;
std::Chrono::stable_clock::time_point lastFrameTime。
bool recordedFirstFrame = false;
void OnFrameArrived(winrt::Windows::Graphics::Capture:: Direct3D11CaptureFramePool const& sender, winrt::Windows::Foundation::IInspectable const &) {
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame = sender.TryGetNextFrame()。
std::Chrono::stable_clock::time_point frameTime = std::Chrono::stable_clock::now()。
LONGLONG duration = 0;
LONGLONG frametime100ns;
if (! recordedFirstFrame) {
recordedFirstFrame = true;
firstFrameTime = frameTime;
frametime100ns = 0;
}
else {
frametime100ns = std::Chrono::duration_cast<std::Chrono::nanoseconds>(std::Chrono::stable_clock::now() - firstFrameTime) 。 count() / 100。
duration = std::Chrono::duration_cast<std::Chrono::milliseconds> (frameTime - lastFrameTime).count()。
}
auto surface = frame.Surface()。
auto access = surface.as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>()。
winrt::com_ptr<ID3D11Texture2D> 紋理。
winrt::check_hresult(access->GetInterface(winrt::guid_of<ID3D11Texture2D> (), texture. put_void()) )。
IMFMediaBuffer* buffer。
MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), texture.get(), 0, FALSE, & buffer);
IMFSample *sample。
winrt::check_hresult(MFCreateSample(& sample))。
HRESULT hr = sample->AddBuffer(buffer)。
printf("add buffer! %x
", hr)。)
hr = sample->SetSampleTime(frametime100ns)。
printf("set sample time (%lld) %d
", frametime100ns, hr)。)
hr = sample->SetSampleDuration(duration)。
printf("set sample duration (%lld) %d
"/span>, duration, hr)。)
hr = sinkWriter->WriteSample(0 /*視頻流索引*/, sample)。
printf("wrote sample %x
", hr)。)
lastFrameTime = frameTime。
}
int main()
{
winrt::init_apartment(winrt::apartment_type::multi_threaded)。
winrt::check_hresult(MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET)) 。
// get a list of monitor handles.
std::vector<HMONITOR> monitors。
EnumDisplayMonitors(
nullptr, nullptr,
[](HMONITOR hmon, HDC, LPRECT, LPARAM lparam) {
auto& monitors = *reinterpret_cast<std::vector<HMONITOR>*>(lparam)。
monitors.push_back(hmon)。
return TRUE。
},
reinterpret_cast<LPARAM>(& monitors)
);
//獲取第一個顯示幕的GraphicsCaptureItem。
auto interop_factory = winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem, IGraphicsCaptureItemInterop> ()。
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = { nullptr };
winrt::check_hresult(
interop_factory->CreateForMonitor(
monitors[0]。
winrt::guid_of<ABI::Windows::Graphics::IGraphicsCaptureItem>( )。
winrt::put_abi( item)
)
);
//創建直接3D設備。
winrt::com_ptr<ID3D11Device> d3dDevice;
winrt:: check_hresult(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, D3D11_SDK_VERSION, d3dDevice. put(), nullptr, nullptr))。)
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice設備。
const auto dxgiDevice = d3dDevice.as<IDXGIDevice>()。
{
winrt::com_ptr<:IInspectable> inspectable;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice. get(), inspectable.put() )。
device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>()。
}
auto idxgiDevice2 = dxgiDevice.as<IDXGIDevice2>()。
winrt::com_ptr<IDXGIAdapter> adapter。
winrt::check_hresult(idxgiDevice2->GetParent(winrt::guid_of< IDXGIAdapter> (), adapter. put_void())。
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult(adapter->GetParent(winrt::guid_of< IDXGIFactory2> (), factory. put_void()))。)
ID3D11DeviceContext* d3dContext = nullptr;
d3dDevice->GetImmediateContext(&d3dContext)。
//設定交換鏈。
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = static_cast<uint32_t>(item.Size() width);
desc.Height = static_cast<uint32_t> (item.Size().Height)。
desc.Format = static_cast<DXGI_FORMAT>(winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16G16B16A16Float) 。
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferCount = 2;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
winrt::com_ptr<IDXGISwapChain1> swapchain。
winrt::check_hresult(factory->CreateSwapChainForComposition(d3dDevice. get(), &desc, nullptr, swapchain.put()) 。)
auto framepool = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool:: CreateFreeThreaded(設備,winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16G16B16A16Float,2,專案。 Size())。
auto session = framepool.CreateCaptureSession( item )。
framepool.FrameArrived(OnFrameArrived)。
//設定MF輸出流。
winrt::com_ptr<IMFDXGIDeviceManager> devManager;
UINT resetToken。
winrt::check_hresult(MFCreateDXGIDeviceManager(& resetToken, devManager.put())。
winrt::check_hresult(devManager->ResetDevice(d3dDevice.get(), resetToken)。
winrt::com_ptr<IMFByteStream> outputStream;
winrt::check_hresult(MFCreateFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, L "C: est. mp4", outputStream.put()) )。
//配置MF輸出媒體型別。
winrt::com_ptr<IMFMediaType> videoMediaType。
//winrt::com_ptr<IMFMediaType> audioMediaType;/span>
//for video
winrt::check_hresult(MFCreateMediaType(videoMediaType.put()) 。)
winrt::check_hresult(videoMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video))。
winrt::check_hresult(videoMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264)) 。
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_AVG_BITRATE, 2000000)。
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive)) 。
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main)) 。
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_YUV_MATRIX, MFVideoTransferMatrix_BT601)) 。
winrt::check_hresult(MFSetAttributeSize(videoMediaType. get(), MF_MT_FRAME_SIZE, item.Size().Width, item.Size().Height) )。
winrt::check_hresult(MFSetAttributeRatio(videoMediaType. get(), MF_MT_FRAME_RATE, 30, 1))。)
winrt::check_hresult(MFSetAttributeRatio(videoMediaType. get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1) )。
//Creates a streaming writer[/span]。
winrt::com_ptr<IMFMediaSink> mp4StreamSink。
winrt::check_hresult(MFCreateMPEG4MediaSink(outputStream.get(), videoMediaType. get(), NULL, mp4StreamSink.put())。)
//設定MF輸入流。
winrt::com_ptr<IMFMediaType> inputVideoMediaType。
HRESULT hr = S_OK。
GUID majortype = { 0 };
MFRatio par = { 0 };
hr = videoMediaType->GetMajorType(& majortype);
if (maintype != MFMediaType_Video)
{
throw new winrt::hresult_invalid_argument()。
}
//創建一個新的媒體型別并復制到所有的專案。
//這確保了擴展的顏色資訊被保留。
winrt::check_hresult(MFCreateMediaType(inputVideoMediaType.put()) 。)
winrt::check_hresult(videoMediaType->CopyAllItems(inputVideoMediaType.get() ) 。)
//設定子型別.。
winrt::check_hresult(inputVideoMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32))。
//Uncompressed means all samples are independent.
winrt::check_hresult(inputVideoMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE))。
//修復PAR,如果在原始型別上沒有設定。
hr = MFGetAttributeRatio(
inputVideoMediaType.get()。
mf_mt_pixel_aspect_ratio,
(UINT32*)&par.Numerator,
(UINT32*)&par.Denominator
);
//default to square pixels.。
if (FAILED(hr))
{
winrt::check_hresult(MFSetAttributeRatio(
inputVideoMediaType.get()。
mf_mt_pixel_aspect_ratio,
1, 1.
));
}
winrt::check_hresult(MFSetAttributeSize(inputVideoMediaType. get(), MF_MT_FRAME_SIZE, item.Size().Width, item.Size().Height) )。
inputVideoMediaType->SetUINT32(MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0); //XXX 我們從哪里得到旋轉?
winrt::com_ptr<IMFAttributes> 屬性。
winrt::check_hresult(MFCreateAttributes(attribute.put), 6)
winrt::check_hresult(attribute->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_MPEG4))。
winrt::check_hresult(attribute->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1)。
winrt::check_hresult(attribute->SetUINT32(MF_MPEG4SINK_MOOV_BEFORE_MDAT, 1)。
winrt::check_hresult(attribute->SetUINT32(MF_LOW_LATENCY, FALSE)); ///XXX should we?
winrt::check_hresult(attribute->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, FALSE)); //XX should we?/span>
///將設備管理器添加到屬性中。這樣就可以實作硬體編碼。
winrt::check_hresult(attributions->SetUnknown(MF_SINK_WRITER_D3D_MANAGER, devManager.get()) 。)
//winrt::com_ptr<IMFSinkWriter> sinkWriter;/span>
winrt::check_hresult(MFCreateSinkWriterFromMediaSink(mp4StreamSink. get(), attributes.get(), sinkWriter.put() )。
sinkWriter->SetInputMediaType(0, inputVideoMediaType.get(), nullptr) 。
winrt::com_ptr<ICodecAPI> encoder;
sinkWriter->GetServiceForStream(0 /* video stream index */, GUID_NULL, IID_PPV_ARGS(encoder. put())。
VARIANT var;
VariantInit(&var)。
var.vt = VT_UI4;
var.ulVal = eAVEncCommonRateControlMode_Quality;
winrt::check_hresult(encoder->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var)) 。
var.ulVal = 70;
winrt::check_hresult(encoder->SetValue(&CODECAPI_AVEncCommonQuality, &var)) 。
winrt::check_hresult(sinkWriter->BeginWriting()。
session.StartCapture()。
std::cout << "Hello World!
"。
Sleep(1000)。
session.Close()。
sinkWriter->Flush(0)。
sinkWriter->Finalize()。
uj5u.com熱心網友回復:
我能夠追蹤到問題所在。上面的代碼有兩個問題:
需要在IMFMediaBuffer物件上呼叫
標籤:SetCurrentLength()/code>。這看起來很傻,因為獲取長度的方法是通過IMFMediaBuffer物件獲取IMF2DBuffer介面并呼叫GetContiguousLength(),但它是可行的。
OnFrameArrived()回呼中獲取紋理并將其傳遞到IMF匯中也是錯誤的。這將耗盡framepool(它被宣告為有2個框架)并掛起編碼器。一個可能的解決方案是在將資料傳遞給編碼器之前將其復制到一個新的紋理中。
