FFmpeg
Loading...
Searching...
No Matches
vsrc_gfxcapture_winrt.cpp
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
21
22#include <dwmapi.h>
23#include <d3d11.h>
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>
32#endif
33
34extern "C" {
35#include "libavutil/avassert.h"
36#include "libavutil/internal.h"
37#include "libavutil/mem.h"
38#include "libavutil/opt.h"
39#include "libavutil/time.h"
40#include "libavutil/pixdesc.h"
41#include "libavutil/hwcontext.h"
43#include "avfilter.h"
44#include "filters.h"
45#include "video.h"
46
47#include "vsrc_gfxcapture.h"
48}
49
50#include <cinttypes>
51#include <condition_variable>
52#include <cwchar>
53#include <memory>
54#include <mutex>
55#include <regex>
56#include <string>
57#include <system_error>
58#include <thread>
59#include <type_traits>
60
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;
70
71#define TIMESPAN_RES 10000000
72#define TIMESPAN_RES64 INT64_C(10000000)
73
74#define CAPTURE_POOL_SIZE 2
75
76enum {
78};
79
80#define CCTX(ctx) static_cast<GfxCaptureContext*>(ctx)
81
82typedef struct GfxCaptureFunctions {
84
86 HRESULT (WINAPI *RoInitialize)(RO_INIT_TYPE initType);
87 void (WINAPI *RoUninitialize)(void);
88 HRESULT (WINAPI *RoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory);
89 HRESULT (WINAPI *WindowsCreateStringReference)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);
90
92 HRESULT (WINAPI *DwmGetWindowAttribute)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);
93
95 HRESULT (WINAPI *CreateDirect3D11DeviceFromDXGIDevice)(IDXGIDevice *dxgiDevice, IInspectable **graphicsDevice);
96
98 HRESULT (WINAPI *CreateDispatcherQueueController)(DispatcherQueueOptions options, PDISPATCHERQUEUECONTROLLER *dispatcherQueueController);
99
101 DPI_AWARENESS_CONTEXT (WINAPI *SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext);
102
104 HRESULT (WINAPI *SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
105
107 HRESULT (WINAPI *D3DCompile)(LPCVOID pSrcData, SIZE_T SrcDataSize, LPCSTR pSourceName, const D3D10_SHADER_MACRO *pDefines, ID3DInclude *pInclude,
108 LPCSTR pEntrypoint, LPCSTR pTarget, UINT Flags1, UINT Flags2, ID3DBlob **ppCode, ID3DBlob **ppErrorMsgs);
110
111// This struct contains all data handled by the capture thread
113 ComPtr<IDispatcherQueueController> dispatcher_queue_controller;
114 ComPtr<IDispatcherQueue> dispatcher_queue;
115
116 ComPtr<IGraphicsCaptureItem> capture_item;
117 ComPtr<IDirect3DDevice> d3d_device;
118 ComPtr<IDirect3D11CaptureFramePool> frame_pool;
119 ComPtr<IGraphicsCaptureSession> capture_session;
120
121 EventRegistrationToken frame_arrived_token { 0 };
122 EventRegistrationToken closed_token { 0 };
123
125 std::condition_variable frame_arrived_cond;
126 bool window_closed { false };
127 uint64_t frame_seq { 0 };
128
129 SizeInt32 cap_size { 0, 0 };
130 RECT client_area_offsets { 0, 0, 0, 0 };
131};
132
134 ComPtr<ID3D11VertexShader> vertex_shader;
135 ComPtr<ID3D11PixelShader> pixel_shader;
136 ComPtr<ID3D11SamplerState> sampler_state;
137 ComPtr<ID3D11Buffer> shader_cb;
138 ComPtr<ID3D11DeviceContext> deferred_ctx;
139};
140
143 std::unique_ptr<GfxCaptureContextWgc> wgc;
144 std::unique_ptr<GfxCaptureContextD3D> d3d;
145
146 std::thread wgc_thread;
147 DWORD wgc_thread_id { 0 };
149 std::condition_variable wgc_thread_init_cond;
150 volatile int wgc_thread_init_res { INT_MAX };
151 std::recursive_mutex wgc_thread_uninit_mutex;
152 volatile int wgc_thread_res { 0 };
153 std::shared_ptr<void> wgc_thread_cb_data;
154
155 HWND capture_hwnd { nullptr };
156 HMONITOR capture_hmonitor { nullptr };
157
161
165
168};
169
170template <typename T>
171static HRESULT get_activation_factory(GfxCaptureContextCpp *ctx, PCWSTR clsid, T** factory) {
172 HSTRING_HEADER hsheader = { 0 };
173 HSTRING hs = NULL;
174
175 HRESULT hr = ctx->fn.WindowsCreateStringReference(clsid, (UINT32)wcslen(clsid), &hsheader, &hs);
176 if (FAILED(hr))
177 return hr;
178
179 return ctx->fn.RoGetActivationFactory(hs, IID_PPV_ARGS(factory));
180}
181
182#define CHECK_HR(fcall, action) \
183 do { \
184 HRESULT fhr = fcall; \
185 if (FAILED(fhr)) { \
186 av_log(avctx, AV_LOG_ERROR, #fcall " failed: 0x%08lX\n", fhr); \
187 action; \
188 } \
189 } while (0)
190#define CHECK_HR_RET(...) CHECK_HR((__VA_ARGS__), return AVERROR_EXTERNAL)
191#define CHECK_HR_FAIL(...) CHECK_HR((__VA_ARGS__), ret = AVERROR_EXTERNAL; goto fail)
192#define CHECK_HR_LOG(...) CHECK_HR((__VA_ARGS__), (void)0)
193
194/****************************************************
195 * Windows Graphics Capture Worker Thread *
196 * All wgc_* functions must run only on WGC thread! *
197 ****************************************************/
198
199static void wgc_frame_arrived_handler(const std::unique_ptr<GfxCaptureContextWgc> &wgctx) {
200 {
201 std::lock_guard lock(wgctx->frame_arrived_mutex);
202 wgctx->frame_seq += 1;
203 }
204 wgctx->frame_arrived_cond.notify_one();
205}
206
207static void wgc_closed_handler(const std::unique_ptr<GfxCaptureContextWgc> &wgctx) {
208 {
209 std::lock_guard lock(wgctx->frame_arrived_mutex);
210 wgctx->window_closed = true;
211 }
212 wgctx->frame_arrived_cond.notify_one();
213}
214
215static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
216{
217 GfxCaptureContext *cctx = CCTX(avctx->priv);
219 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
220
221 if (wgctx->closed_token.value && wgctx->capture_item) {
222 CHECK_HR_LOG(wgctx->capture_item->remove_Closed(wgctx->closed_token));
223 wgctx->closed_token.value = 0;
224 }
225
226 if (wgctx->frame_arrived_token.value && wgctx->frame_pool) {
227 CHECK_HR_LOG(wgctx->frame_pool->remove_FrameArrived(wgctx->frame_arrived_token));
228 wgctx->frame_arrived_token.value = 0;
229 }
230
231 if (wgctx->capture_session) {
232 ComPtr<IClosable> closable;
233 if (SUCCEEDED(wgctx->capture_session.As(&closable))) {
234 CHECK_HR_LOG(closable->Close());
235 } else {
236 av_log(avctx, AV_LOG_ERROR, "Failed to get capture session IClosable interface\n");
237 }
238 }
239
240 if (wgctx->frame_pool) {
241 ComPtr<IClosable> closable;
242 if (SUCCEEDED(wgctx->frame_pool.As(&closable))) {
243 CHECK_HR_LOG(closable->Close());
244 } else {
245 av_log(avctx, AV_LOG_ERROR, "Failed to get frame pool IClosable interface\n");
246 }
247 }
248
249 wgctx->capture_session.Reset();
250 wgctx->frame_pool.Reset();
251 wgctx->capture_item.Reset();
252 wgctx->d3d_device.Reset();
253}
254
256{
257 GfxCaptureContext *cctx = CCTX(avctx->priv);
259 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
260
261 if (!ctx->capture_hwnd) {
262 wgctx->client_area_offsets.left = 0;
263 wgctx->client_area_offsets.top = 0;
264 wgctx->client_area_offsets.right = 0;
265 wgctx->client_area_offsets.bottom = 0;
266 return 0;
267 }
268
269 RECT client_rect = {};
270 RECT frame_bounds = {};
271 RECT window_rect = {};
272
273 if (IsIconic(ctx->capture_hwnd)) {
274 av_log(avctx, AV_LOG_VERBOSE, "Capture window is iconic, no client area\n");
275 return 0;
276 }
277
278 if (!GetClientRect(ctx->capture_hwnd, &client_rect)) {
279 av_log(avctx, AV_LOG_ERROR, "GetClientRect failed\n");
280 return AVERROR_EXTERNAL;
281 }
282
283 SetLastError(0);
284 if (!MapWindowPoints(ctx->capture_hwnd, nullptr, (POINT*)&client_rect, 2) && GetLastError()) {
285 av_log(avctx, AV_LOG_ERROR, "MapWindowPoints failed\n");
286 return AVERROR_EXTERNAL;
287 }
288
289 if (FAILED(ctx->fn.DwmGetWindowAttribute(ctx->capture_hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_bounds, sizeof(window_rect))))
290 av_log(avctx, AV_LOG_DEBUG, "DwmGetWindowAttribute failed\n");
291
292 if (!GetWindowRect(ctx->capture_hwnd, &window_rect))
293 av_log(avctx, AV_LOG_DEBUG, "GetWindowRect failed\n");
294
295 if (wgctx->cap_size.Width == frame_bounds.right - frame_bounds.left ||
296 wgctx->cap_size.Height == frame_bounds.bottom - frame_bounds.top) {
297 av_log(avctx, AV_LOG_DEBUG, "Using window rect from DWMWA_EXTENDED_FRAME_BOUNDS\n");
298 } else if (wgctx->cap_size.Width == window_rect.right - window_rect.left ||
299 wgctx->cap_size.Height == window_rect.bottom - window_rect.top) {
300 av_log(avctx, AV_LOG_DEBUG, "Using window rect from GetWindowRect\n");
301 frame_bounds = window_rect;
302 } else {
303 if ((frame_bounds.top == frame_bounds.bottom || frame_bounds.left == frame_bounds.right) &&
304 (window_rect.top == window_rect.bottom || window_rect.left == window_rect.right))
305 {
306 av_log(avctx, AV_LOG_ERROR, "No valid window rect found\n");
307 return AVERROR_EXTERNAL;
308 }
309 av_log(avctx, AV_LOG_VERBOSE, "Failed to get valid window rect, client area may be inaccurate\n");
310 return 0;
311 }
312
313 wgctx->client_area_offsets.left = FFMAX(client_rect.left - frame_bounds.left, 0);
314 wgctx->client_area_offsets.top = FFMAX(client_rect.top - frame_bounds.top, 0);
315 wgctx->client_area_offsets.right = FFMAX(frame_bounds.right - client_rect.right, 0);
316 wgctx->client_area_offsets.bottom = FFMAX(frame_bounds.bottom - client_rect.bottom, 0);
317
318 av_log(avctx, AV_LOG_DEBUG, "Client area offsets: left=%ld top=%ld right=%ld bottom=%ld\n",
319 wgctx->client_area_offsets.left, wgctx->client_area_offsets.top,
320 wgctx->client_area_offsets.right, wgctx->client_area_offsets.bottom);
321
322 return 0;
323}
324
326{
327 GfxCaptureContext *cctx = CCTX(avctx->priv);
329 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
330 int ret;
331
332 ComPtr<IDirect3D11CaptureFramePoolStatics2> frame_pool_statics;
333 ComPtr<ID3D11Device> d3d11_device = ctx->device_hwctx->device;
334 ComPtr<ID3D10Multithread> d3d10_multithread;
335 ComPtr<IDXGIDevice> dxgi_device;
336 ComPtr<IGraphicsCaptureSession2> session2;
337 ComPtr<IGraphicsCaptureSession3> session3;
338 ComPtr<IGraphicsCaptureSession5> session5;
339
340 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
341 if (cctx->out_fmt != AV_PIX_FMT_BGRA)
342 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
343
344 CHECK_HR_RET(wgctx->capture_item->get_Size(&wgctx->cap_size));
345 ret = wgc_calculate_client_area(avctx);
346 if (ret < 0)
347 return ret;
348
349 CHECK_HR_RET(d3d11_device.As(&d3d10_multithread));
350 d3d10_multithread->SetMultithreadProtected(TRUE);
351
352 CHECK_HR_RET(d3d11_device.As(&dxgi_device));
353 CHECK_HR_RET(ctx->fn.CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), &wgctx->d3d_device));
354
355 CHECK_HR_RET(get_activation_factory<IDirect3D11CaptureFramePoolStatics2>(ctx, RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool, &frame_pool_statics));
356 CHECK_HR_RET(frame_pool_statics->CreateFreeThreaded(wgctx->d3d_device.Get(), fmt, CAPTURE_POOL_SIZE, wgctx->cap_size, &wgctx->frame_pool));
357 CHECK_HR_RET(wgctx->frame_pool->CreateCaptureSession(wgctx->capture_item.Get(), &wgctx->capture_session));
358
359 if (SUCCEEDED(wgctx->capture_session.As(&session2))) {
360 if (FAILED(session2->put_IsCursorCaptureEnabled(cctx->capture_cursor))) {
361 av_log(avctx, AV_LOG_WARNING, "Failed setting cursor capture mode\n");
362 }
363 } else {
364 av_log(avctx, AV_LOG_WARNING, "Cursor capture unavailable\n");
365 }
366
367 if (SUCCEEDED(wgctx->capture_session.As(&session3))) {
368 // this one is weird, it can return failure but still work
369 if (FAILED(session3->put_IsBorderRequired(cctx->display_border))) {
370 av_log(avctx, AV_LOG_WARNING, "Failed setting border drawing mode\n");
371 }
372 } else {
373 av_log(avctx, AV_LOG_WARNING, "Disabling border drawing unavailable\n");
374 }
375
376 if (SUCCEEDED(wgctx->capture_session.As(&session5))) {
377 TimeSpan ivl = { av_rescale_q(1, av_inv_q(cctx->frame_rate), AVRational{1, TIMESPAN_RES}) };
378 if (FAILED(session5->put_MinUpdateInterval(ivl))) {
379 av_log(avctx, AV_LOG_WARNING, "Failed setting minimum update interval, framerate may be limited\n");
380 }
381 } else {
382 av_log(avctx, AV_LOG_WARNING, "Setting minimum update interval unavailable, framerate may be limited\n");
383 }
384
385 wgctx->window_closed = 0;
386
387 CHECK_HR_RET(wgctx->capture_item->add_Closed(
388 create_cb_handler<ITypedEventHandler<GraphicsCaptureItem*,IInspectable*>, IGraphicsCaptureItem*, IInspectable*>(
389 [avctx, ctx](auto, auto) {
390 av_log(avctx, AV_LOG_INFO, "Capture item closed\n");
391 wgc_closed_handler(ctx->wgc);
392 return S_OK;
393 }).Get(), &wgctx->closed_token));
394
395 CHECK_HR_RET(wgctx->frame_pool->add_FrameArrived(
396 create_cb_handler<ITypedEventHandler<Direct3D11CaptureFramePool*,IInspectable*>, IDirect3D11CaptureFramePool*, IInspectable*>(
397 [avctx, ctx](auto, auto) {
398 av_log(avctx, AV_LOG_TRACE, "Frame arrived\n");
399 wgc_frame_arrived_handler(ctx->wgc);
400 return S_OK;
401 }).Get(), &wgctx->frame_arrived_token));
402
403 return 0;
404}
405
407{
408 GfxCaptureContext *cctx = CCTX(avctx->priv);
410 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
411 HRESULT hr;
412 int ret;
413
414 ComPtr<IGraphicsCaptureItemInterop> capture_item_interop;
415 CHECK_HR_RET(get_activation_factory<IGraphicsCaptureItemInterop>(ctx, RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem, &capture_item_interop));
416
417 if (ctx->capture_hmonitor) {
418 hr = capture_item_interop->CreateForMonitor(ctx->capture_hmonitor, IID_PPV_ARGS(&wgctx->capture_item));
419 if (FAILED(hr)) {
420 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for monitor (0x%08lX)\n", hr);
421 return AVERROR_EXTERNAL;
422 }
423 } else if (ctx->capture_hwnd) {
424 hr = capture_item_interop->CreateForWindow(ctx->capture_hwnd, IID_PPV_ARGS(&wgctx->capture_item));
425 if (FAILED(hr)) {
426 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for window (0x%08lX)\n", hr);
427 return AVERROR_EXTERNAL;
428 }
429 }
430
431 ret = wgc_setup_gfxcapture_session(avctx);
432 if (ret < 0) {
433 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture pool\n");
434 return ret;
435 }
436
437 hr = ctx->wgc->capture_session->StartCapture();
438 if (FAILED(hr)) {
439 av_log(avctx, AV_LOG_ERROR, "Failed to start graphics capture session (0x%08lX)\n", hr);
440 return AVERROR_EXTERNAL;
441 }
442
443 return 0;
444}
445
446static int wgc_try_get_next_frame(AVFilterContext *avctx, ComPtr<IDirect3D11CaptureFrame> &capture_frame)
447{
448 GfxCaptureContext *cctx = CCTX(avctx->priv);
450 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
451
452 ComPtr<IDirect3DSurface> capture_surface;
453 ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
454 ComPtr<ID3D11Texture2D> frame_texture;
455 SizeInt32 frame_size = { 0, 0 };
456
457 CHECK_HR_RET(wgctx->frame_pool->TryGetNextFrame(&capture_frame));
458 if (!capture_frame)
459 return AVERROR(EAGAIN);
460
461 CHECK_HR_RET(capture_frame->get_ContentSize(&frame_size));
462 if (frame_size.Width != wgctx->cap_size.Width || frame_size.Height != wgctx->cap_size.Height) {
463 av_log(avctx, AV_LOG_VERBOSE, "Capture size changed to %dx%d\n", frame_size.Width, frame_size.Height);
464
465 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
466 if (cctx->out_fmt != AV_PIX_FMT_BGRA)
467 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
468
469 CHECK_HR_RET(wgctx->frame_pool->Recreate(wgctx->d3d_device.Get(), fmt, CAPTURE_POOL_SIZE, frame_size));
470 wgctx->cap_size = frame_size;
471
472 int ret = wgc_calculate_client_area(avctx);
473 if (ret < 0)
474 return ret;
475 }
476
477 return 0;
478}
479
481{
482 GfxCaptureContext *cctx = CCTX(avctx->priv);
484 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
485 MSG msg;
486
487 // pre-create the message-queue
488 PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE);
489
490 DispatcherQueueOptions options = { 0 };
491 options.dwSize = sizeof(DispatcherQueueOptions);
492 options.threadType = DISPATCHERQUEUE_THREAD_TYPE::DQTYPE_THREAD_CURRENT;
493 options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE::DQTAT_COM_NONE;
494
495 CHECK_HR_RET(ctx->fn.CreateDispatcherQueueController(options, &wgctx->dispatcher_queue_controller));
496 CHECK_HR_RET(wgctx->dispatcher_queue_controller->get_DispatcherQueue(&wgctx->dispatcher_queue));
497
498 return 0;
499}
500
501static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
502{
503 GfxCaptureContext *cctx = CCTX(avctx->priv);
505
507
508 ctx->wgc.reset();
509 ctx->fn.RoUninitialize();
510}
511
513{
514 GfxCaptureContext *cctx = CCTX(avctx->priv);
516 HRESULT hr;
517 int ret;
518
519 ctx->wgc = std::make_unique<GfxCaptureContextWgc>();
520
521 ctx->fn.SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
522
523 hr = ctx->fn.RoInitialize(RO_INIT_MULTITHREADED);
524 if (FAILED(hr)) {
525 av_log(avctx, AV_LOG_ERROR, "Failed to initialize WinRT\n");
526 ctx->wgc.reset();
527 return AVERROR_EXTERNAL;
528 }
529
530 ret = wgc_setup_winrt(avctx);
531 if (ret < 0) {
532 av_log(avctx, AV_LOG_ERROR, "Failed to setup WinRT\n");
533 goto fail;
534 }
535
536 ret = wgc_setup_gfxcapture_capture(avctx);
537 if (ret < 0) {
538 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
539 goto fail;
540 }
541
542 return 0;
543
544fail:
545 wgc_thread_uninit(avctx);
546 return ret;
547}
548
550{
551 GfxCaptureContext *cctx = CCTX(avctx->priv);
553 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
554 ComPtr<IAsyncAction> async;
555 MSG msg;
556
557 av_log(avctx, AV_LOG_DEBUG, "Starting message loop\n");
558
559 while (BOOL res = GetMessage(&msg, NULL, 0, 0)) {
560 if (res == -1) {
561 av_log(avctx, AV_LOG_ERROR, "Failed to get message\n");
562 return AVERROR(EIO);
563 }
564
565 if (!msg.hwnd && msg.message == WM_WGC_THREAD_SHUTDOWN) {
566 av_log(avctx, AV_LOG_DEBUG, "Initializing WGC thread shutdown\n");
567
569
570 if (FAILED(wgctx->dispatcher_queue_controller->ShutdownQueueAsync(&async))) {
571 av_log(avctx, AV_LOG_ERROR, "Failed to shutdown dispatcher queue\n");
572 return AVERROR_EXTERNAL;
573 }
575 [avctx, ctx](auto, auto status) {
576 PostThreadMessage(ctx->wgc_thread_id, WM_QUIT, 0, 0);
577 av_log(avctx, AV_LOG_DEBUG, "WGC thread async shutdown completed: %d\n", (int)status);
578 return S_OK;
579 }).Get());
580 continue;
581 }
582
583 av_log(avctx, AV_LOG_TRACE, "Got message: %u\n", msg.message);
584
585 TranslateMessage(&msg);
586 DispatchMessage(&msg);
587 }
588
589 if (!async) {
590 av_log(avctx, AV_LOG_ERROR, "WGC Thread message loop ended without proper shutdown\n");
591 return AVERROR_EXTERNAL;
592 }
593
594 av_log(avctx, AV_LOG_DEBUG, "Message loop ended\n");
595
596 return msg.wParam;
597}
598
599static void wgc_thread_entry(AVFilterContext *avctx) noexcept
600{
601 GfxCaptureContext *cctx = CCTX(avctx->priv);
603
604 {
605 static const wchar_t name_prefix[] = L"wgc_winrt@0x";
606 wchar_t thread_name[FF_ARRAY_ELEMS(name_prefix) + sizeof(void*) * 2] = { 0 };
607 swprintf(thread_name, FF_ARRAY_ELEMS(thread_name), L"%ls%" PRIxPTR, name_prefix, (uintptr_t)avctx);
608 ctx->fn.SetThreadDescription(GetCurrentThread(), thread_name);
609
610 std::lock_guard init_lock(ctx->wgc_thread_init_mutex);
611 ctx->wgc_thread_id = GetCurrentThreadId();
612
613 try {
614 ctx->wgc_thread_init_res = wgc_thread_init(avctx);
615 } catch (const std::bad_alloc &) {
616 ctx->wgc_thread_init_res = AVERROR(ENOMEM);
617 } catch (const std::exception &e) {
618 av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread init: %s\n", e.what());
619 ctx->wgc_thread_init_res = AVERROR_BUG;
620 } catch (...) {
621 av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread init\n");
622 ctx->wgc_thread_init_res = AVERROR_BUG;
623 }
624
625 ctx->wgc_thread_init_cond.notify_all();
626 if (ctx->wgc_thread_init_res < 0) {
627 ctx->wgc_thread_res = ctx->wgc_thread_init_res;
628 return;
629 }
630 }
631
632 int ret;
633
634 try {
635 ret = wgc_thread_worker(avctx);
636 } catch (const std::bad_alloc &) {
637 ret = AVERROR(ENOMEM);
638 } catch (const std::exception &e) {
639 av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread worker: %s\n", e.what());
640 ret = AVERROR_BUG;
641 } catch (...) {
642 av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread worker\n");
643 ret = AVERROR_BUG;
644 }
645
646 std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
647 wgc_thread_uninit(avctx);
648
649 ctx->wgc_thread_res = ret;
650}
651
652/***********************************
653 * WGC Thread Management Functions *
654 ***********************************/
655
657{
658 GfxCaptureContext *cctx = CCTX(avctx->priv);
660 int ret = 0;
661
662 if (ctx->wgc_thread.joinable()) {
663 if (ctx->wgc_thread_id && !PostThreadMessage(ctx->wgc_thread_id, WM_WGC_THREAD_SHUTDOWN, 0, 0))
664 av_log(avctx, AV_LOG_ERROR, "Failed to post shutdown message to WGC thread\n");
665
666 ctx->wgc_thread.join();
667 ret = ctx->wgc_thread_res;
668
669 ctx->wgc_thread_id = 0;
670 }
671
672 return ret;
673}
674
676{
677 GfxCaptureContext *cctx = CCTX(avctx->priv);
679
680 if (ctx->wgc_thread.joinable() || ctx->wgc_thread_id) {
681 av_log(avctx, AV_LOG_ERROR, "Double-creation of WGC thread\n");
682 return AVERROR_BUG;
683 }
684
685 std::unique_lock wgc_lock(ctx->wgc_thread_init_mutex);
686 ctx->wgc_thread_init_res = INT_MAX;
687
688 try {
689 ctx->wgc_thread = std::thread(wgc_thread_entry, avctx);
690 } catch (const std::system_error &e) {
691 av_log(avctx, AV_LOG_ERROR, "Failed to create WGC thread: %s\n", e.what());
692 return AVERROR_EXTERNAL;
693 }
694
695 if (!ctx->wgc_thread_init_cond.wait_for(wgc_lock, std::chrono::seconds(1), [&]() {
696 return ctx->wgc_thread_init_res != INT_MAX;
697 })) {
698 av_log(avctx, AV_LOG_ERROR, "WGC thread init timed out\n");
699 return AVERROR(ETIMEDOUT);
700 }
701
702 return ctx->wgc_thread_init_res;
703}
704
705template <typename F>
707{
708 GfxCaptureContext *cctx = CCTX(avctx->priv);
710 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
711
712 std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
713 if (!wgctx) {
714 av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
715 return AVERROR(ENOSYS);
716 }
717
718 struct CBData {
719 std::mutex mutex;
720 std::condition_variable cond;
721 bool done;
722 bool cancel;
723 int ret;
724 };
725 auto cbdata = ctx->wgc_thread_cb_data ?
726 std::static_pointer_cast<CBData>(ctx->wgc_thread_cb_data) :
727 std::make_shared<CBData>();
728 ctx->wgc_thread_cb_data = cbdata;
729
730 cbdata->done = cbdata->cancel = false;
731 cbdata->ret = AVERROR_BUG;
732
733 boolean res = 0;
734 CHECK_HR_RET(wgctx->dispatcher_queue->TryEnqueue(
736 [cb = std::forward<F>(cb), cbdata]() {
737 {
738 std::lock_guard lock(cbdata->mutex);
739 if (cbdata->cancel)
740 return S_OK;
741
742 try {
743 cbdata->ret = cb();
744 } catch (const std::bad_alloc &) {
745 cbdata->ret = AVERROR(ENOMEM);
746 } catch (...) {
747 cbdata->ret = AVERROR_BUG;
748 }
749
750 cbdata->done = true;
751 }
752
753 cbdata->cond.notify_one();
754 return S_OK;
755 }).Get(), &res));
756 if (!res) {
757 av_log(avctx, AV_LOG_ERROR, "Failed to enqueue WGC thread callback\n");
758 return AVERROR_EXTERNAL;
759 }
760
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;
764 av_log(avctx, AV_LOG_ERROR, "WGC thread callback timed out\n");
765 return AVERROR(ETIMEDOUT);
766 }
767
768 return cbdata->ret;
769}
770
771/*******************************
772 * Standard AVFilter functions *
773 *******************************/
774
775static int build_regex(AVFilterContext *avctx, const char *pattern, std::regex *out)
776{
777 if (!pattern)
778 return 0;
779
780 std::string pat(pattern);
781
782 auto flags = std::regex::ECMAScript | std::regex::optimize;
783 if (pat.rfind("(?i)", 0) == 0 || pat.rfind("(?I)", 0) == 0) {
784 pat.erase(0, 4);
785 flags |= std::regex::icase;
786 } else if(pat.rfind("(?c)", 0) == 0 || pat.rfind("(?C)", 0) == 0) {
787 pat.erase(0, 4);
788 }
789
790 try {
791 *out = std::regex(pat, flags);
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());
794 return AVERROR(EINVAL);
795 }
796
797 av_log(avctx, AV_LOG_DEBUG, "Built regex: %s\n", pattern);
798
799 return 0;
800}
801
802static int wstring_to_utf8(const wchar_t *in, std::string *out)
803{
804 int utf8size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
805 if (utf8size <= 0)
806 return AVERROR(EINVAL);
807
808 // over-writing std::string by one is valid in C++17 according to 27.4.3.6 if and only if it's overwritten with 0
809 out->resize(utf8size - 1);
810
811 if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out->data(), utf8size, nullptr, nullptr) != utf8size)
812 return AVERROR_EXTERNAL;
813
814 return 0;
815}
816
817static int get_window_exe_name(HWND hwnd, std::string *out)
818{
819 out->clear();
820
821 DWORD pid = 0;
822 if (!GetWindowThreadProcessId(hwnd, &pid))
823 return AVERROR(ENOENT);
824
825 handle_ptr_t proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
826 if (!proc)
827 return AVERROR(EACCES);
828
829 std::wstring image_name;
830 DWORD image_name_size = 512;
831
832 for (;;) {
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);
837 break;
838 }
839 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
840 image_name_size *= 2;
841 continue;
842 }
843 return AVERROR_EXTERNAL;
844 }
845
846 if (image_name.empty())
847 return AVERROR_EXTERNAL;
848
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)
852 base += pos + 1;
853
854 return wstring_to_utf8(base, out);
855}
856
858{
859 GfxCaptureContext *cctx = CCTX(avctx->priv);
861 int cur_idx = 0;
862
863 ctx->capture_hwnd = NULL;
864 ctx->capture_hmonitor = NULL;
865
866 if (cctx->user_hmonitor) {
867 ctx->capture_hmonitor = (HMONITOR)(uintptr_t)cctx->user_hmonitor;
868 return 0;
869 } else if (cctx->user_hwnd) {
870 ctx->capture_hwnd = (HWND)(uintptr_t)cctx->user_hwnd;
871 return 0;
872 } else if (cctx->monitor_idx >= 0) {
873 auto cb = make_win32_callback([&](HMONITOR hmonitor, HDC, LPRECT) {
874 if (cur_idx++ == cctx->monitor_idx) {
875 av_log(avctx, AV_LOG_DEBUG, "Found capture monitor: %d\n", cctx->monitor_idx);
876 ctx->capture_hmonitor = hmonitor;
877 return FALSE;
878 }
879 return TRUE;
880 });
881 if (EnumDisplayMonitors(NULL, NULL, cb->proc, cb->lparam) || !ctx->capture_hmonitor)
882 return AVERROR(ENOENT);
883 return 0;
884 } else if (cctx->window_text || cctx->window_class || cctx->window_exe) {
885 std::regex text_regex;
886 if (build_regex(avctx, cctx->window_text, &text_regex) < 0)
887 return AVERROR(EINVAL);
888
889 std::regex class_regex;
890 if (build_regex(avctx, cctx->window_class, &class_regex) < 0)
891 return AVERROR(EINVAL);
892
893 std::regex exe_regex;
894 if (build_regex(avctx, cctx->window_exe, &exe_regex) < 0)
895 return AVERROR(EINVAL);
896
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;
902 auto cb = make_win32_callback([&](HWND hwnd) {
903 RECT r = { 0 };
904 if (!GetWindowRect(hwnd, &r) || r.right <= r.left || r.bottom <= r.top || !IsWindowVisible(hwnd))
905 return TRUE;
906
907 window_text_w.resize(GetWindowTextLengthW(hwnd) + 1);
908 int len = GetWindowTextW(hwnd, window_text_w.data(), (int)window_text_w.size());
909 if (len >= 0) {
910 window_text_w.resize(len);
911 if (wstring_to_utf8(window_text_w.c_str(), &window_text) < 0)
912 window_text.clear();
913 } else {
914 window_text.clear();
915 }
916
917 window_class_w.resize(256);
918 len = GetClassNameW(hwnd, window_class_w.data(), (int)window_class_w.size());
919 if (len >= 0) {
920 window_class_w.resize(len);
921 if (wstring_to_utf8(window_class_w.c_str(), &window_class) < 0)
922 window_class.clear();
923 } else {
924 window_class.clear();
925 }
926
927 get_window_exe_name(hwnd, &window_exe);
928
929 av_log(avctx, AV_LOG_TRACE, "Checking window: hwnd=%p text=%s class=%s exe=%s\n",
930 hwnd, window_text.c_str(), window_class.c_str(), window_exe.c_str());
931
932 if (cctx->window_text) {
933 if (window_text.empty() || !std::regex_search(window_text, text_regex))
934 return TRUE;
935 }
936
937 if (cctx->window_class) {
938 if (window_class.empty() || !std::regex_search(window_class, class_regex))
939 return TRUE;
940 }
941
942 if (cctx->window_exe) {
943 if (window_exe.empty() || !std::regex_search(window_exe, exe_regex))
944 return TRUE;
945 }
946
947 av_log(avctx, AV_LOG_VERBOSE, "Found capture window: %s (Class: %s, Exe: %s)\n",
948 window_text.c_str(), window_class.c_str(), window_exe.c_str());
949 ctx->capture_hwnd = hwnd;
950 return FALSE;
951 });
952 if (EnumWindows(cb->proc, cb->lparam) || !ctx->capture_hwnd)
953 return AVERROR(ENOENT);
954
955 if (cctx->monitor_idx == GFX_MONITOR_IDX_WINDOW) {
956 ctx->capture_hmonitor = MonitorFromWindow(ctx->capture_hwnd, MONITOR_DEFAULTTONEAREST);
957 ctx->capture_hwnd = NULL;
958 if (!ctx->capture_hmonitor) {
959 av_log(avctx, AV_LOG_ERROR, "Failed to get monitor for capture window\n");
960 return AVERROR(ENOENT);
961 }
962 }
963
964 return 0;
965 }
966
967 av_log(avctx, AV_LOG_ERROR, "No capture source specified\n");
968 return AVERROR(EINVAL);
969}
970
972{
973 GfxCaptureContext *cctx = CCTX(avctx->priv);
975
976 if (!ctx)
977 return;
978
979 stop_wgc_thread(avctx);
980
981 ctx->d3d.reset();
982
983 av_buffer_unref(&ctx->frames_ref);
984 av_buffer_unref(&ctx->device_ref);
985
986 delete ctx;
987 cctx->ctx = nullptr;
988}
989
990template<typename T>
991static av_cold void GetProcAddressTyped(const hmodule_ptr_t &hModule, LPCSTR lpProcName, T *out) {
992 *out = reinterpret_cast<T>(GetProcAddress(hModule.get(), lpProcName));
993}
994
996{
997 GfxCaptureContext *cctx = CCTX(avctx->priv);
999
1000#define LOAD_DLL(handle, name) \
1001 handle = hmodule_ptr_t(LoadLibraryExW(L##name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); \
1002 if (!handle) { \
1003 av_log(avctx, AV_LOG_ERROR, "Failed opening " #name "\n"); \
1004 return AVERROR(ENOSYS); \
1005 }
1006
1007#define LOAD_FUNC(handle, name) \
1008 GetProcAddressTyped(handle, #name, &ctx->fn.name); \
1009 if (!ctx->fn.name) { \
1010 av_log(avctx, AV_LOG_ERROR, "Failed loading " #name "\n"); \
1011 return AVERROR(ENOSYS); \
1012 }
1013
1014 // this handle is not used anywhere, but letting it get auto-freed during RoUninit causes crashes
1015 LOAD_DLL(ctx->fn.graphicscapture_handle, "graphicscapture.dll");
1016
1017 LOAD_DLL(ctx->fn.combase_handle, "combase.dll");
1018 LOAD_DLL(ctx->fn.dwmapi_handle, "dwmapi.dll");
1019 LOAD_DLL(ctx->fn.d3d11_handle, "d3d11.dll");
1020 LOAD_DLL(ctx->fn.coremsg_handle, "coremessaging.dll");
1021 LOAD_DLL(ctx->fn.user32_handle, "user32.dll");
1022 LOAD_DLL(ctx->fn.kernel32_handle, "kernel32.dll");
1023 LOAD_DLL(ctx->fn.d3dcompiler_handle, "d3dcompiler_47.dll");
1024
1025 LOAD_FUNC(ctx->fn.combase_handle, RoInitialize);
1026 LOAD_FUNC(ctx->fn.combase_handle, RoUninitialize);
1027 LOAD_FUNC(ctx->fn.combase_handle, RoGetActivationFactory);
1028 LOAD_FUNC(ctx->fn.combase_handle, WindowsCreateStringReference);
1029
1030 LOAD_FUNC(ctx->fn.dwmapi_handle, DwmGetWindowAttribute);
1031
1032 LOAD_FUNC(ctx->fn.d3d11_handle, CreateDirect3D11DeviceFromDXGIDevice);
1033
1034 LOAD_FUNC(ctx->fn.coremsg_handle, CreateDispatcherQueueController);
1035
1036 LOAD_FUNC(ctx->fn.user32_handle, SetThreadDpiAwarenessContext);
1037
1038 LOAD_FUNC(ctx->fn.kernel32_handle, SetThreadDescription);
1039
1040 LOAD_FUNC(ctx->fn.d3dcompiler_handle, D3DCompile);
1041
1042#undef LOAD_FUNC
1043#undef LOAD_DLL
1044 return 0;
1045}
1046
1048{
1049 GfxCaptureContext *cctx = CCTX(avctx->priv);
1050 int ret = 0;
1051
1053 ctx->d3d = std::make_unique<GfxCaptureContextD3D>();
1054
1055 ret = load_functions(avctx);
1056 if (ret < 0) {
1057 ctx->fn.RoUninitialize = nullptr;
1058 goto fail;
1059 }
1060
1061 return 0;
1062
1063fail:
1064 gfxcapture_uninit(avctx);
1065 return ret;
1066}
1067
1069{
1070 GfxCaptureContext *cctx = CCTX(avctx->priv);
1071 GfxCaptureContextCpp *ctx = cctx->ctx;
1072 int ret = 0;
1073
1074 ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
1075 if (!ctx->frames_ref)
1076 return AVERROR(ENOMEM);
1077 ctx->frames_ctx = (AVHWFramesContext*)ctx->frames_ref->data;
1078 ctx->frames_hwctx = (AVD3D11VAFramesContext*)ctx->frames_ctx->hwctx;
1079
1080 ctx->frames_ctx->format = AV_PIX_FMT_D3D11;
1081 ctx->frames_ctx->width = cctx->canvas_width;
1082 ctx->frames_ctx->height = cctx->canvas_height;
1083 ctx->frames_ctx->sw_format = (AVPixelFormat)cctx->out_fmt;
1084 if (avctx->extra_hw_frames > 0)
1085 ctx->frames_ctx->initial_pool_size = 8 + avctx->extra_hw_frames;
1086
1087 ctx->frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
1088
1089 ret = av_hwframe_ctx_init(ctx->frames_ref);
1090 if (ret < 0) {
1091 av_log(avctx, AV_LOG_ERROR, "Failed to initialise hardware frames context: %d.\n", ret);
1092 goto fail;
1093 }
1094
1095 return 0;
1096fail:
1097 av_buffer_unref(&ctx->frames_ref);
1098 return ret;
1099}
1100
1102{
1103 GfxCaptureContext *cctx = CCTX(avctx->priv);
1104 GfxCaptureContextCpp *ctx = cctx->ctx;
1105 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1106 int ret = 0;
1107
1108 stop_wgc_thread(avctx);
1109
1110 ret = find_capture_source(avctx);
1111 if (ret < 0) {
1112 av_log(avctx, AV_LOG_ERROR, "Failed to find capture source\n");
1113 return ret;
1114 }
1115
1116 ret = start_wgc_thread(avctx);
1117 if (ret < 0) {
1118 av_log(avctx, AV_LOG_ERROR, "Failed to start WGC thread\n");
1119 return ret;
1120 }
1121
1122 int cap_w = wgctx->cap_size.Width - cctx->crop_left - cctx->crop_right;
1123 int cap_h = wgctx->cap_size.Height - cctx->crop_top - cctx->crop_bottom;
1124
1125 if (!cctx->capture_border) {
1126 cap_w -= wgctx->client_area_offsets.left + wgctx->client_area_offsets.right;
1127 cap_h -= wgctx->client_area_offsets.top + wgctx->client_area_offsets.bottom;
1128 }
1129
1130 if (cctx->canvas_width == 0)
1131 cctx->canvas_width = cap_w;
1132 else if (cctx->canvas_width < 0)
1133 cctx->canvas_width = (cap_w / cctx->canvas_width) * cctx->canvas_width;
1134
1135 if (cctx->canvas_height == 0)
1136 cctx->canvas_height = cap_h;
1137 else if (cctx->canvas_height < 0)
1138 cctx->canvas_height = (cap_h / cctx->canvas_height) * cctx->canvas_height;
1139
1140 return 0;
1141}
1142
1144{
1145 GfxCaptureContext *cctx = CCTX(avctx->priv);
1146 GfxCaptureContextCpp *ctx = cctx->ctx;
1147 std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
1148 HRESULT hr;
1149
1150 ComPtr<ID3DBlob> vs_blob, ps_blob, err_blob;
1151 CD3D11_SAMPLER_DESC sampler_desc(CD3D11_DEFAULT{});
1152 UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
1153
1154 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);
1155 if (FAILED(hr)) {
1156 if (err_blob) {
1157 av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: %.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
1158 } else {
1159 av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: 0x%08lX\n", hr);
1160 }
1161 return AVERROR_EXTERNAL;
1162 }
1163
1164 const char *ps_entry = "main_ps_bicubic";
1165 if (cctx->resize_mode == GFX_RESIZE_CROP || cctx->scale_mode == GFX_SCALE_POINT) {
1166 ps_entry = "main_ps";
1167 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
1168 }
1169
1170 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);
1171 if (FAILED(hr)) {
1172 if (err_blob) {
1173 av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: %.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
1174 } else {
1175 av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: 0x%08lX\n", hr);
1176 }
1177 return AVERROR_EXTERNAL;
1178 }
1179
1180 CHECK_HR_RET(ctx->device_hwctx->device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), NULL, &d3dctx->vertex_shader));
1181 CHECK_HR_RET(ctx->device_hwctx->device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), NULL, &d3dctx->pixel_shader));
1182
1183 CHECK_HR_RET(ctx->device_hwctx->device->CreateSamplerState(&sampler_desc, &d3dctx->sampler_state));
1184
1185 D3D11_BUFFER_DESC cb_desc = { 0 };
1186 cb_desc.ByteWidth = 48;
1187 cb_desc.Usage = D3D11_USAGE_DYNAMIC;
1188 cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1189 cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1190
1191 CHECK_HR_RET(ctx->device_hwctx->device->CreateBuffer(&cb_desc, NULL, &d3dctx->shader_cb));
1192
1193 CHECK_HR_RET(ctx->device_hwctx->device->CreateDeferredContext(0, &d3dctx->deferred_ctx));
1194
1195 return 0;
1196}
1197
1199{
1200 AVFilterContext *avctx = outlink->src;
1201 GfxCaptureContext *cctx = CCTX(avctx->priv);
1202 GfxCaptureContextCpp *ctx = cctx->ctx;
1203
1204 FilterLink *link = ff_filter_link(outlink);
1205 int ret;
1206
1207 if (avctx->hw_device_ctx) {
1208 ctx->device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
1209
1210 if (ctx->device_ctx->type != AV_HWDEVICE_TYPE_D3D11VA) {
1211 av_log(avctx, AV_LOG_ERROR, "Non-D3D11VA input hw_device_ctx\n");
1212 return AVERROR(EINVAL);
1213 }
1214
1215 ctx->device_ref = av_buffer_ref(avctx->hw_device_ctx);
1216 if (!ctx->device_ref)
1217 return AVERROR(ENOMEM);
1218
1219 av_log(avctx, AV_LOG_VERBOSE, "Using provided hw_device_ctx\n");
1220 } else {
1222 if (ret < 0) {
1223 av_log(avctx, AV_LOG_ERROR, "Failed to create D3D11VA device.\n");
1224 return ret;
1225 }
1226
1227 ctx->device_ctx = (AVHWDeviceContext*)ctx->device_ref->data;
1228
1229 av_log(avctx, AV_LOG_VERBOSE, "Created internal hw_device_ctx\n");
1230 }
1231
1232 ctx->device_hwctx = (AVD3D11VADeviceContext*)ctx->device_ctx->hwctx;
1233
1234 ret = prepare_render_resources(avctx);
1235 if (ret < 0) {
1236 av_log(avctx, AV_LOG_ERROR, "Failed to prepare render resources\n");
1237 return ret;
1238 }
1239
1240 ret = setup_gfxcapture_capture(avctx);
1241 if (ret < 0) {
1242 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
1243 return ret;
1244 }
1245
1246 ret = init_hwframes_ctx(avctx);
1247 if (ret < 0)
1248 return ret;
1249
1250 link->hw_frames_ctx = av_buffer_ref(ctx->frames_ref);
1251 if (!link->hw_frames_ctx)
1252 return AVERROR(ENOMEM);
1253
1254 std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
1255 if (!ctx->wgc) {
1256 av_log(avctx, AV_LOG_ERROR, "WGC thread died prematurely\n");
1257 return AVERROR(ENOSYS);
1258 }
1259
1260 outlink->w = ctx->frames_ctx->width;
1261 outlink->h = ctx->frames_ctx->height;
1262 outlink->time_base = AVRational{1, TIMESPAN_RES};
1264 link->frame_rate = cctx->frame_rate;
1265
1266 av_log(avctx, AV_LOG_DEBUG, "Capture setup with res %dx%d\n", outlink->w, outlink->h);
1267
1268 return 0;
1269}
1270
1271static int render_capture_to_frame(AVFilterContext *avctx, AVFrame *frame, const ComPtr<ID3D11Texture2D> &src_tex)
1272{
1273 GfxCaptureContext *cctx = CCTX(avctx->priv);
1274 GfxCaptureContextCpp *ctx = cctx->ctx;
1275 std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
1276 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1277
1278 ID3D11Device *dev = ctx->device_hwctx->device;
1279 ID3D11DeviceContext *dev_ctx = ctx->device_hwctx->device_context;
1280 ComPtr<ID3D11DeviceContext> &def_ctx = d3dctx->deferred_ctx;
1281
1282 D3D11_TEXTURE2D_DESC dst_tex_desc;
1283 reinterpret_cast<ID3D11Texture2D*>(frame->data[0])->GetDesc(&dst_tex_desc);
1284
1285 D3D11_TEXTURE2D_DESC src_tex_desc;
1286 src_tex->GetDesc(&src_tex_desc);
1287
1288 D3D11_RENDER_TARGET_VIEW_DESC target_desc = {};
1289 target_desc.Format = dst_tex_desc.Format;
1290
1291 if (dst_tex_desc.ArraySize > 1) {
1292 target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
1293 target_desc.Texture2DArray.ArraySize = 1;
1294 target_desc.Texture2DArray.FirstArraySlice = (uintptr_t)frame->data[1];
1295 target_desc.Texture2DArray.MipSlice = 0;
1296 } else {
1297 target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
1298 target_desc.Texture2D.MipSlice = 0;
1299 }
1300
1301 ComPtr<ID3D11RenderTargetView> rtv;
1302 CHECK_HR_RET(dev->CreateRenderTargetView(
1303 reinterpret_cast<ID3D11Resource*>(frame->data[0]), &target_desc, &rtv));
1304
1305 ComPtr<ID3D11ShaderResourceView> srv;
1306 CHECK_HR_RET(dev->CreateShaderResourceView(src_tex.Get(), nullptr, &srv));
1307
1308 int crop_left = cctx->crop_left;
1309 int crop_top = cctx->crop_top;
1310 int crop_right = cctx->crop_right;
1311 int crop_bottom = cctx->crop_bottom;
1312
1313 if (!cctx->capture_border) {
1314 crop_left += wgctx->client_area_offsets.left;
1315 crop_top += wgctx->client_area_offsets.top;
1316 crop_right += wgctx->client_area_offsets.right;
1317 crop_bottom += wgctx->client_area_offsets.bottom;
1318 }
1319
1320 // Using the actual capture frame size here adjusts for jank that can happen during rapid
1321 // resizing of the source window. The capture frame pool is only recreated once a frame
1322 // of changed size came out of it, so we need to cut/pad such frames to fit.
1323 // Just discarding such frames can lead to visible stutter if the source window is being
1324 // resized continuously, so this code does its best to adjust them instead. With the risk
1325 // of slight clamping artifacts when enlarging rapidly.
1326 int cropped_w = wgctx->cap_size.Width - crop_left - crop_right;
1327 int cropped_h = wgctx->cap_size.Height - crop_top - crop_bottom;
1328
1329 D3D11_VIEWPORT viewport = { 0 };
1330 viewport.MinDepth = 0.f;
1331 viewport.MaxDepth = 1.f;
1332
1333 switch (cctx->resize_mode) {
1334 case GFX_RESIZE_CROP:
1335 viewport.Width = (float)cropped_w;
1336 viewport.Height = (float)cropped_h;
1337 break;
1338 case GFX_RESIZE_SCALE:
1339 viewport.Width = dst_tex_desc.Width;
1340 viewport.Height = dst_tex_desc.Height;
1341 break;
1343 float scale = FFMIN(dst_tex_desc.Width / (float)cropped_w,
1344 dst_tex_desc.Height / (float)cropped_h);
1345 viewport.Width = cropped_w * scale;
1346 viewport.Height = cropped_h * scale;
1347 break;
1348 }
1349 default:
1350 av_log(avctx, AV_LOG_ERROR, "Invalid scaling mode\n");
1351 return AVERROR_BUG;
1352 };
1353
1354 def_ctx->RSSetViewports(1, &viewport);
1355
1356 D3D11_MAPPED_SUBRESOURCE map;
1357 CHECK_HR_RET(def_ctx->Map(d3dctx->shader_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map));
1358 {
1359 float *cb_f = static_cast<float*>(map.pData);
1360 uint32_t *cb_u = static_cast<uint32_t*>(map.pData);
1361 cb_f[0] = (float)cropped_w;
1362 cb_f[1] = (float)cropped_h;
1363 cb_f[2] = viewport.Width;
1364 cb_f[3] = viewport.Height;
1365 cb_f[4] = crop_left / (float)src_tex_desc.Width; // min_u
1366 cb_f[5] = crop_top / (float)src_tex_desc.Height; // min_v
1367 cb_f[6] = (crop_left + cropped_w) / (float)src_tex_desc.Width; // max_u
1368 cb_f[7] = (crop_top + cropped_h) / (float)src_tex_desc.Height; // max_v
1369 cb_u[8] = !cctx->premult_alpha; // to_unpremult
1370 cb_u[9] = src_tex_desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT &&
1371 dst_tex_desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT; // to_srgb
1372 }
1373 def_ctx->Unmap(d3dctx->shader_cb.Get(), 0);
1374
1375 def_ctx->OMSetRenderTargets(1, rtv.GetAddressOf(), nullptr);
1376
1377 const float clear_color[4] = {0.f, 0.f, 0.f, 1.f};
1378 def_ctx->ClearRenderTargetView(rtv.Get(), clear_color);
1379
1380 def_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1381 def_ctx->VSSetShader(d3dctx->vertex_shader.Get(), nullptr, 0);
1382 def_ctx->VSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
1383 def_ctx->PSSetShader(d3dctx->pixel_shader.Get(), nullptr, 0);
1384 def_ctx->PSSetSamplers(0, 1, d3dctx->sampler_state.GetAddressOf());
1385 def_ctx->PSSetShaderResources(0, 1, srv.GetAddressOf());
1386 def_ctx->PSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
1387
1388 def_ctx->Draw(3, 0);
1389
1390 ComPtr<ID3D11CommandList> cmd_list;
1391 CHECK_HR_RET(def_ctx->FinishCommandList(FALSE, &cmd_list));
1392 dev_ctx->ExecuteCommandList(cmd_list.Get(), FALSE);
1393
1394 return 0;
1395}
1396
1398{
1399 AVFilterContext *avctx = outlink->src;
1400 GfxCaptureContext *cctx = CCTX(avctx->priv);
1401 GfxCaptureContextCpp *ctx = cctx->ctx;
1402 int ret;
1403
1404 AVFrame *frame = nullptr;
1405
1406 ret = run_on_wgc_thread(avctx, [&]() {
1407 ComPtr<IDirect3D11CaptureFrame> capture_frame;
1408 ComPtr<IDirect3DSurface> capture_surface;
1409 ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
1410 ComPtr<ID3D11Texture2D> frame_texture;
1411 TimeSpan frame_time = { 0 };
1412
1413 int res = wgc_try_get_next_frame(avctx, capture_frame);
1414 if (res < 0)
1415 return res;
1416
1417 CHECK_HR_RET(capture_frame->get_SystemRelativeTime(&frame_time));
1418
1419 CHECK_HR_RET(capture_frame->get_Surface(&capture_surface));
1420 CHECK_HR_RET(capture_surface.As(&dxgi_interface_access));
1421 CHECK_HR_RET(dxgi_interface_access->GetInterface(IID_PPV_ARGS(&frame_texture)));
1422
1423 if (!frame_texture)
1424 return AVERROR(EAGAIN);
1425
1426 frame = ff_get_video_buffer(outlink, cctx->canvas_width, cctx->canvas_height);
1427 if (!frame)
1428 return AVERROR(ENOMEM);
1429
1430 frame->pts = frame_time.Duration;
1431
1432 return render_capture_to_frame(avctx, frame, frame_texture);
1433 });
1434 if (ret < 0)
1435 return ret;
1436
1437 frame->sample_aspect_ratio = AVRational{1, 1};
1438
1439 if (ctx->frames_ctx->sw_format == AV_PIX_FMT_RGBAF16) {
1440 // According to MSDN, all floating point formats contain sRGB image data with linear 1.0 gamma.
1441 frame->color_range = AVCOL_RANGE_JPEG;
1442 frame->color_primaries = AVCOL_PRI_BT709;
1443 frame->color_trc = AVCOL_TRC_LINEAR;
1444 frame->colorspace = AVCOL_SPC_RGB;
1445 } else {
1446 // According to MSDN, all integer formats contain sRGB image data
1447 frame->color_range = AVCOL_RANGE_JPEG;
1448 frame->color_primaries = AVCOL_PRI_BT709;
1449 frame->color_trc = AVCOL_TRC_IEC61966_2_1;
1450 frame->colorspace = AVCOL_SPC_RGB;
1451 }
1452
1453 ctx->last_pts = frame->pts;
1454
1455 if (!ctx->first_pts)
1456 ctx->first_pts = frame->pts;
1457 frame->pts -= ctx->first_pts;
1458
1459 return ff_filter_frame(outlink, frame);
1460}
1461
1463{
1464 AVFilterLink *outlink = avctx->outputs[0];
1465 GfxCaptureContext *cctx = CCTX(avctx->priv);
1466 GfxCaptureContextCpp *ctx = cctx->ctx;
1467 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1468
1469 std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
1470 if (!wgctx) {
1471 av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
1472 return AVERROR(ENOSYS);
1473 }
1474
1475 if (!ff_outlink_frame_wanted(outlink))
1476 return FFERROR_NOT_READY;
1477
1478 for (;;) {
1479 uint64_t last_seq = wgctx->frame_seq;
1480
1481 int ret = process_frame_if_exists(outlink);
1482 if (ret != AVERROR(EAGAIN))
1483 return ret;
1484
1485 std::unique_lock frame_lock(wgctx->frame_arrived_mutex);
1486
1487 if (wgctx->window_closed && wgctx->frame_seq == last_seq) {
1488 ff_outlink_set_status(outlink, AVERROR_EOF, ctx->last_pts - ctx->first_pts + 1);
1489 break;
1490 }
1491
1492 if (!wgctx->frame_arrived_cond.wait_for(frame_lock, std::chrono::seconds(1), [&]() {
1493 return wgctx->frame_seq != last_seq || wgctx->window_closed;
1494 }))
1495 break;
1496 }
1497
1498 return 0;
1499}
1500
1502{
1503 try {
1504 gfxcapture_uninit(avctx);
1505 } catch (const std::exception &e) {
1506 av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit: %s\n", e.what());
1507 } catch (...) {
1508 av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit\n");
1509 }
1510}
1511
1513{
1514 try {
1515 return gfxcapture_init(avctx);
1516 } catch (const std::bad_alloc&) {
1517 return AVERROR(ENOMEM);
1518 } catch (const std::exception &e) {
1519 av_log(avctx, AV_LOG_ERROR, "unhandled exception during init: %s\n", e.what());
1520 return AVERROR_BUG;
1521 } catch (...) {
1522 av_log(avctx, AV_LOG_ERROR, "unhandled exception during init\n");
1523 return AVERROR_BUG;
1524 }
1525}
1526
1528{
1529 try {
1530 return gfxcapture_activate(avctx);
1531 } catch (const std::bad_alloc&) {
1532 return AVERROR(ENOMEM);
1533 } catch (const std::exception &e) {
1534 av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate: %s\n", e.what());
1535 return AVERROR_BUG;
1536 } catch (...) {
1537 av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate\n");
1538 return AVERROR_BUG;
1539 }
1540}
1541
1543{
1544 AVFilterContext *avctx = outlink->src;
1545
1546 try {
1547 return gfxcapture_config_props(outlink);
1548 } catch (const std::bad_alloc&) {
1549 return AVERROR(ENOMEM);
1550 } catch (const std::exception &e) {
1551 av_log(avctx, AV_LOG_ERROR, "unhandled exception during config_props: %s\n", e.what());
1552 return AVERROR_BUG;
1553 } catch (...) {
1554 av_log(avctx, AV_LOG_ERROR, "unhandled exception during config_props\n");
1555 return AVERROR_BUG;
1556 }
1557}
static FILE * out
static AVFormatContext * ctx
#define T(x)
Definition vpx_arith.h:29
#define L(x)
Definition vpx_arith.h:36
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.
Definition avfilter.c:1068
int ff_outlink_frame_wanted(AVFilterLink *link)
Test if a frame is wanted on an output link.
Definition avfilter.c:1690
Main libavfilter public API header.
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
@ DXGI_FORMAT_R16G16B16A16_FLOAT
Definition dds.c:63
static AVFrame * frame
#define F(x)
static const uint8_t frame_size[4]
Definition g723_1.h:222
#define fail
Definition test.h:479
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:103
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition error.h:52
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition log.h:236
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition rational.h:159
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
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.
Definition hwcontext.c:615
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition hwcontext.c:337
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition hwcontext.c:263
@ AV_HWDEVICE_TYPE_D3D11VA
Definition hwcontext.h:35
static av_cold void load_functions(void)
An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
const VDPAUPixFmtMap * map
#define r
Definition input.c:42
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
static pthread_mutex_t lock
Definition ffjni.c:39
static void ff_outlink_set_status(AVFilterLink *link, int status, int64_t pts)
Set the status field of a link from the source filter.
Definition filters.h:629
#define FFERROR_NOT_READY
Filters implementation helper functions and internal structures.
Definition filters.h:34
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define av_cold
Definition attributes.h:117
common internal API header
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
Memory handling functions.
void ID3D11Device
Definition nvenc.h:28
AVOptions.
int(* cond)(enum AVPixelFormat pix_fmt)
#define AV_PIX_FMT_RGBAF16
Definition pixfmt.h:630
@ AVCOL_RANGE_JPEG
Full range content.
Definition pixfmt.h:783
@ AVALPHA_MODE_STRAIGHT
Alpha channel is independent of color values.
Definition pixfmt.h:819
@ AVALPHA_MODE_PREMULTIPLIED
Alpha channel is multiplied into color values.
Definition pixfmt.h:818
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition pixfmt.h:102
@ AV_PIX_FMT_D3D11
Hardware surfaces for Direct3D11.
Definition pixfmt.h:336
@ AVCOL_PRI_BT709
also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
Definition pixfmt.h:644
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
Definition pixfmt.h:681
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
Definition pixfmt.h:686
@ AVCOL_SPC_RGB
order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
Definition pixfmt.h:707
static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
Definition qsvvpp.c:216
static AVMutex mutex
Definition resman.c:61
#define FF_ARRAY_ELEMS(a)
unsigned int pos
Definition spdifenc.c:431
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
This struct is allocated as AVHWDeviceContext.hwctx.
This struct is allocated as AVHWFramesContext.hwctx.
An instance of a filter.
Definition avfilter.h:273
int extra_hw_frames
Sets the number of extra hardware frames which the filter will allocate on its output links for use i...
Definition avfilter.h:352
void * priv
private data for use by the filter
Definition avfilter.h:288
AVBufferRef * hw_device_ctx
For filters which will create hardware frames, sets the device the filter should create them in.
Definition avfilter.h:336
AVFilterLink ** outputs
array of pointers to output links
Definition avfilter.h:285
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition hwcontext.h:63
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
Rational number (pair of numerator and denominator).
Definition rational.h:58
std::unique_ptr< GfxCaptureContextD3D > d3d
std::unique_ptr< GfxCaptureContextWgc > wgc
AVD3D11VADeviceContext * device_hwctx
std::recursive_mutex wgc_thread_uninit_mutex
AVD3D11VAFramesContext * frames_hwctx
AVHWDeviceContext * device_ctx
std::shared_ptr< void > wgc_thread_cb_data
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
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
const char * window_text
GfxCaptureContextCpp * ctx
const char * window_exe
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
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
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude * pInclude
HRESULT(WINAPI *SetThreadDescription)(HANDLE hThread
DWORD PVOID DWORD cbAttribute
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
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR pEntrypoint
DPI_AWARENESS_CONTEXT(WINAPI *SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext)
#define av_log(a,...)
static double cb(void *priv, double x, double y)
Definition vf_geq.c:247
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89
int len
uint8_t base
Definition vp3data.h:128
@ GFX_SCALE_POINT
@ GFX_RESIZE_SCALE
@ GFX_RESIZE_CROP
@ GFX_RESIZE_SCALE_ASPECT
@ GFX_MONITOR_IDX_WINDOW
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 void wgc_thread_uninit(AVFilterContext *avctx) noexcept
#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
@ WM_WGC_THREAD_SHUTDOWN
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)
#define CCTX(ctx)
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)
#define TIMESPAN_RES
static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
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(...)