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
154 HWND capture_hwnd { nullptr };
155 HMONITOR capture_hmonitor { nullptr };
156
160
164
167
168 // dup_frames: last output frame and the QPC time (100 ns units) the next repeat is due
169 AVFrame *last_frame { nullptr };
171};
172
173template <typename T>
174static HRESULT get_activation_factory(GfxCaptureContextCpp *ctx, PCWSTR clsid, T** factory) {
175 HSTRING_HEADER hsheader = { 0 };
176 HSTRING hs = NULL;
177
178 HRESULT hr = ctx->fn.WindowsCreateStringReference(clsid, (UINT32)wcslen(clsid), &hsheader, &hs);
179 if (FAILED(hr))
180 return hr;
181
182 return ctx->fn.RoGetActivationFactory(hs, IID_PPV_ARGS(factory));
183}
184
185#define CHECK_HR(fcall, action) \
186 do { \
187 HRESULT fhr = fcall; \
188 if (FAILED(fhr)) { \
189 av_log(avctx, AV_LOG_ERROR, #fcall " failed: 0x%08lX\n", fhr); \
190 action; \
191 } \
192 } while (0)
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)
196
197/****************************************************
198 * Windows Graphics Capture Worker Thread *
199 * All wgc_* functions must run only on WGC thread! *
200 ****************************************************/
201
202static void wgc_frame_arrived_handler(const std::unique_ptr<GfxCaptureContextWgc> &wgctx) {
203 {
204 std::lock_guard lock(wgctx->frame_arrived_mutex);
205 wgctx->frame_seq += 1;
206 }
207 wgctx->frame_arrived_cond.notify_one();
208}
209
210static void wgc_closed_handler(const std::unique_ptr<GfxCaptureContextWgc> &wgctx) {
211 {
212 std::lock_guard lock(wgctx->frame_arrived_mutex);
213 wgctx->window_closed = true;
214 }
215 wgctx->frame_arrived_cond.notify_one();
216}
217
218static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
219{
220 GfxCaptureContext *cctx = CCTX(avctx->priv);
222 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
223
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;
227 }
228
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;
232 }
233
234 if (wgctx->capture_session) {
235 ComPtr<IClosable> closable;
236 if (SUCCEEDED(wgctx->capture_session.As(&closable))) {
237 CHECK_HR_LOG(closable->Close());
238 } else {
239 av_log(avctx, AV_LOG_ERROR, "Failed to get capture session IClosable interface\n");
240 }
241 }
242
243 if (wgctx->frame_pool) {
244 ComPtr<IClosable> closable;
245 if (SUCCEEDED(wgctx->frame_pool.As(&closable))) {
246 CHECK_HR_LOG(closable->Close());
247 } else {
248 av_log(avctx, AV_LOG_ERROR, "Failed to get frame pool IClosable interface\n");
249 }
250 }
251
252 wgctx->capture_session.Reset();
253 wgctx->frame_pool.Reset();
254 wgctx->capture_item.Reset();
255 wgctx->d3d_device.Reset();
256}
257
259{
260 GfxCaptureContext *cctx = CCTX(avctx->priv);
262 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
263
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;
269 return 0;
270 }
271
272 RECT client_rect = {};
273 RECT frame_bounds = {};
274 RECT window_rect = {};
275
276 if (IsIconic(ctx->capture_hwnd)) {
277 av_log(avctx, AV_LOG_VERBOSE, "Capture window is iconic, no client area\n");
278 return 0;
279 }
280
281 if (!GetClientRect(ctx->capture_hwnd, &client_rect)) {
282 av_log(avctx, AV_LOG_ERROR, "GetClientRect failed\n");
283 return AVERROR_EXTERNAL;
284 }
285
286 SetLastError(0);
287 if (!MapWindowPoints(ctx->capture_hwnd, nullptr, (POINT*)&client_rect, 2) && GetLastError()) {
288 av_log(avctx, AV_LOG_ERROR, "MapWindowPoints failed\n");
289 return AVERROR_EXTERNAL;
290 }
291
292 if (FAILED(ctx->fn.DwmGetWindowAttribute(ctx->capture_hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_bounds, sizeof(window_rect))))
293 av_log(avctx, AV_LOG_DEBUG, "DwmGetWindowAttribute failed\n");
294
295 if (!GetWindowRect(ctx->capture_hwnd, &window_rect))
296 av_log(avctx, AV_LOG_DEBUG, "GetWindowRect failed\n");
297
298 if (wgctx->cap_size.Width == frame_bounds.right - frame_bounds.left ||
299 wgctx->cap_size.Height == frame_bounds.bottom - frame_bounds.top) {
300 av_log(avctx, AV_LOG_DEBUG, "Using window rect from DWMWA_EXTENDED_FRAME_BOUNDS\n");
301 } else if (wgctx->cap_size.Width == window_rect.right - window_rect.left ||
302 wgctx->cap_size.Height == window_rect.bottom - window_rect.top) {
303 av_log(avctx, AV_LOG_DEBUG, "Using window rect from GetWindowRect\n");
304 frame_bounds = window_rect;
305 } else {
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))
308 {
309 av_log(avctx, AV_LOG_ERROR, "No valid window rect found\n");
310 return AVERROR_EXTERNAL;
311 }
312 av_log(avctx, AV_LOG_VERBOSE, "Failed to get valid window rect, client area may be inaccurate\n");
313 return 0;
314 }
315
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);
320
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);
324
325 return 0;
326}
327
329{
330 GfxCaptureContext *cctx = CCTX(avctx->priv);
332 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
333 int ret;
334
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;
342
343 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
344 if (cctx->out_fmt != AV_PIX_FMT_BGRA)
345 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
346
347 CHECK_HR_RET(wgctx->capture_item->get_Size(&wgctx->cap_size));
349 if (ret < 0)
350 return ret;
351
352 CHECK_HR_RET(d3d11_device.As(&d3d10_multithread));
353 d3d10_multithread->SetMultithreadProtected(TRUE);
354
355 CHECK_HR_RET(d3d11_device.As(&dxgi_device));
356 CHECK_HR_RET(ctx->fn.CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), &wgctx->d3d_device));
357
358 CHECK_HR_RET(get_activation_factory<IDirect3D11CaptureFramePoolStatics2>(ctx, RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool, &frame_pool_statics));
359 CHECK_HR_RET(frame_pool_statics->CreateFreeThreaded(wgctx->d3d_device.Get(), fmt, CAPTURE_POOL_SIZE, wgctx->cap_size, &wgctx->frame_pool));
360 CHECK_HR_RET(wgctx->frame_pool->CreateCaptureSession(wgctx->capture_item.Get(), &wgctx->capture_session));
361
362 if (SUCCEEDED(wgctx->capture_session.As(&session2))) {
363 if (FAILED(session2->put_IsCursorCaptureEnabled(cctx->capture_cursor))) {
364 av_log(avctx, AV_LOG_WARNING, "Failed setting cursor capture mode\n");
365 }
366 } else {
367 av_log(avctx, AV_LOG_WARNING, "Cursor capture unavailable\n");
368 }
369
370 if (SUCCEEDED(wgctx->capture_session.As(&session3))) {
371 // this one is weird, it can return failure but still work
372 if (FAILED(session3->put_IsBorderRequired(cctx->display_border))) {
373 av_log(avctx, AV_LOG_WARNING, "Failed setting border drawing mode\n");
374 }
375 } else {
376 av_log(avctx, AV_LOG_WARNING, "Disabling border drawing unavailable\n");
377 }
378
379 if (SUCCEEDED(wgctx->capture_session.As(&session5))) {
380 TimeSpan ivl = { av_rescale_q(1, av_inv_q(cctx->frame_rate), AVRational{1, TIMESPAN_RES}) };
381 if (FAILED(session5->put_MinUpdateInterval(ivl))) {
382 av_log(avctx, AV_LOG_WARNING, "Failed setting minimum update interval, framerate may be limited\n");
383 }
384 } else {
385 av_log(avctx, AV_LOG_WARNING, "Setting minimum update interval unavailable, framerate may be limited\n");
386 }
387
388 wgctx->window_closed = 0;
389
390 CHECK_HR_RET(wgctx->capture_item->add_Closed(
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);
395 return S_OK;
396 }).Get(), &wgctx->closed_token));
397
398 CHECK_HR_RET(wgctx->frame_pool->add_FrameArrived(
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);
403 return S_OK;
404 }).Get(), &wgctx->frame_arrived_token));
405
406 return 0;
407}
408
410{
411 GfxCaptureContext *cctx = CCTX(avctx->priv);
413 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
414 HRESULT hr;
415 int ret;
416
417 ComPtr<IGraphicsCaptureItemInterop> capture_item_interop;
418 CHECK_HR_RET(get_activation_factory<IGraphicsCaptureItemInterop>(ctx, RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem, &capture_item_interop));
419
420 if (ctx->capture_hmonitor) {
421 hr = capture_item_interop->CreateForMonitor(ctx->capture_hmonitor, IID_PPV_ARGS(&wgctx->capture_item));
422 if (FAILED(hr)) {
423 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for monitor (0x%08lX)\n", hr);
424 return AVERROR_EXTERNAL;
425 }
426 } else if (ctx->capture_hwnd) {
427 hr = capture_item_interop->CreateForWindow(ctx->capture_hwnd, IID_PPV_ARGS(&wgctx->capture_item));
428 if (FAILED(hr)) {
429 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for window (0x%08lX)\n", hr);
430 return AVERROR_EXTERNAL;
431 }
432 }
433
435 if (ret < 0) {
436 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture pool\n");
437 return ret;
438 }
439
440 hr = ctx->wgc->capture_session->StartCapture();
441 if (FAILED(hr)) {
442 av_log(avctx, AV_LOG_ERROR, "Failed to start graphics capture session (0x%08lX)\n", hr);
443 return AVERROR_EXTERNAL;
444 }
445
446 return 0;
447}
448
449static int wgc_try_get_next_frame(AVFilterContext *avctx, ComPtr<IDirect3D11CaptureFrame> &capture_frame)
450{
451 GfxCaptureContext *cctx = CCTX(avctx->priv);
453 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
454
455 ComPtr<IDirect3DSurface> capture_surface;
456 ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
457 ComPtr<ID3D11Texture2D> frame_texture;
458 SizeInt32 frame_size = { 0, 0 };
459
460 CHECK_HR_RET(wgctx->frame_pool->TryGetNextFrame(&capture_frame));
461 if (!capture_frame)
462 return AVERROR(EAGAIN);
463
464 CHECK_HR_RET(capture_frame->get_ContentSize(&frame_size));
465 if (frame_size.Width != wgctx->cap_size.Width || frame_size.Height != wgctx->cap_size.Height) {
466 av_log(avctx, AV_LOG_VERBOSE, "Capture size changed to %dx%d\n", frame_size.Width, frame_size.Height);
467
468 DirectXPixelFormat fmt = DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
469 if (cctx->out_fmt != AV_PIX_FMT_BGRA)
470 fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
471
472 CHECK_HR_RET(wgctx->frame_pool->Recreate(wgctx->d3d_device.Get(), fmt, CAPTURE_POOL_SIZE, frame_size));
473 wgctx->cap_size = frame_size;
474
475 int ret = wgc_calculate_client_area(avctx);
476 if (ret < 0)
477 return ret;
478 }
479
480 return 0;
481}
482
484{
485 GfxCaptureContext *cctx = CCTX(avctx->priv);
487 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
488 MSG msg;
489
490 // pre-create the message-queue
491 PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE);
492
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;
497
498 CHECK_HR_RET(ctx->fn.CreateDispatcherQueueController(options, &wgctx->dispatcher_queue_controller));
499 CHECK_HR_RET(wgctx->dispatcher_queue_controller->get_DispatcherQueue(&wgctx->dispatcher_queue));
500
501 return 0;
502}
503
504static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
505{
506 GfxCaptureContext *cctx = CCTX(avctx->priv);
508
510
511 ctx->wgc.reset();
512 ctx->fn.RoUninitialize();
513}
514
516{
517 GfxCaptureContext *cctx = CCTX(avctx->priv);
519 HRESULT hr;
520 int ret;
521
522 ctx->wgc = std::make_unique<GfxCaptureContextWgc>();
523
524 ctx->fn.SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
525
526 hr = ctx->fn.RoInitialize(RO_INIT_MULTITHREADED);
527 if (FAILED(hr)) {
528 av_log(avctx, AV_LOG_ERROR, "Failed to initialize WinRT\n");
529 ctx->wgc.reset();
530 return AVERROR_EXTERNAL;
531 }
532
533 ret = wgc_setup_winrt(avctx);
534 if (ret < 0) {
535 av_log(avctx, AV_LOG_ERROR, "Failed to setup WinRT\n");
536 goto fail;
537 }
538
540 if (ret < 0) {
541 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
542 goto fail;
543 }
544
545 return 0;
546
547fail:
548 wgc_thread_uninit(avctx);
549 return ret;
550}
551
553{
554 GfxCaptureContext *cctx = CCTX(avctx->priv);
556 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
557 ComPtr<IAsyncAction> async;
558 MSG msg;
559
560 av_log(avctx, AV_LOG_DEBUG, "Starting message loop\n");
561
562 while (BOOL res = GetMessage(&msg, NULL, 0, 0)) {
563 if (res == -1) {
564 av_log(avctx, AV_LOG_ERROR, "Failed to get message\n");
565 return AVERROR(EIO);
566 }
567
568 if (!msg.hwnd && msg.message == WM_WGC_THREAD_SHUTDOWN) {
569 av_log(avctx, AV_LOG_DEBUG, "Initializing WGC thread shutdown\n");
570
572
573 if (FAILED(wgctx->dispatcher_queue_controller->ShutdownQueueAsync(&async))) {
574 av_log(avctx, AV_LOG_ERROR, "Failed to shutdown dispatcher queue\n");
575 return AVERROR_EXTERNAL;
576 }
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);
581 return S_OK;
582 }).Get());
583 continue;
584 }
585
586 av_log(avctx, AV_LOG_TRACE, "Got message: %u\n", msg.message);
587
588 TranslateMessage(&msg);
589 DispatchMessage(&msg);
590 }
591
592 if (!async) {
593 av_log(avctx, AV_LOG_ERROR, "WGC Thread message loop ended without proper shutdown\n");
594 return AVERROR_EXTERNAL;
595 }
596
597 av_log(avctx, AV_LOG_DEBUG, "Message loop ended\n");
598
599 return msg.wParam;
600}
601
602static void wgc_thread_entry(AVFilterContext *avctx) noexcept
603{
604 GfxCaptureContext *cctx = CCTX(avctx->priv);
606
607 {
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);
612
613 std::lock_guard init_lock(ctx->wgc_thread_init_mutex);
614 ctx->wgc_thread_id = GetCurrentThreadId();
615
616 try {
617 ctx->wgc_thread_init_res = wgc_thread_init(avctx);
618 } catch (const std::bad_alloc &) {
619 ctx->wgc_thread_init_res = AVERROR(ENOMEM);
620 } catch (const std::exception &e) {
621 av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread init: %s\n", e.what());
622 ctx->wgc_thread_init_res = AVERROR_BUG;
623 } catch (...) {
624 av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread init\n");
625 ctx->wgc_thread_init_res = AVERROR_BUG;
626 }
627
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;
631 return;
632 }
633 }
634
635 int ret;
636
637 try {
638 ret = wgc_thread_worker(avctx);
639 } catch (const std::bad_alloc &) {
640 ret = AVERROR(ENOMEM);
641 } catch (const std::exception &e) {
642 av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread worker: %s\n", e.what());
644 } catch (...) {
645 av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread worker\n");
647 }
648
649 std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
650 wgc_thread_uninit(avctx);
651
652 ctx->wgc_thread_res = ret;
653}
654
655/***********************************
656 * WGC Thread Management Functions *
657 ***********************************/
658
660{
661 GfxCaptureContext *cctx = CCTX(avctx->priv);
663 int ret = 0;
664
665 if (ctx->wgc_thread.joinable()) {
666 if (ctx->wgc_thread_id && !PostThreadMessage(ctx->wgc_thread_id, WM_WGC_THREAD_SHUTDOWN, 0, 0))
667 av_log(avctx, AV_LOG_ERROR, "Failed to post shutdown message to WGC thread\n");
668
669 ctx->wgc_thread.join();
670 ret = ctx->wgc_thread_res;
671
672 ctx->wgc_thread_id = 0;
673 }
674
675 return ret;
676}
677
679{
680 GfxCaptureContext *cctx = CCTX(avctx->priv);
682
683 if (ctx->wgc_thread.joinable() || ctx->wgc_thread_id) {
684 av_log(avctx, AV_LOG_ERROR, "Double-creation of WGC thread\n");
685 return AVERROR_BUG;
686 }
687
688 std::unique_lock wgc_lock(ctx->wgc_thread_init_mutex);
689 ctx->wgc_thread_init_res = INT_MAX;
690
691 try {
692 ctx->wgc_thread = std::thread(wgc_thread_entry, avctx);
693 } catch (const std::system_error &e) {
694 av_log(avctx, AV_LOG_ERROR, "Failed to create WGC thread: %s\n", e.what());
695 return AVERROR_EXTERNAL;
696 }
697
698 if (!ctx->wgc_thread_init_cond.wait_for(wgc_lock, std::chrono::seconds(1), [&]() {
699 return ctx->wgc_thread_init_res != INT_MAX;
700 })) {
701 av_log(avctx, AV_LOG_ERROR, "WGC thread init timed out\n");
702 return AVERROR(ETIMEDOUT);
703 }
704
705 return ctx->wgc_thread_init_res;
706}
707
708template <typename F>
710{
711 GfxCaptureContext *cctx = CCTX(avctx->priv);
713 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
714
715 std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
716 if (!wgctx) {
717 av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
718 return AVERROR(ENOSYS);
719 }
720
721 struct CBData {
722 std::mutex mutex;
723 std::condition_variable cond;
724 bool done;
725 bool cancel;
726 int ret;
727 };
728 auto cbdata = std::make_shared<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 av_frame_free(&ctx->last_frame);
982 ctx->d3d.reset();
983
984 av_buffer_unref(&ctx->frames_ref);
985 av_buffer_unref(&ctx->device_ref);
986
987 delete ctx;
988 cctx->ctx = nullptr;
989}
990
991template<typename T>
992static av_cold void GetProcAddressTyped(const hmodule_ptr_t &hModule, LPCSTR lpProcName, T *out) {
993 *out = reinterpret_cast<T>(GetProcAddress(hModule.get(), lpProcName));
994}
995
997{
998 GfxCaptureContext *cctx = CCTX(avctx->priv);
1000
1001#define LOAD_DLL(handle, name) \
1002 handle = hmodule_ptr_t(LoadLibraryExW(L##name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); \
1003 if (!handle) { \
1004 av_log(avctx, AV_LOG_ERROR, "Failed opening " #name "\n"); \
1005 return AVERROR(ENOSYS); \
1006 }
1007
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); \
1013 }
1014
1015 // this handle is not used anywhere, but letting it get auto-freed during RoUninit causes crashes
1016 LOAD_DLL(ctx->fn.graphicscapture_handle, "graphicscapture.dll");
1017
1018 LOAD_DLL(ctx->fn.combase_handle, "combase.dll");
1019 LOAD_DLL(ctx->fn.dwmapi_handle, "dwmapi.dll");
1020 LOAD_DLL(ctx->fn.d3d11_handle, "d3d11.dll");
1021 LOAD_DLL(ctx->fn.coremsg_handle, "coremessaging.dll");
1022 LOAD_DLL(ctx->fn.user32_handle, "user32.dll");
1023 LOAD_DLL(ctx->fn.kernel32_handle, "kernel32.dll");
1024 LOAD_DLL(ctx->fn.d3dcompiler_handle, "d3dcompiler_47.dll");
1025
1026 LOAD_FUNC(ctx->fn.combase_handle, RoInitialize);
1027 LOAD_FUNC(ctx->fn.combase_handle, RoUninitialize);
1028 LOAD_FUNC(ctx->fn.combase_handle, RoGetActivationFactory);
1029 LOAD_FUNC(ctx->fn.combase_handle, WindowsCreateStringReference);
1030
1031 LOAD_FUNC(ctx->fn.dwmapi_handle, DwmGetWindowAttribute);
1032
1033 LOAD_FUNC(ctx->fn.d3d11_handle, CreateDirect3D11DeviceFromDXGIDevice);
1034
1035 LOAD_FUNC(ctx->fn.coremsg_handle, CreateDispatcherQueueController);
1036
1037 LOAD_FUNC(ctx->fn.user32_handle, SetThreadDpiAwarenessContext);
1038
1039 LOAD_FUNC(ctx->fn.kernel32_handle, SetThreadDescription);
1040
1041 LOAD_FUNC(ctx->fn.d3dcompiler_handle, D3DCompile);
1042
1043#undef LOAD_FUNC
1044#undef LOAD_DLL
1045 return 0;
1046}
1047
1049{
1050 GfxCaptureContext *cctx = CCTX(avctx->priv);
1051 int ret = 0;
1052
1054 ctx->d3d = std::make_unique<GfxCaptureContextD3D>();
1055
1056 ret = load_functions(avctx);
1057 if (ret < 0) {
1058 ctx->fn.RoUninitialize = nullptr;
1059 goto fail;
1060 }
1061
1062 return 0;
1063
1064fail:
1065 gfxcapture_uninit(avctx);
1066 return ret;
1067}
1068
1070{
1071 GfxCaptureContext *cctx = CCTX(avctx->priv);
1072 GfxCaptureContextCpp *ctx = cctx->ctx;
1073 int ret = 0;
1074
1075 ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
1076 if (!ctx->frames_ref)
1077 return AVERROR(ENOMEM);
1078 ctx->frames_ctx = (AVHWFramesContext*)ctx->frames_ref->data;
1079 ctx->frames_hwctx = (AVD3D11VAFramesContext*)ctx->frames_ctx->hwctx;
1080
1081 ctx->frames_ctx->format = AV_PIX_FMT_D3D11;
1082 ctx->frames_ctx->width = cctx->canvas_width;
1083 ctx->frames_ctx->height = cctx->canvas_height;
1084 ctx->frames_ctx->sw_format = (AVPixelFormat)cctx->out_fmt;
1085 if (avctx->extra_hw_frames > 0)
1086 ctx->frames_ctx->initial_pool_size = 8 + avctx->extra_hw_frames;
1087
1088 ctx->frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
1089
1090 ret = av_hwframe_ctx_init(ctx->frames_ref);
1091 if (ret < 0) {
1092 av_log(avctx, AV_LOG_ERROR, "Failed to initialise hardware frames context: %d.\n", ret);
1093 goto fail;
1094 }
1095
1096 return 0;
1097fail:
1098 av_buffer_unref(&ctx->frames_ref);
1099 return ret;
1100}
1101
1103{
1104 GfxCaptureContext *cctx = CCTX(avctx->priv);
1105 GfxCaptureContextCpp *ctx = cctx->ctx;
1106 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1107 int ret = 0;
1108
1109 stop_wgc_thread(avctx);
1110
1111 ret = find_capture_source(avctx);
1112 if (ret < 0) {
1113 av_log(avctx, AV_LOG_ERROR, "Failed to find capture source\n");
1114 return ret;
1115 }
1116
1117 ret = start_wgc_thread(avctx);
1118 if (ret < 0) {
1119 av_log(avctx, AV_LOG_ERROR, "Failed to start WGC thread\n");
1120 return ret;
1121 }
1122
1123 int cap_w = wgctx->cap_size.Width - cctx->crop_left - cctx->crop_right;
1124 int cap_h = wgctx->cap_size.Height - cctx->crop_top - cctx->crop_bottom;
1125
1126 if (!cctx->capture_border) {
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;
1129 }
1130
1131 if (cctx->canvas_width == 0)
1132 cctx->canvas_width = cap_w;
1133 else if (cctx->canvas_width < 0)
1134 cctx->canvas_width = (cap_w / cctx->canvas_width) * cctx->canvas_width;
1135
1136 if (cctx->canvas_height == 0)
1137 cctx->canvas_height = cap_h;
1138 else if (cctx->canvas_height < 0)
1139 cctx->canvas_height = (cap_h / cctx->canvas_height) * cctx->canvas_height;
1140
1141 return 0;
1142}
1143
1145{
1146 GfxCaptureContext *cctx = CCTX(avctx->priv);
1147 GfxCaptureContextCpp *ctx = cctx->ctx;
1148 std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
1149 HRESULT hr;
1150
1151 ComPtr<ID3DBlob> vs_blob, ps_blob, err_blob;
1152 CD3D11_SAMPLER_DESC sampler_desc(CD3D11_DEFAULT{});
1153 UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
1154
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);
1156 if (FAILED(hr)) {
1157 if (err_blob) {
1158 av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: %.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
1159 } else {
1160 av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: 0x%08lX\n", hr);
1161 }
1162 return AVERROR_EXTERNAL;
1163 }
1164
1165 const char *ps_entry = "main_ps_bicubic";
1166 if (cctx->resize_mode == GFX_RESIZE_CROP || cctx->scale_mode == GFX_SCALE_POINT) {
1167 ps_entry = "main_ps";
1168 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
1169 }
1170
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);
1172 if (FAILED(hr)) {
1173 if (err_blob) {
1174 av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: %.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
1175 } else {
1176 av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: 0x%08lX\n", hr);
1177 }
1178 return AVERROR_EXTERNAL;
1179 }
1180
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));
1183
1184 CHECK_HR_RET(ctx->device_hwctx->device->CreateSamplerState(&sampler_desc, &d3dctx->sampler_state));
1185
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;
1191
1192 CHECK_HR_RET(ctx->device_hwctx->device->CreateBuffer(&cb_desc, NULL, &d3dctx->shader_cb));
1193
1194 CHECK_HR_RET(ctx->device_hwctx->device->CreateDeferredContext(0, &d3dctx->deferred_ctx));
1195
1196 return 0;
1197}
1198
1200{
1201 AVFilterContext *avctx = outlink->src;
1202 GfxCaptureContext *cctx = CCTX(avctx->priv);
1203 GfxCaptureContextCpp *ctx = cctx->ctx;
1204
1205 FilterLink *link = ff_filter_link(outlink);
1206 int ret;
1207
1208 if (avctx->hw_device_ctx) {
1209 ctx->device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
1210
1211 if (ctx->device_ctx->type != AV_HWDEVICE_TYPE_D3D11VA) {
1212 av_log(avctx, AV_LOG_ERROR, "Non-D3D11VA input hw_device_ctx\n");
1213 return AVERROR(EINVAL);
1214 }
1215
1216 ctx->device_ref = av_buffer_ref(avctx->hw_device_ctx);
1217 if (!ctx->device_ref)
1218 return AVERROR(ENOMEM);
1219
1220 av_log(avctx, AV_LOG_VERBOSE, "Using provided hw_device_ctx\n");
1221 } else {
1223 if (ret < 0) {
1224 av_log(avctx, AV_LOG_ERROR, "Failed to create D3D11VA device.\n");
1225 return ret;
1226 }
1227
1228 ctx->device_ctx = (AVHWDeviceContext*)ctx->device_ref->data;
1229
1230 av_log(avctx, AV_LOG_VERBOSE, "Created internal hw_device_ctx\n");
1231 }
1232
1233 ctx->device_hwctx = (AVD3D11VADeviceContext*)ctx->device_ctx->hwctx;
1234
1236 if (ret < 0) {
1237 av_log(avctx, AV_LOG_ERROR, "Failed to prepare render resources\n");
1238 return ret;
1239 }
1240
1242 if (ret < 0) {
1243 av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
1244 return ret;
1245 }
1246
1247 ret = init_hwframes_ctx(avctx);
1248 if (ret < 0)
1249 return ret;
1250
1251 link->hw_frames_ctx = av_buffer_ref(ctx->frames_ref);
1252 if (!link->hw_frames_ctx)
1253 return AVERROR(ENOMEM);
1254
1255 std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
1256 if (!ctx->wgc) {
1257 av_log(avctx, AV_LOG_ERROR, "WGC thread died prematurely\n");
1258 return AVERROR(ENOSYS);
1259 }
1260
1261 outlink->w = ctx->frames_ctx->width;
1262 outlink->h = ctx->frames_ctx->height;
1263 outlink->time_base = AVRational{1, TIMESPAN_RES};
1265 link->frame_rate = cctx->frame_rate;
1266
1267 av_log(avctx, AV_LOG_DEBUG, "Capture setup with res %dx%d\n", outlink->w, outlink->h);
1268
1269 return 0;
1270}
1271
1272static HRESULT create_frame_rtv(ID3D11Device *dev, const AVFrame *frame,
1273 const D3D11_TEXTURE2D_DESC &dst_tex_desc,
1274 ID3D11RenderTargetView **rtv)
1275{
1276 D3D11_RENDER_TARGET_VIEW_DESC target_desc = {};
1277 target_desc.Format = dst_tex_desc.Format;
1278
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;
1284 } else {
1285 target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
1286 target_desc.Texture2D.MipSlice = 0;
1287 }
1288
1289 return dev->CreateRenderTargetView(
1290 reinterpret_cast<ID3D11Resource*>(frame->data[0]), &target_desc, rtv);
1291}
1292
1294{
1295 GfxCaptureContextCpp *ctx = CCTX(avctx->priv)->ctx;
1296 ID3D11DeviceContext *dev_ctx = ctx->device_hwctx->device_context;
1297
1298 D3D11_TEXTURE2D_DESC dst_tex_desc;
1299 reinterpret_cast<ID3D11Texture2D*>(frame->data[0])->GetDesc(&dst_tex_desc);
1300
1301 ComPtr<ID3D11RenderTargetView> rtv;
1302 CHECK_HR_RET(create_frame_rtv(ctx->device_hwctx->device, frame, dst_tex_desc, &rtv));
1303
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);
1308
1309 return 0;
1310}
1311
1312static int render_capture_to_frame(AVFilterContext *avctx, AVFrame *frame, const ComPtr<ID3D11Texture2D> &src_tex)
1313{
1314 GfxCaptureContext *cctx = CCTX(avctx->priv);
1315 GfxCaptureContextCpp *ctx = cctx->ctx;
1316 std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
1317 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1318
1319 ID3D11Device *dev = ctx->device_hwctx->device;
1320 ID3D11DeviceContext *dev_ctx = ctx->device_hwctx->device_context;
1321 ComPtr<ID3D11DeviceContext> &def_ctx = d3dctx->deferred_ctx;
1322
1323 D3D11_TEXTURE2D_DESC dst_tex_desc;
1324 reinterpret_cast<ID3D11Texture2D*>(frame->data[0])->GetDesc(&dst_tex_desc);
1325
1326 D3D11_TEXTURE2D_DESC src_tex_desc;
1327 src_tex->GetDesc(&src_tex_desc);
1328
1329 ComPtr<ID3D11RenderTargetView> rtv;
1330 CHECK_HR_RET(create_frame_rtv(dev, frame, dst_tex_desc, &rtv));
1331
1332 ComPtr<ID3D11ShaderResourceView> srv;
1333 CHECK_HR_RET(dev->CreateShaderResourceView(src_tex.Get(), nullptr, &srv));
1334
1335 int crop_left = cctx->crop_left;
1336 int crop_top = cctx->crop_top;
1337 int crop_right = cctx->crop_right;
1338 int crop_bottom = cctx->crop_bottom;
1339
1340 if (!cctx->capture_border) {
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;
1345 }
1346
1347 // Using the actual capture frame size here adjusts for jank that can happen during rapid
1348 // resizing of the source window. The capture frame pool is only recreated once a frame
1349 // of changed size came out of it, so we need to cut/pad such frames to fit.
1350 // Just discarding such frames can lead to visible stutter if the source window is being
1351 // resized continuously, so this code does its best to adjust them instead. With the risk
1352 // of slight clamping artifacts when enlarging rapidly.
1353 int cropped_w = wgctx->cap_size.Width - crop_left - crop_right;
1354 int cropped_h = wgctx->cap_size.Height - crop_top - crop_bottom;
1355
1356 D3D11_VIEWPORT viewport = { 0 };
1357 viewport.MinDepth = 0.f;
1358 viewport.MaxDepth = 1.f;
1359
1360 switch (cctx->resize_mode) {
1361 case GFX_RESIZE_CROP:
1362 viewport.Width = (float)cropped_w;
1363 viewport.Height = (float)cropped_h;
1364 break;
1365 case GFX_RESIZE_SCALE:
1366 viewport.Width = dst_tex_desc.Width;
1367 viewport.Height = dst_tex_desc.Height;
1368 break;
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;
1374 break;
1375 }
1376 default:
1377 av_log(avctx, AV_LOG_ERROR, "Invalid scaling mode\n");
1378 return AVERROR_BUG;
1379 };
1380
1381 def_ctx->RSSetViewports(1, &viewport);
1382
1383 D3D11_MAPPED_SUBRESOURCE map;
1384 CHECK_HR_RET(def_ctx->Map(d3dctx->shader_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map));
1385 {
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; // min_u
1393 cb_f[5] = crop_top / (float)src_tex_desc.Height; // min_v
1394 cb_f[6] = (crop_left + cropped_w) / (float)src_tex_desc.Width; // max_u
1395 cb_f[7] = (crop_top + cropped_h) / (float)src_tex_desc.Height; // max_v
1396 cb_u[8] = !cctx->premult_alpha; // to_unpremult
1397 cb_u[9] = src_tex_desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT &&
1398 dst_tex_desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT; // to_srgb
1399 }
1400 def_ctx->Unmap(d3dctx->shader_cb.Get(), 0);
1401
1402 def_ctx->OMSetRenderTargets(1, rtv.GetAddressOf(), nullptr);
1403
1404 const float clear_color[4] = {0.f, 0.f, 0.f, 1.f};
1405 def_ctx->ClearRenderTargetView(rtv.Get(), clear_color);
1406
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());
1414
1415 def_ctx->Draw(3, 0);
1416
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);
1422
1423 return 0;
1424}
1425
1427{
1428 frame->sample_aspect_ratio = AVRational{1, 1};
1429
1430 if (ctx->frames_ctx->sw_format == AV_PIX_FMT_RGBAF16) {
1431 // According to MSDN, all floating point formats contain sRGB image data with linear 1.0 gamma.
1432 frame->color_range = AVCOL_RANGE_JPEG;
1433 frame->color_primaries = AVCOL_PRI_BT709;
1434 frame->color_trc = AVCOL_TRC_LINEAR;
1435 frame->colorspace = AVCOL_SPC_RGB;
1436 } else {
1437 // According to MSDN, all integer formats contain sRGB image data
1438 frame->color_range = AVCOL_RANGE_JPEG;
1439 frame->color_primaries = AVCOL_PRI_BT709;
1440 frame->color_trc = AVCOL_TRC_IEC61966_2_1;
1441 frame->colorspace = AVCOL_SPC_RGB;
1442 }
1443}
1444
1445// Current time in the clock and unit of WGC's SystemRelativeTime (QPC, 100 ns)
1447{
1448 static const int64_t freq = []() {
1449 LARGE_INTEGER f;
1450 QueryPerformanceFrequency(&f);
1451 return f.QuadPart;
1452 }();
1453
1454 LARGE_INTEGER now;
1455 QueryPerformanceCounter(&now);
1456
1457 if (freq == TIMESPAN_RES64)
1458 return now.QuadPart;
1459 else
1460 return av_rescale(now.QuadPart, TIMESPAN_RES64, freq);
1461}
1462
1464{
1465 return av_rescale(TIMESPAN_RES64, cctx->frame_rate.den, cctx->frame_rate.num);
1466}
1467
1468// frame->pts is in QPC 100 ns units, as SystemRelativeTime
1470{
1471 GfxCaptureContextCpp *ctx = CCTX(outlink->src->priv)->ctx;
1472
1473 // a captured frame can carry a time slightly before an already sent repeat
1474 if (ctx->last_pts != AV_NOPTS_VALUE && frame->pts <= ctx->last_pts)
1475 frame->pts = ctx->last_pts + 1;
1476
1477 ctx->last_pts = frame->pts;
1478
1479 if (ctx->first_pts == AV_NOPTS_VALUE)
1480 ctx->first_pts = frame->pts;
1481 frame->pts -= ctx->first_pts;
1482
1483 return ff_filter_frame(outlink, frame);
1484}
1485
1487{
1488 AVFilterContext *avctx = outlink->src;
1489 GfxCaptureContext *cctx = CCTX(avctx->priv);
1490 GfxCaptureContextCpp *ctx = cctx->ctx;
1491 int ret;
1492
1493 AVFrame *frame = nullptr;
1494
1495 ret = run_on_wgc_thread(avctx, [&]() {
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 };
1501
1502 int res = wgc_try_get_next_frame(avctx, capture_frame);
1503 if (res < 0)
1504 return res;
1505
1506 CHECK_HR_RET(capture_frame->get_SystemRelativeTime(&frame_time));
1507
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)));
1511
1512 if (!frame_texture)
1513 return AVERROR(EAGAIN);
1514
1515 frame = ff_get_video_buffer(outlink, cctx->canvas_width, cctx->canvas_height);
1516 if (!frame)
1517 return AVERROR(ENOMEM);
1518
1519 frame->pts = frame_time.Duration;
1520
1521 return render_capture_to_frame(avctx, frame, frame_texture);
1522 });
1523 if (ret < 0)
1524 return ret;
1525
1527
1528 if (cctx->dup_frames) {
1529 // Half an interval of slack, so a source running at max_framerate does not get
1530 // repeats interleaved on timer jitter.
1531 ctx->next_pts = qpc_now() + dup_interval(cctx) * 3 / 2;
1532
1533 if (!ctx->last_frame && !(ctx->last_frame = av_frame_alloc())) {
1535 return AVERROR(ENOMEM);
1536 }
1537 ret = av_frame_replace(ctx->last_frame, frame);
1538 if (ret < 0) {
1540 return ret;
1541 }
1542 }
1543
1544 return output_frame(outlink, frame);
1545}
1546
1547// dup_frames: no new frame arrived in time, repeat the last one, or send black before the first.
1548static int output_dup_frame(AVFilterLink *outlink)
1549{
1550 AVFilterContext *avctx = outlink->src;
1551 GfxCaptureContext *cctx = CCTX(avctx->priv);
1552 GfxCaptureContextCpp *ctx = cctx->ctx;
1553 AVFrame *frame;
1554 int ret;
1555
1556 if (!ctx->last_frame) {
1557 ctx->last_frame = ff_get_video_buffer(outlink, cctx->canvas_width, cctx->canvas_height);
1558 if (!ctx->last_frame)
1559 return AVERROR(ENOMEM);
1560 ret = clear_frame(avctx, ctx->last_frame);
1561 if (ret < 0) {
1562 av_frame_free(&ctx->last_frame);
1563 return ret;
1564 }
1565 set_frame_props(ctx, ctx->last_frame);
1566 }
1567
1568 frame = av_frame_clone(ctx->last_frame);
1569 if (!frame)
1570 return AVERROR(ENOMEM);
1571
1572 // Stamp with the deadline and advance from it, so timer overshoot shows up
1573 // neither in the timestamps nor in the rate. Restart from now after a stall.
1574 int64_t now = qpc_now();
1575 frame->pts = ctx->next_pts;
1576 ctx->next_pts += dup_interval(cctx);
1577 if (ctx->next_pts <= now)
1578 ctx->next_pts = now + dup_interval(cctx);
1579
1580 return output_frame(outlink, frame);
1581}
1582
1584{
1585 AVFilterLink *outlink = avctx->outputs[0];
1586 GfxCaptureContext *cctx = CCTX(avctx->priv);
1587 GfxCaptureContextCpp *ctx = cctx->ctx;
1588 std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
1589
1590 std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
1591 if (!wgctx) {
1592 av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
1593 return AVERROR(ENOSYS);
1594 }
1595
1596 if (!ff_outlink_frame_wanted(outlink))
1597 return FFERROR_NOT_READY;
1598
1599 for (;;) {
1600 uint64_t last_seq = wgctx->frame_seq;
1601
1602 int ret = process_frame_if_exists(outlink);
1603 if (ret != AVERROR(EAGAIN))
1604 return ret;
1605
1606 std::unique_lock frame_lock(wgctx->frame_arrived_mutex);
1607
1608 if (wgctx->window_closed && wgctx->frame_seq == last_seq) {
1610 ctx->last_pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE
1611 : ctx->last_pts - ctx->first_pts + 1);
1612 break;
1613 }
1614
1615 int64_t timeout = TIMESPAN_RES64;
1616 if (cctx->dup_frames) {
1617 if (ctx->next_pts == AV_NOPTS_VALUE)
1618 ctx->next_pts = qpc_now() + dup_interval(cctx);
1619 timeout = av_clip64(ctx->next_pts - qpc_now(), 0, TIMESPAN_RES64);
1620 }
1621
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;
1624 }))
1625 continue;
1626
1627 if (cctx->dup_frames && qpc_now() >= ctx->next_pts) {
1628 frame_lock.unlock();
1629 return output_dup_frame(outlink);
1630 }
1631
1632 break;
1633 }
1634
1635 return 0;
1636}
1637
1639{
1640 try {
1641 gfxcapture_uninit(avctx);
1642 } catch (const std::exception &e) {
1643 av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit: %s\n", e.what());
1644 } catch (...) {
1645 av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit\n");
1646 }
1647}
1648
1650{
1651 try {
1652 return gfxcapture_init(avctx);
1653 } catch (const std::bad_alloc&) {
1654 return AVERROR(ENOMEM);
1655 } catch (const std::exception &e) {
1656 av_log(avctx, AV_LOG_ERROR, "unhandled exception during init: %s\n", e.what());
1657 return AVERROR_BUG;
1658 } catch (...) {
1659 av_log(avctx, AV_LOG_ERROR, "unhandled exception during init\n");
1660 return AVERROR_BUG;
1661 }
1662}
1663
1665{
1666 try {
1667 return gfxcapture_activate(avctx);
1668 } catch (const std::bad_alloc&) {
1669 return AVERROR(ENOMEM);
1670 } catch (const std::exception &e) {
1671 av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate: %s\n", e.what());
1672 return AVERROR_BUG;
1673 } catch (...) {
1674 av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate\n");
1675 return AVERROR_BUG;
1676 }
1677}
1678
1680{
1681 AVFilterContext *avctx = outlink->src;
1682
1683 try {
1684 return gfxcapture_config_props(outlink);
1685 } catch (const std::bad_alloc&) {
1686 return AVERROR(ENOMEM);
1687 } catch (const std::exception &e) {
1688 av_log(avctx, AV_LOG_ERROR, "unhandled exception during config_props: %s\n", e.what());
1689 return AVERROR_BUG;
1690 } catch (...) {
1691 av_log(avctx, AV_LOG_ERROR, "unhandled exception during config_props\n");
1692 return AVERROR_BUG;
1693 }
1694}
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:1079
int ff_outlink_frame_wanted(AVFilterLink *link)
Test if a frame is wanted on an output link.
Definition avfilter.c:1715
Main libavfilter public API header.
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define f(width, name)
Definition cbs_vp8.c:236
#define av_clip64
Definition common.h:103
#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)
int ret
Definition ffplay.c:391
SDL_atomic_t done
Definition ffplay.c:392
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:140
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:104
#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
int av_frame_replace(AVFrame *dst, const AVFrame *src)
Ensure the destination frame refers to the same data described by the source frame,...
Definition frame.c:379
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:65
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:53
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
Definition frame.c:486
#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(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.
Definition avutil.h:247
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.
Definition hwcontext.c:673
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition hwcontext.c:347
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition hwcontext.c:267
@ 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 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:655
#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:635
@ AVCOL_RANGE_JPEG
Full range content.
Definition pixfmt.h:788
@ AVALPHA_MODE_STRAIGHT
Alpha channel is independent of color values.
Definition pixfmt.h:824
@ AVALPHA_MODE_PREMULTIPLIED
Alpha channel is multiplied into color values.
Definition pixfmt.h:823
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:649
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
Definition pixfmt.h:686
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
Definition pixfmt.h:691
@ AVCOL_SPC_RGB
order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
Definition pixfmt.h:712
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:279
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:358
void * priv
private data for use by the filter
Definition avfilter.h:294
AVBufferRef * hw_device_ctx
For filters which will create hardware frames, sets the device the filter should create them in.
Definition avfilter.h:342
AVFilterLink ** outputs
array of pointers to output links
Definition avfilter.h:291
This structure describes decoded (raw) audio or video data.
Definition frame.h:479
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
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
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
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:120
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
#define TIMESPAN_RES64
static int output_frame(AVFilterLink *outlink, AVFrame *frame)
static int64_t dup_interval(GfxCaptureContext *cctx)
static int64_t qpc_now()
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)
@ 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 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)
#define TIMESPAN_RES
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(...)