24#include <d3dcompiler.h>
25#include <dispatcherqueue.h>
26#include <windows.foundation.h>
27#include <windows.graphics.capture.h>
28#include <windows.graphics.capture.interop.h>
29#include <windows.graphics.directx.direct3d11.h>
30#if HAVE_IDIRECT3DDXGIINTERFACEACCESS
31#include <windows.graphics.directx.direct3d11.interop.h>
51#include <condition_variable>
57#include <system_error>
61using namespace ABI::Windows::System;
62using namespace ABI::Windows::Foundation;
63using namespace ABI::Windows::Graphics::Capture;
64using namespace ABI::Windows::Graphics::DirectX::Direct3D11;
66using Microsoft::WRL::ComPtr;
67using ABI::Windows::Graphics::SizeInt32;
68using ABI::Windows::Foundation::TimeSpan;
69using ABI::Windows::Graphics::DirectX::DirectXPixelFormat;
71#define TIMESPAN_RES 10000000
72#define TIMESPAN_RES64 INT64_C(10000000)
74#define CAPTURE_POOL_SIZE 2
80#define CCTX(ctx) static_cast<GfxCaptureContext*>(ctx)
86 HRESULT (WINAPI *RoInitialize)(RO_INIT_TYPE initType);
88 HRESULT (WINAPI *RoGetActivationFactory)(HSTRING activatableClassId, REFIID
iid,
void **
factory);
143 std::unique_ptr<GfxCaptureContextWgc>
wgc;
144 std::unique_ptr<GfxCaptureContextD3D>
d3d;
175 HSTRING_HEADER hsheader = { 0 };
178 HRESULT hr =
ctx->fn.WindowsCreateStringReference(clsid, (UINT32)wcslen(clsid), &hsheader, &hs);
182 return ctx->fn.RoGetActivationFactory(hs, IID_PPV_ARGS(factory));
185#define CHECK_HR(fcall, action) \
187 HRESULT fhr = fcall; \
189 av_log(avctx, AV_LOG_ERROR, #fcall " failed: 0x%08lX\n", fhr); \
193#define CHECK_HR_RET(...) CHECK_HR((__VA_ARGS__), return AVERROR_EXTERNAL)
194#define CHECK_HR_FAIL(...) CHECK_HR((__VA_ARGS__), ret = AVERROR_EXTERNAL; goto fail)
195#define CHECK_HR_LOG(...) CHECK_HR((__VA_ARGS__), (void)0)
204 std::lock_guard
lock(wgctx->frame_arrived_mutex);
205 wgctx->frame_seq += 1;
207 wgctx->frame_arrived_cond.notify_one();
212 std::lock_guard
lock(wgctx->frame_arrived_mutex);
213 wgctx->window_closed =
true;
215 wgctx->frame_arrived_cond.notify_one();
222 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
224 if (wgctx->closed_token.value && wgctx->capture_item) {
225 CHECK_HR_LOG(wgctx->capture_item->remove_Closed(wgctx->closed_token));
226 wgctx->closed_token.value = 0;
229 if (wgctx->frame_arrived_token.value && wgctx->frame_pool) {
230 CHECK_HR_LOG(wgctx->frame_pool->remove_FrameArrived(wgctx->frame_arrived_token));
231 wgctx->frame_arrived_token.value = 0;
234 if (wgctx->capture_session) {
235 ComPtr<IClosable> closable;
236 if (SUCCEEDED(wgctx->capture_session.As(&closable))) {
243 if (wgctx->frame_pool) {
244 ComPtr<IClosable> closable;
245 if (SUCCEEDED(wgctx->frame_pool.As(&closable))) {
252 wgctx->capture_session.Reset();
253 wgctx->frame_pool.Reset();
254 wgctx->capture_item.Reset();
255 wgctx->d3d_device.Reset();
262 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
264 if (!
ctx->capture_hwnd) {
265 wgctx->client_area_offsets.left = 0;
266 wgctx->client_area_offsets.top = 0;
267 wgctx->client_area_offsets.right = 0;
268 wgctx->client_area_offsets.bottom = 0;
272 RECT client_rect = {};
273 RECT frame_bounds = {};
274 RECT window_rect = {};
276 if (IsIconic(
ctx->capture_hwnd)) {
281 if (!GetClientRect(
ctx->capture_hwnd, &client_rect)) {
287 if (!MapWindowPoints(
ctx->capture_hwnd,
nullptr, (POINT*)&client_rect, 2) && GetLastError()) {
292 if (FAILED(
ctx->fn.DwmGetWindowAttribute(
ctx->capture_hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_bounds,
sizeof(window_rect))))
295 if (!GetWindowRect(
ctx->capture_hwnd, &window_rect))
298 if (wgctx->cap_size.Width == frame_bounds.right - frame_bounds.left ||
299 wgctx->cap_size.Height == frame_bounds.bottom - frame_bounds.top) {
301 }
else if (wgctx->cap_size.Width == window_rect.right - window_rect.left ||
302 wgctx->cap_size.Height == window_rect.bottom - window_rect.top) {
304 frame_bounds = window_rect;
306 if ((frame_bounds.top == frame_bounds.bottom || frame_bounds.left == frame_bounds.right) &&
307 (window_rect.top == window_rect.bottom || window_rect.left == window_rect.right))
312 av_log(avctx,
AV_LOG_VERBOSE,
"Failed to get valid window rect, client area may be inaccurate\n");
316 wgctx->client_area_offsets.left =
FFMAX(client_rect.left - frame_bounds.left, 0);
317 wgctx->client_area_offsets.top =
FFMAX(client_rect.top - frame_bounds.top, 0);
318 wgctx->client_area_offsets.right =
FFMAX(frame_bounds.right - client_rect.right, 0);
319 wgctx->client_area_offsets.bottom =
FFMAX(frame_bounds.bottom - client_rect.bottom, 0);
321 av_log(avctx,
AV_LOG_DEBUG,
"Client area offsets: left=%ld top=%ld right=%ld bottom=%ld\n",
322 wgctx->client_area_offsets.left, wgctx->client_area_offsets.top,
323 wgctx->client_area_offsets.right, wgctx->client_area_offsets.bottom);
332 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
335 ComPtr<IDirect3D11CaptureFramePoolStatics2> frame_pool_statics;
336 ComPtr<ID3D11Device> d3d11_device =
ctx->device_hwctx->device;
337 ComPtr<ID3D10Multithread> d3d10_multithread;
338 ComPtr<IDXGIDevice> dxgi_device;
339 ComPtr<IGraphicsCaptureSession2> session2;
340 ComPtr<IGraphicsCaptureSession3> session3;
341 ComPtr<IGraphicsCaptureSession5> session5;
343 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
345 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
347 CHECK_HR_RET(wgctx->capture_item->get_Size(&wgctx->cap_size));
353 d3d10_multithread->SetMultithreadProtected(TRUE);
356 CHECK_HR_RET(
ctx->fn.CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), &wgctx->d3d_device));
360 CHECK_HR_RET(wgctx->frame_pool->CreateCaptureSession(wgctx->capture_item.Get(), &wgctx->capture_session));
362 if (SUCCEEDED(wgctx->capture_session.As(&session2))) {
363 if (FAILED(session2->put_IsCursorCaptureEnabled(cctx->
capture_cursor))) {
370 if (SUCCEEDED(wgctx->capture_session.As(&session3))) {
372 if (FAILED(session3->put_IsBorderRequired(cctx->
display_border))) {
379 if (SUCCEEDED(wgctx->capture_session.As(&session5))) {
381 if (FAILED(session5->put_MinUpdateInterval(ivl))) {
382 av_log(avctx,
AV_LOG_WARNING,
"Failed setting minimum update interval, framerate may be limited\n");
385 av_log(avctx,
AV_LOG_WARNING,
"Setting minimum update interval unavailable, framerate may be limited\n");
388 wgctx->window_closed = 0;
391 create_cb_handler<ITypedEventHandler<GraphicsCaptureItem*,IInspectable*>, IGraphicsCaptureItem*, IInspectable*>(
392 [avctx,
ctx](
auto,
auto) {
393 av_log(avctx, AV_LOG_INFO,
"Capture item closed\n");
394 wgc_closed_handler(ctx->wgc);
396 }).Get(), &wgctx->closed_token));
399 create_cb_handler<ITypedEventHandler<Direct3D11CaptureFramePool*,IInspectable*>, IDirect3D11CaptureFramePool*, IInspectable*>(
400 [avctx,
ctx](
auto,
auto) {
401 av_log(avctx, AV_LOG_TRACE,
"Frame arrived\n");
402 wgc_frame_arrived_handler(ctx->wgc);
404 }).Get(), &wgctx->frame_arrived_token));
413 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
417 ComPtr<IGraphicsCaptureItemInterop> capture_item_interop;
420 if (
ctx->capture_hmonitor) {
421 hr = capture_item_interop->CreateForMonitor(
ctx->capture_hmonitor, IID_PPV_ARGS(&wgctx->capture_item));
423 av_log(avctx,
AV_LOG_ERROR,
"Failed to setup graphics capture for monitor (0x%08lX)\n", hr);
426 }
else if (
ctx->capture_hwnd) {
427 hr = capture_item_interop->CreateForWindow(
ctx->capture_hwnd, IID_PPV_ARGS(&wgctx->capture_item));
429 av_log(avctx,
AV_LOG_ERROR,
"Failed to setup graphics capture for window (0x%08lX)\n", hr);
440 hr =
ctx->wgc->capture_session->StartCapture();
442 av_log(avctx,
AV_LOG_ERROR,
"Failed to start graphics capture session (0x%08lX)\n", hr);
453 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
455 ComPtr<IDirect3DSurface> capture_surface;
456 ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
457 ComPtr<ID3D11Texture2D> frame_texture;
460 CHECK_HR_RET(wgctx->frame_pool->TryGetNextFrame(&capture_frame));
468 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
470 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
487 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
491 PeekMessage(&msg,
nullptr, 0, 0, PM_NOREMOVE);
493 DispatcherQueueOptions
options = { 0 };
494 options.dwSize =
sizeof(DispatcherQueueOptions);
495 options.threadType = DISPATCHERQUEUE_THREAD_TYPE::DQTYPE_THREAD_CURRENT;
496 options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE::DQTAT_COM_NONE;
499 CHECK_HR_RET(wgctx->dispatcher_queue_controller->get_DispatcherQueue(&wgctx->dispatcher_queue));
512 ctx->fn.RoUninitialize();
522 ctx->wgc = std::make_unique<GfxCaptureContextWgc>();
524 ctx->fn.SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
526 hr =
ctx->fn.RoInitialize(RO_INIT_MULTITHREADED);
556 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
557 ComPtr<IAsyncAction> async;
562 while (BOOL res = GetMessage(&msg,
NULL, 0, 0)) {
573 if (FAILED(wgctx->dispatcher_queue_controller->ShutdownQueueAsync(&async))) {
578 [avctx,
ctx](
auto,
auto status) {
579 PostThreadMessage(
ctx->wgc_thread_id, WM_QUIT, 0, 0);
580 av_log(avctx,
AV_LOG_DEBUG,
"WGC thread async shutdown completed: %d\n", (
int)status);
588 TranslateMessage(&msg);
589 DispatchMessage(&msg);
593 av_log(avctx,
AV_LOG_ERROR,
"WGC Thread message loop ended without proper shutdown\n");
608 static const wchar_t name_prefix[] =
L"wgc_winrt@0x";
609 wchar_t thread_name[
FF_ARRAY_ELEMS(name_prefix) +
sizeof(
void*) * 2] = { 0 };
610 swprintf(thread_name,
FF_ARRAY_ELEMS(thread_name),
L"%ls%" PRIxPTR, name_prefix, (uintptr_t)avctx);
611 ctx->fn.SetThreadDescription(GetCurrentThread(), thread_name);
613 std::lock_guard init_lock(
ctx->wgc_thread_init_mutex);
614 ctx->wgc_thread_id = GetCurrentThreadId();
618 }
catch (
const std::bad_alloc &) {
620 }
catch (
const std::exception &e) {
621 av_log(avctx,
AV_LOG_ERROR,
"unhandled exception in WGC thread init: %s\n", e.what());
628 ctx->wgc_thread_init_cond.notify_all();
629 if (
ctx->wgc_thread_init_res < 0) {
630 ctx->wgc_thread_res =
ctx->wgc_thread_init_res;
639 }
catch (
const std::bad_alloc &) {
641 }
catch (
const std::exception &e) {
642 av_log(avctx,
AV_LOG_ERROR,
"unhandled exception in WGC thread worker: %s\n", e.what());
649 std::lock_guard uninit_lock(
ctx->wgc_thread_uninit_mutex);
652 ctx->wgc_thread_res =
ret;
665 if (
ctx->wgc_thread.joinable()) {
669 ctx->wgc_thread.join();
670 ret =
ctx->wgc_thread_res;
672 ctx->wgc_thread_id = 0;
683 if (
ctx->wgc_thread.joinable() ||
ctx->wgc_thread_id) {
688 std::unique_lock wgc_lock(
ctx->wgc_thread_init_mutex);
689 ctx->wgc_thread_init_res = INT_MAX;
693 }
catch (
const std::system_error &e) {
698 if (!
ctx->wgc_thread_init_cond.wait_for(wgc_lock, std::chrono::seconds(1), [&]() {
699 return ctx->wgc_thread_init_res != INT_MAX;
705 return ctx->wgc_thread_init_res;
713 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
715 std::lock_guard uninit_lock(
ctx->wgc_thread_uninit_mutex);
723 std::condition_variable
cond;
728 auto cbdata = std::make_shared<CBData>();
730 cbdata->done = cbdata->cancel =
false;
736 [
cb = std::forward<F>(
cb), cbdata]() {
738 std::lock_guard lock(cbdata->mutex);
744 } catch (const std::bad_alloc &) {
745 cbdata->ret = AVERROR(ENOMEM);
747 cbdata->ret = AVERROR_BUG;
753 cbdata->cond.notify_one();
761 std::unique_lock cblock(cbdata->mutex);
762 if (!cbdata->cond.wait_for(cblock, std::chrono::seconds(1), [&]() { return cbdata->done; })) {
763 cbdata->cancel =
true;
780 std::string pat(pattern);
782 auto flags = std::regex::ECMAScript | std::regex::optimize;
783 if (pat.rfind(
"(?i)", 0) == 0 || pat.rfind(
"(?I)", 0) == 0) {
785 flags |= std::regex::icase;
786 }
else if(pat.rfind(
"(?c)", 0) == 0 || pat.rfind(
"(?C)", 0) == 0) {
792 }
catch (
const std::regex_error &e) {
793 av_log(avctx,
AV_LOG_ERROR,
"Failed to compile regex '%s': %s\n", pat.c_str(), e.what());
804 int utf8size = WideCharToMultiByte(CP_UTF8, 0, in, -1,
nullptr, 0,
nullptr,
nullptr);
809 out->resize(utf8size - 1);
811 if (WideCharToMultiByte(CP_UTF8, 0, in, -1,
out->data(), utf8size,
nullptr,
nullptr) != utf8size)
822 if (!GetWindowThreadProcessId(hwnd, &pid))
825 handle_ptr_t proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
829 std::wstring image_name;
830 DWORD image_name_size = 512;
833 DWORD
len = image_name_size;
834 image_name.resize(
len);
835 if (QueryFullProcessImageNameW(proc.get(), 0, image_name.data(), &
len)) {
836 image_name.resize(
len);
839 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
840 image_name_size *= 2;
846 if (image_name.empty())
849 const wchar_t *
base = image_name.c_str();
850 size_t pos = image_name.find_last_of(
L"\\/");
851 if (
pos != std::string::npos)
876 ctx->capture_hmonitor = hmonitor;
881 if (EnumDisplayMonitors(
NULL,
NULL,
cb->proc,
cb->lparam) || !
ctx->capture_hmonitor)
885 std::regex text_regex;
889 std::regex class_regex;
893 std::regex exe_regex;
897 std::string window_text;
898 std::wstring window_text_w;
899 std::string window_class;
900 std::wstring window_class_w;
901 std::string window_exe;
904 if (!GetWindowRect(hwnd, &
r) ||
r.right <=
r.left ||
r.bottom <=
r.top || !IsWindowVisible(hwnd))
907 window_text_w.resize(GetWindowTextLengthW(hwnd) + 1);
908 int len = GetWindowTextW(hwnd, window_text_w.data(), (
int)window_text_w.size());
910 window_text_w.resize(
len);
917 window_class_w.resize(256);
918 len = GetClassNameW(hwnd, window_class_w.data(), (
int)window_class_w.size());
920 window_class_w.resize(
len);
922 window_class.clear();
924 window_class.clear();
930 hwnd, window_text.c_str(), window_class.c_str(), window_exe.c_str());
933 if (window_text.empty() || !std::regex_search(window_text, text_regex))
938 if (window_class.empty() || !std::regex_search(window_class, class_regex))
943 if (window_exe.empty() || !std::regex_search(window_exe, exe_regex))
948 window_text.c_str(), window_class.c_str(), window_exe.c_str());
949 ctx->capture_hwnd = hwnd;
952 if (EnumWindows(
cb->proc,
cb->lparam) || !
ctx->capture_hwnd)
956 ctx->capture_hmonitor = MonitorFromWindow(
ctx->capture_hwnd, MONITOR_DEFAULTTONEAREST);
958 if (!
ctx->capture_hmonitor) {
993 *
out =
reinterpret_cast<T>(GetProcAddress(hModule.get(), lpProcName));
1001#define LOAD_DLL(handle, name) \
1002 handle = hmodule_ptr_t(LoadLibraryExW(L##name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); \
1004 av_log(avctx, AV_LOG_ERROR, "Failed opening " #name "\n"); \
1005 return AVERROR(ENOSYS); \
1008#define LOAD_FUNC(handle, name) \
1009 GetProcAddressTyped(handle, #name, &ctx->fn.name); \
1010 if (!ctx->fn.name) { \
1011 av_log(avctx, AV_LOG_ERROR, "Failed loading " #name "\n"); \
1012 return AVERROR(ENOSYS); \
1016 LOAD_DLL(
ctx->fn.graphicscapture_handle,
"graphicscapture.dll");
1021 LOAD_DLL(
ctx->fn.coremsg_handle,
"coremessaging.dll");
1023 LOAD_DLL(
ctx->fn.kernel32_handle,
"kernel32.dll");
1024 LOAD_DLL(
ctx->fn.d3dcompiler_handle,
"d3dcompiler_47.dll");
1028 LOAD_FUNC(
ctx->fn.combase_handle, RoGetActivationFactory);
1029 LOAD_FUNC(
ctx->fn.combase_handle, WindowsCreateStringReference);
1031 LOAD_FUNC(
ctx->fn.dwmapi_handle, DwmGetWindowAttribute);
1033 LOAD_FUNC(
ctx->fn.d3d11_handle, CreateDirect3D11DeviceFromDXGIDevice);
1035 LOAD_FUNC(
ctx->fn.coremsg_handle, CreateDispatcherQueueController);
1037 LOAD_FUNC(
ctx->fn.user32_handle, SetThreadDpiAwarenessContext);
1039 LOAD_FUNC(
ctx->fn.kernel32_handle, SetThreadDescription);
1054 ctx->d3d = std::make_unique<GfxCaptureContextD3D>();
1058 ctx->fn.RoUninitialize =
nullptr;
1076 if (!
ctx->frames_ref)
1088 ctx->frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
1106 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
1127 cap_w -= wgctx->client_area_offsets.left + wgctx->client_area_offsets.right;
1128 cap_h -= wgctx->client_area_offsets.top + wgctx->client_area_offsets.bottom;
1148 std::unique_ptr<GfxCaptureContextD3D> &d3dctx =
ctx->d3d;
1151 ComPtr<ID3DBlob> vs_blob, ps_blob, err_blob;
1152 CD3D11_SAMPLER_DESC sampler_desc(CD3D11_DEFAULT{});
1153 UINT
flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
1155 hr =
ctx->fn.D3DCompile(
render_shader_src,
sizeof(
render_shader_src) - 1,
NULL,
NULL,
NULL,
"main_vs",
"vs_4_0",
flags, 0, &vs_blob, &err_blob);
1158 av_log(avctx,
AV_LOG_ERROR,
"Failed compiling vertex shader: %.*s\n", (
int)err_blob->GetBufferSize(), (
char*)err_blob->GetBufferPointer());
1165 const char *ps_entry =
"main_ps_bicubic";
1167 ps_entry =
"main_ps";
1168 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
1171 hr =
ctx->fn.D3DCompile(
render_shader_src,
sizeof(
render_shader_src) - 1,
NULL,
NULL,
NULL, ps_entry,
"ps_4_0",
flags, 0, &ps_blob, &err_blob);
1174 av_log(avctx,
AV_LOG_ERROR,
"Failed compiling pixel shader: %.*s\n", (
int)err_blob->GetBufferSize(), (
char*)err_blob->GetBufferPointer());
1181 CHECK_HR_RET(
ctx->device_hwctx->device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(),
NULL, &d3dctx->vertex_shader));
1182 CHECK_HR_RET(
ctx->device_hwctx->device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(),
NULL, &d3dctx->pixel_shader));
1184 CHECK_HR_RET(
ctx->device_hwctx->device->CreateSamplerState(&sampler_desc, &d3dctx->sampler_state));
1186 D3D11_BUFFER_DESC cb_desc = { 0 };
1187 cb_desc.ByteWidth = 48;
1188 cb_desc.Usage = D3D11_USAGE_DYNAMIC;
1189 cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1190 cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1192 CHECK_HR_RET(
ctx->device_hwctx->device->CreateBuffer(&cb_desc,
NULL, &d3dctx->shader_cb));
1194 CHECK_HR_RET(
ctx->device_hwctx->device->CreateDeferredContext(0, &d3dctx->deferred_ctx));
1217 if (!
ctx->device_ref)
1255 std::lock_guard wgc_lock(
ctx->wgc_thread_uninit_mutex);
1261 outlink->
w =
ctx->frames_ctx->width;
1262 outlink->
h =
ctx->frames_ctx->height;
1273 const D3D11_TEXTURE2D_DESC &dst_tex_desc,
1274 ID3D11RenderTargetView **rtv)
1276 D3D11_RENDER_TARGET_VIEW_DESC target_desc = {};
1277 target_desc.Format = dst_tex_desc.Format;
1279 if (dst_tex_desc.ArraySize > 1) {
1280 target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
1281 target_desc.Texture2DArray.ArraySize = 1;
1282 target_desc.Texture2DArray.FirstArraySlice = (uintptr_t)
frame->data[1];
1283 target_desc.Texture2DArray.MipSlice = 0;
1285 target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
1286 target_desc.Texture2D.MipSlice = 0;
1289 return dev->CreateRenderTargetView(
1290 reinterpret_cast<ID3D11Resource*
>(
frame->data[0]), &target_desc, rtv);
1296 ID3D11DeviceContext *dev_ctx =
ctx->device_hwctx->device_context;
1298 D3D11_TEXTURE2D_DESC dst_tex_desc;
1299 reinterpret_cast<ID3D11Texture2D*
>(
frame->data[0])->GetDesc(&dst_tex_desc);
1301 ComPtr<ID3D11RenderTargetView> rtv;
1304 const float clear_color[4] = {0.f, 0.f, 0.f, 1.f};
1305 ctx->device_hwctx->lock(
ctx->device_hwctx->lock_ctx);
1306 dev_ctx->ClearRenderTargetView(rtv.Get(), clear_color);
1307 ctx->device_hwctx->unlock(
ctx->device_hwctx->lock_ctx);
1316 std::unique_ptr<GfxCaptureContextD3D> &d3dctx =
ctx->d3d;
1317 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
1320 ID3D11DeviceContext *dev_ctx =
ctx->device_hwctx->device_context;
1321 ComPtr<ID3D11DeviceContext> &def_ctx = d3dctx->deferred_ctx;
1323 D3D11_TEXTURE2D_DESC dst_tex_desc;
1324 reinterpret_cast<ID3D11Texture2D*
>(
frame->data[0])->GetDesc(&dst_tex_desc);
1326 D3D11_TEXTURE2D_DESC src_tex_desc;
1327 src_tex->GetDesc(&src_tex_desc);
1329 ComPtr<ID3D11RenderTargetView> rtv;
1332 ComPtr<ID3D11ShaderResourceView> srv;
1333 CHECK_HR_RET(dev->CreateShaderResourceView(src_tex.Get(),
nullptr, &srv));
1341 crop_left += wgctx->client_area_offsets.left;
1342 crop_top += wgctx->client_area_offsets.top;
1343 crop_right += wgctx->client_area_offsets.right;
1344 crop_bottom += wgctx->client_area_offsets.bottom;
1353 int cropped_w = wgctx->cap_size.Width - crop_left - crop_right;
1354 int cropped_h = wgctx->cap_size.Height - crop_top - crop_bottom;
1356 D3D11_VIEWPORT viewport = { 0 };
1357 viewport.MinDepth = 0.f;
1358 viewport.MaxDepth = 1.f;
1362 viewport.Width = (
float)cropped_w;
1363 viewport.Height = (
float)cropped_h;
1366 viewport.Width = dst_tex_desc.Width;
1367 viewport.Height = dst_tex_desc.Height;
1370 float scale =
FFMIN(dst_tex_desc.Width / (
float)cropped_w,
1371 dst_tex_desc.Height / (
float)cropped_h);
1372 viewport.Width = cropped_w *
scale;
1373 viewport.Height = cropped_h *
scale;
1381 def_ctx->RSSetViewports(1, &viewport);
1383 D3D11_MAPPED_SUBRESOURCE
map;
1384 CHECK_HR_RET(def_ctx->Map(d3dctx->shader_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &
map));
1386 float *cb_f =
static_cast<float*
>(
map.pData);
1387 uint32_t *cb_u =
static_cast<uint32_t*
>(
map.pData);
1388 cb_f[0] = (
float)cropped_w;
1389 cb_f[1] = (
float)cropped_h;
1390 cb_f[2] = viewport.Width;
1391 cb_f[3] = viewport.Height;
1392 cb_f[4] = crop_left / (
float)src_tex_desc.Width;
1393 cb_f[5] = crop_top / (
float)src_tex_desc.Height;
1394 cb_f[6] = (crop_left + cropped_w) / (
float)src_tex_desc.Width;
1395 cb_f[7] = (crop_top + cropped_h) / (
float)src_tex_desc.Height;
1400 def_ctx->Unmap(d3dctx->shader_cb.Get(), 0);
1402 def_ctx->OMSetRenderTargets(1, rtv.GetAddressOf(),
nullptr);
1404 const float clear_color[4] = {0.f, 0.f, 0.f, 1.f};
1405 def_ctx->ClearRenderTargetView(rtv.Get(), clear_color);
1407 def_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1408 def_ctx->VSSetShader(d3dctx->vertex_shader.Get(),
nullptr, 0);
1409 def_ctx->VSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
1410 def_ctx->PSSetShader(d3dctx->pixel_shader.Get(),
nullptr, 0);
1411 def_ctx->PSSetSamplers(0, 1, d3dctx->sampler_state.GetAddressOf());
1412 def_ctx->PSSetShaderResources(0, 1, srv.GetAddressOf());
1413 def_ctx->PSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
1415 def_ctx->Draw(3, 0);
1417 ComPtr<ID3D11CommandList> cmd_list;
1418 CHECK_HR_RET(def_ctx->FinishCommandList(FALSE, &cmd_list));
1419 ctx->device_hwctx->lock(
ctx->device_hwctx->lock_ctx);
1420 dev_ctx->ExecuteCommandList(cmd_list.Get(), FALSE);
1421 ctx->device_hwctx->unlock(
ctx->device_hwctx->lock_ctx);
1448 static const int64_t freq = []() {
1450 QueryPerformanceFrequency(&
f);
1455 QueryPerformanceCounter(&now);
1458 return now.QuadPart;
1496 ComPtr<IDirect3D11CaptureFrame> capture_frame;
1497 ComPtr<IDirect3DSurface> capture_surface;
1498 ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
1499 ComPtr<ID3D11Texture2D> frame_texture;
1500 TimeSpan frame_time = { 0 };
1506 CHECK_HR_RET(capture_frame->get_SystemRelativeTime(&frame_time));
1508 CHECK_HR_RET(capture_frame->get_Surface(&capture_surface));
1509 CHECK_HR_RET(capture_surface.As(&dxgi_interface_access));
1510 CHECK_HR_RET(dxgi_interface_access->GetInterface(IID_PPV_ARGS(&frame_texture)));
1519 frame->pts = frame_time.Duration;
1556 if (!
ctx->last_frame) {
1558 if (!
ctx->last_frame)
1577 if (
ctx->next_pts <= now)
1588 std::unique_ptr<GfxCaptureContextWgc> &wgctx =
ctx->wgc;
1590 std::lock_guard wgc_lock(
ctx->wgc_thread_uninit_mutex);
1600 uint64_t last_seq = wgctx->frame_seq;
1606 std::unique_lock
frame_lock(wgctx->frame_arrived_mutex);
1608 if (wgctx->window_closed && wgctx->frame_seq == last_seq) {
1611 :
ctx->last_pts -
ctx->first_pts + 1);
1622 if (wgctx->frame_arrived_cond.wait_for(
frame_lock, std::chrono::nanoseconds(timeout * 100), [&]() {
1623 return wgctx->frame_seq != last_seq || wgctx->window_closed;
1642 }
catch (
const std::exception &e) {
1653 }
catch (
const std::bad_alloc&) {
1655 }
catch (
const std::exception &e) {
1668 }
catch (
const std::bad_alloc&) {
1670 }
catch (
const std::exception &e) {
1685 }
catch (
const std::bad_alloc&) {
1687 }
catch (
const std::exception &e) {
1688 av_log(avctx,
AV_LOG_ERROR,
"unhandled exception during config_props: %s\n", e.what());
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
int ff_outlink_frame_wanted(AVFilterLink *link)
Test if a frame is wanted on an output link.
Main libavfilter public API header.
#define flags(name, subs,...)
@ DXGI_FORMAT_R16G16B16A16_FLOAT
static const uint8_t frame_size[4]
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
#define AVERROR_EXTERNAL
Generic error in an external library.
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
#define AVERROR_EOF
End of file.
int av_frame_replace(AVFrame *dst, const AVFrame *src)
Ensure the destination frame refers to the same data described by the source frame,...
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_VERBOSE
Detailed information.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
#define AV_NOPTS_VALUE
Undefined timestamp value.
static av_always_inline void scale(vec_s32 res[4], vec_s16 res_packed[2], const int shift)
int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, const char *device, AVDictionary *opts, int flags)
Open a device of the specified type and create an AVHWDeviceContext for it.
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
@ AV_HWDEVICE_TYPE_D3D11VA
static av_cold void load_functions(void)
An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
const VDPAUPixFmtMap * map
static pthread_mutex_t lock
static void ff_outlink_set_status(AVFilterLink *link, int status, int64_t pts)
Set the status field of a link from the source filter.
#define FFERROR_NOT_READY
Filters implementation helper functions and internal structures.
static FilterLink * ff_filter_link(AVFilterLink *link)
common internal API header
Memory handling functions.
int(* cond)(enum AVPixelFormat pix_fmt)
#define AV_PIX_FMT_RGBAF16
@ AVCOL_RANGE_JPEG
Full range content.
@ AVALPHA_MODE_STRAIGHT
Alpha channel is independent of color values.
@ AVALPHA_MODE_PREMULTIPLIED
Alpha channel is multiplied into color values.
AVPixelFormat
Pixel format.
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
@ AV_PIX_FMT_D3D11
Hardware surfaces for Direct3D11.
@ AVCOL_PRI_BT709
also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
@ AVCOL_SPC_RGB
order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
#define FF_ARRAY_ELEMS(a)
A reference to a data buffer.
uint8_t * data
The data buffer.
This struct is allocated as AVHWDeviceContext.hwctx.
This struct is allocated as AVHWFramesContext.hwctx.
int extra_hw_frames
Sets the number of extra hardware frames which the filter will allocate on its output links for use i...
void * priv
private data for use by the filter
AVBufferRef * hw_device_ctx
For filters which will create hardware frames, sets the device the filter should create them in.
AVFilterLink ** outputs
array of pointers to output links
A link between two filters.
int w
agreed upon image width
int h
agreed upon image height
AVFilterContext * src
source filter
AVRational time_base
Define the time base used by the PTS of the frames/samples which will pass through this link.
enum AVAlphaMode alpha_mode
alpha mode (for videos with an alpha channel)
This structure describes decoded (raw) audio or video data.
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
This struct describes a set or pool of "hardware" frames (i.e.
Rational number (pair of numerator and denominator).
Link properties exposed to filter code, but not external callers.
AVRational frame_rate
Frame rate of the stream on the link, or 1/0 if unknown or variable.
AVBufferRef * hw_frames_ctx
For hwaccel pixel formats, this should be a reference to the AVHWFramesContext describing the frames.
volatile int wgc_thread_res
std::unique_ptr< GfxCaptureContextD3D > d3d
volatile int wgc_thread_init_res
std::unique_ptr< GfxCaptureContextWgc > wgc
AVD3D11VADeviceContext * device_hwctx
HMONITOR capture_hmonitor
std::mutex wgc_thread_init_mutex
std::recursive_mutex wgc_thread_uninit_mutex
AVD3D11VAFramesContext * frames_hwctx
AVHWDeviceContext * device_ctx
AVHWFramesContext * frames_ctx
std::condition_variable wgc_thread_init_cond
ComPtr< ID3D11Buffer > shader_cb
ComPtr< ID3D11VertexShader > vertex_shader
ComPtr< ID3D11PixelShader > pixel_shader
ComPtr< ID3D11SamplerState > sampler_state
ComPtr< ID3D11DeviceContext > deferred_ctx
std::mutex frame_arrived_mutex
ComPtr< IDispatcherQueueController > dispatcher_queue_controller
ComPtr< IGraphicsCaptureSession > capture_session
ComPtr< IDirect3D11CaptureFramePool > frame_pool
EventRegistrationToken frame_arrived_token
ComPtr< IDispatcherQueue > dispatcher_queue
ComPtr< IDirect3DDevice > d3d_device
ComPtr< IGraphicsCaptureItem > capture_item
std::condition_variable frame_arrived_cond
EventRegistrationToken closed_token
GfxCaptureContextCpp * ctx
const char * window_class
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR UINT UINT Flags2
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR UINT Flags1
hmodule_ptr_t dwmapi_handle
hmodule_ptr_t graphicscapture_handle
UINT32 HSTRING_HEADER HSTRING * string
PDISPATCHERQUEUECONTROLLER * dispatcherQueueController
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR pTarget
SIZE_T LPCSTR const D3D10_SHADER_MACRO * pDefines
hmodule_ptr_t coremsg_handle
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude * pInclude
hmodule_ptr_t user32_handle
IInspectable ** graphicsDevice
PCWSTR lpThreadDescription
HRESULT(WINAPI *SetThreadDescription)(HANDLE hThread
hmodule_ptr_t d3d11_handle
DWORD PVOID DWORD cbAttribute
hmodule_ptr_t combase_handle
HRESULT(WINAPI *WindowsCreateStringReference)(PCWSTR sourceString
HRESULT(WINAPI *D3DCompile)(LPCVOID pSrcData
UINT32 HSTRING_HEADER * hstringHeader
void(WINAPI *RoUninitialize)(void)
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR UINT UINT ID3DBlob ID3DBlob ** ppErrorMsgs
HRESULT(WINAPI *RoInitialize)(RO_INIT_TYPE initType)
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR UINT UINT ID3DBlob ** ppCode
hmodule_ptr_t kernel32_handle
hmodule_ptr_t d3dcompiler_handle
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR pEntrypoint
SIZE_T LPCSTR pSourceName
DPI_AWARENESS_CONTEXT(WINAPI *SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext)
static double cb(void *priv, double x, double y)
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
@ GFX_RESIZE_SCALE_ASPECT
static const char render_shader_src[]
static int init_hwframes_ctx(AVFilterContext *avctx)
static int run_on_wgc_thread(AVFilterContext *avctx, F &&cb)
av_cold int ff_gfxcapture_init(AVFilterContext *avctx) noexcept
static int output_frame(AVFilterLink *outlink, AVFrame *frame)
static int64_t dup_interval(GfxCaptureContext *cctx)
static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
static int clear_frame(AVFilterContext *avctx, AVFrame *frame)
#define CHECK_HR_LOG(...)
static int wgc_try_get_next_frame(AVFilterContext *avctx, ComPtr< IDirect3D11CaptureFrame > &capture_frame)
#define LOAD_DLL(handle, name)
static void wgc_thread_entry(AVFilterContext *avctx) noexcept
static int output_dup_frame(AVFilterLink *outlink)
static int start_wgc_thread(AVFilterContext *avctx)
int ff_gfxcapture_activate(AVFilterContext *avctx) noexcept
static int gfxcapture_activate(AVFilterContext *avctx)
static av_cold void gfxcapture_uninit(AVFilterContext *avctx)
#define CAPTURE_POOL_SIZE
static int process_frame_if_exists(AVFilterLink *outlink)
av_cold void ff_gfxcapture_uninit(AVFilterContext *avctx) noexcept
static void wgc_closed_handler(const std::unique_ptr< GfxCaptureContextWgc > &wgctx)
static av_cold int gfxcapture_init(AVFilterContext *avctx)
static int get_window_exe_name(HWND hwnd, std::string *out)
static int wstring_to_utf8(const wchar_t *in, std::string *out)
static int build_regex(AVFilterContext *avctx, const char *pattern, std::regex *out)
static int wgc_setup_winrt(AVFilterContext *avctx)
static int prepare_render_resources(AVFilterContext *avctx)
static void set_frame_props(GfxCaptureContextCpp *ctx, AVFrame *frame)
static av_cold void GetProcAddressTyped(const hmodule_ptr_t &hModule, LPCSTR lpProcName, T *out)
static void wgc_frame_arrived_handler(const std::unique_ptr< GfxCaptureContextWgc > &wgctx)
#define LOAD_FUNC(handle, name)
static int wgc_setup_gfxcapture_capture(AVFilterContext *avctx)
static HRESULT get_activation_factory(GfxCaptureContextCpp *ctx, PCWSTR clsid, T **factory)
static int gfxcapture_config_props(AVFilterLink *outlink)
static int wgc_calculate_client_area(AVFilterContext *avctx)
static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
static HRESULT create_frame_rtv(ID3D11Device *dev, const AVFrame *frame, const D3D11_TEXTURE2D_DESC &dst_tex_desc, ID3D11RenderTargetView **rtv)
static int render_capture_to_frame(AVFilterContext *avctx, AVFrame *frame, const ComPtr< ID3D11Texture2D > &src_tex)
static int wgc_thread_init(AVFilterContext *avctx)
static int find_capture_source(AVFilterContext *avctx)
static int stop_wgc_thread(AVFilterContext *avctx)
static int wgc_thread_worker(AVFilterContext *avctx)
int ff_gfxcapture_config_props(AVFilterLink *outlink) noexcept
#define CHECK_HR_RET(...)
static int setup_gfxcapture_capture(AVFilterContext *avctx)
static int wgc_setup_gfxcapture_session(AVFilterContext *avctx)
std::unique_ptr< HMODULE, HMODULEDeleter > hmodule_ptr_t
static Microsoft::WRL::ComPtr< Iface > create_cb_handler(F &&cb_func)
std::unique_ptr< HANDLE, HANDLEDeleter > handle_ptr_t
#define make_win32_callback(...)