FFmpeg
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 
20 #include "vsrc_gfxcapture_shader.h"
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 
34 extern "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 
61 using namespace ABI::Windows::System;
62 using namespace ABI::Windows::Foundation;
63 using namespace ABI::Windows::Graphics::Capture;
64 using namespace ABI::Windows::Graphics::DirectX::Direct3D11;
66 using Microsoft::WRL::ComPtr;
67 using ABI::Windows::Graphics::SizeInt32;
68 using ABI::Windows::Foundation::TimeSpan;
69 using 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 
76 enum {
78 };
79 
80 #define CCTX(ctx) static_cast<GfxCaptureContext*>(ctx)
81 
82 typedef 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 
158  AVBufferRef *device_ref { nullptr };
159  AVHWDeviceContext *device_ctx { nullptr };
160  AVD3D11VADeviceContext *device_hwctx { nullptr };
161 
162  AVBufferRef *frames_ref { nullptr };
163  AVHWFramesContext *frames_ctx { nullptr };
164  AVD3D11VAFramesContext *frames_hwctx { nullptr };
165 
166  int64_t first_pts { 0 };
168 };
169 
170 template <typename T>
171 static 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 
199 static 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 
207 static 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 
215 static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
216 {
217  GfxCaptureContext *cctx = CCTX(avctx->priv);
218  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
258  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
328  GfxCaptureContextCpp *ctx = cctx->ctx;
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));
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);
409  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
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 
446 static int wgc_try_get_next_frame(AVFilterContext *avctx, ComPtr<IDirect3D11CaptureFrame> &capture_frame)
447 {
448  GfxCaptureContext *cctx = CCTX(avctx->priv);
449  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
483  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
501 static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
502 {
503  GfxCaptureContext *cctx = CCTX(avctx->priv);
504  GfxCaptureContextCpp *ctx = cctx->ctx;
505 
507 
508  ctx->wgc.reset();
509  ctx->fn.RoUninitialize();
510 }
511 
513 {
514  GfxCaptureContext *cctx = CCTX(avctx->priv);
515  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
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 
544 fail:
545  wgc_thread_uninit(avctx);
546  return ret;
547 }
548 
550 {
551  GfxCaptureContext *cctx = CCTX(avctx->priv);
552  GfxCaptureContextCpp *ctx = cctx->ctx;
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  }
574  async->put_Completed(create_cb_handler<IAsyncActionCompletedHandler, IAsyncAction*, AsyncStatus>(
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 
599 static void wgc_thread_entry(AVFilterContext *avctx) noexcept
600 {
601  GfxCaptureContext *cctx = CCTX(avctx->priv);
602  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
659  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
678  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
705 template <typename F>
706 static int run_on_wgc_thread(AVFilterContext *avctx, F &&cb)
707 {
708  GfxCaptureContext *cctx = CCTX(avctx->priv);
709  GfxCaptureContextCpp *ctx = cctx->ctx;
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 {
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(
735  create_cb_handler<IDispatcherQueueHandler>(
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 
775 static 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 
802 static 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 
817 static 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);
860  GfxCaptureContextCpp *ctx = cctx->ctx;
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);
974  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
990 template<typename T>
991 static 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);
998  GfxCaptureContextCpp *ctx = cctx->ctx;
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 
1063 fail:
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;
1096 fail:
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 
1271 static 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;
1342  case GFX_RESIZE_SCALE_ASPECT: {
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 }
flags
const SwsFlags flags[]
Definition: swscale.c:85
ff_get_video_buffer
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
vsrc_gfxcapture_shader.h
GfxCaptureContextD3D::vertex_shader
ComPtr< ID3D11VertexShader > vertex_shader
Definition: vsrc_gfxcapture_winrt.cpp:134
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
GfxCaptureContext::resize_mode
int resize_mode
Definition: vsrc_gfxcapture.h:67
GfxCaptureContext::crop_right
int crop_right
Definition: vsrc_gfxcapture.h:65
GfxCaptureFunctions::iid
REFIID iid
Definition: vsrc_gfxcapture_winrt.cpp:88
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
ff_gfxcapture_config_props
int ff_gfxcapture_config_props(AVFilterLink *outlink) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:1542
r
const char * r
Definition: vf_curves.c:127
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
GfxCaptureFunctions::pvAttribute
DWORD PVOID pvAttribute
Definition: vsrc_gfxcapture_winrt.cpp:92
AVALPHA_MODE_STRAIGHT
@ AVALPHA_MODE_STRAIGHT
Alpha channel is independent of color values.
Definition: pixfmt.h:813
AVALPHA_MODE_PREMULTIPLIED
@ AVALPHA_MODE_PREMULTIPLIED
Alpha channel is multiplied into color values.
Definition: pixfmt.h:812
ff_gfxcapture_uninit
av_cold void ff_gfxcapture_uninit(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:1501
CHECK_HR_RET
#define CHECK_HR_RET(...)
Definition: vsrc_gfxcapture_winrt.cpp:190
out
static FILE * out
Definition: movenc.c:55
create_cb_handler
static Microsoft::WRL::ComPtr< Iface > create_cb_handler(F &&cb_func)
Definition: vsrc_gfxcapture_winrt.hpp:160
cb
static double cb(void *priv, double x, double y)
Definition: vf_geq.c:247
stop_wgc_thread
static int stop_wgc_thread(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:656
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1068
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
AVBufferRef::data
uint8_t * data
The data buffer.
Definition: buffer.h:90
FFERROR_NOT_READY
return FFERROR_NOT_READY
Definition: filter_design.txt:204
AVCOL_TRC_LINEAR
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
Definition: pixfmt.h:675
av_cold
#define av_cold
Definition: attributes.h:119
int64_t
long long int64_t
Definition: coverity.c:34
vsrc_gfxcapture_winrt.hpp
GfxCaptureFunctions::graphicsDevice
IInspectable ** graphicsDevice
Definition: vsrc_gfxcapture_winrt.cpp:95
av_hwframe_ctx_init
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition: hwcontext.c:337
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:466
pixdesc.h
AVCOL_RANGE_JPEG
@ AVCOL_RANGE_JPEG
Full range content.
Definition: pixfmt.h:777
av_hwframe_ctx_alloc
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition: hwcontext.c:263
filters.h
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:226
F
#define F(x)
AVCOL_SPC_RGB
@ AVCOL_SPC_RGB
order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
Definition: pixfmt.h:701
base
uint8_t base
Definition: vp3data.h:128
AVFilterContext::hw_device_ctx
AVBufferRef * hw_device_ctx
For filters which will create hardware frames, sets the device the filter should create them in.
Definition: avfilter.h:336
render_capture_to_frame
static int render_capture_to_frame(AVFilterContext *avctx, AVFrame *frame, const ComPtr< ID3D11Texture2D > &src_tex)
Definition: vsrc_gfxcapture_winrt.cpp:1271
AV_PIX_FMT_BGRA
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition: pixfmt.h:102
System
This document is work in progress *What is CVSS *The Common Vulnerability Scoring System(CVSS) is an open
GfxCaptureContext::capture_cursor
int capture_cursor
Definition: vsrc_gfxcapture.h:60
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
av_buffer_ref
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition: buffer.c:103
run_on_wgc_thread
static int run_on_wgc_thread(AVFilterContext *avctx, F &&cb)
Definition: vsrc_gfxcapture_winrt.cpp:706
GfxCaptureContextWgc::capture_session
ComPtr< IGraphicsCaptureSession > capture_session
Definition: vsrc_gfxcapture_winrt.cpp:119
video.h
GFX_RESIZE_CROP
@ GFX_RESIZE_CROP
Definition: vsrc_gfxcapture.h:29
wgc_thread_worker
static int wgc_thread_worker(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:549
Windows::Graphics::DirectX::Direct3D11
Definition: vsrc_gfxcapture_winrt.hpp:48
GfxCaptureContextWgc::dispatcher_queue_controller
ComPtr< IDispatcherQueueController > dispatcher_queue_controller
Definition: vsrc_gfxcapture_winrt.cpp:113
GfxCaptureContext::premult_alpha
int premult_alpha
Definition: vsrc_gfxcapture.h:69
GfxCaptureContext::monitor_idx
int monitor_idx
Definition: vsrc_gfxcapture.h:55
GfxCaptureContextD3D::pixel_shader
ComPtr< ID3D11PixelShader > pixel_shader
Definition: vsrc_gfxcapture_winrt.cpp:135
CHECK_HR_LOG
#define CHECK_HR_LOG(...)
Definition: vsrc_gfxcapture_winrt.cpp:192
GfxCaptureContext::frame_rate
AVRational frame_rate
Definition: vsrc_gfxcapture.h:63
AVCOL_TRC_IEC61966_2_1
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
Definition: pixfmt.h:680
AVFilterContext::priv
void * priv
private data for use by the filter
Definition: avfilter.h:288
GfxCaptureContextD3D::shader_cb
ComPtr< ID3D11Buffer > shader_cb
Definition: vsrc_gfxcapture_winrt.cpp:137
GfxCaptureContextCpp::fn
GfxCaptureFunctions fn
Definition: vsrc_gfxcapture_winrt.cpp:142
wgc_setup_winrt
static int wgc_setup_winrt(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:480
AV_HWDEVICE_TYPE_D3D11VA
@ AV_HWDEVICE_TYPE_D3D11VA
Definition: hwcontext.h:35
AVFilterContext::extra_hw_frames
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
gfxcapture_config_props
static int gfxcapture_config_props(AVFilterLink *outlink)
Definition: vsrc_gfxcapture_winrt.cpp:1198
setup_gfxcapture_capture
static int setup_gfxcapture_capture(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:1101
GfxCaptureFunctions::d3d11_handle
hmodule_ptr_t d3d11_handle
Definition: vsrc_gfxcapture_winrt.cpp:94
GfxCaptureContext::user_hmonitor
uint64_t user_hmonitor
Definition: vsrc_gfxcapture.h:58
get_window_exe_name
static int get_window_exe_name(HWND hwnd, std::string *out)
Definition: vsrc_gfxcapture_winrt.cpp:817
wgc_setup_gfxcapture_session
static int wgc_setup_gfxcapture_session(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:325
GfxCaptureContextWgc::frame_arrived_cond
std::condition_variable frame_arrived_cond
Definition: vsrc_gfxcapture_winrt.cpp:125
GfxCaptureContextWgc::d3d_device
ComPtr< IDirect3DDevice > d3d_device
Definition: vsrc_gfxcapture_winrt.cpp:117
AVHWDeviceContext
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition: hwcontext.h:63
wstring_to_utf8
static int wstring_to_utf8(const wchar_t *in, std::string *out)
Definition: vsrc_gfxcapture_winrt.cpp:802
mutex
static AVMutex mutex
Definition: resman.c:61
T
#define T(x)
Definition: vpx_arith.h:29
avassert.h
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:236
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
GfxCaptureContextWgc::frame_arrived_mutex
std::mutex frame_arrived_mutex
Definition: vsrc_gfxcapture_winrt.cpp:124
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
wgc_thread_init
static int wgc_thread_init(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:512
render_shader_src
static const char render_shader_src[]
Definition: vsrc_gfxcapture_shader.h:24
GfxCaptureContext::display_border
int display_border
Definition: vsrc_gfxcapture.h:62
GfxCaptureContextWgc
Definition: vsrc_gfxcapture_winrt.cpp:112
float
float
Definition: af_crystalizer.c:122
GfxCaptureFunctions::combase_handle
hmodule_ptr_t combase_handle
Definition: vsrc_gfxcapture_winrt.cpp:85
GfxCaptureContext::crop_left
int crop_left
Definition: vsrc_gfxcapture.h:65
ff_filter_link
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition: filters.h:199
frame_size
int frame_size
Definition: mxfenc.c:2489
ff_outlink_set_status
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
GfxCaptureContextWgc::capture_item
ComPtr< IGraphicsCaptureItem > capture_item
Definition: vsrc_gfxcapture_winrt.cpp:116
GfxCaptureContextCpp::wgc_thread_init_mutex
std::mutex wgc_thread_init_mutex
Definition: vsrc_gfxcapture_winrt.cpp:148
GfxCaptureFunctions::lpThreadDescription
PCWSTR lpThreadDescription
Definition: vsrc_gfxcapture_winrt.cpp:104
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:231
find_capture_source
static int find_capture_source(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:857
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
GfxCaptureFunctions::kernel32_handle
hmodule_ptr_t kernel32_handle
Definition: vsrc_gfxcapture_winrt.cpp:103
av_rescale_q
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:142
start_wgc_thread
static int start_wgc_thread(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:675
wgc_try_get_next_frame
static int wgc_try_get_next_frame(AVFilterContext *avctx, ComPtr< IDirect3D11CaptureFrame > &capture_frame)
Definition: vsrc_gfxcapture_winrt.cpp:446
link
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a link
Definition: filter_design.txt:23
if
if(ret)
Definition: filter_design.txt:179
GfxCaptureContextCpp
Definition: vsrc_gfxcapture_winrt.cpp:141
fail
#define fail
Definition: test.h:478
GfxCaptureFunctions::string
UINT32 HSTRING_HEADER HSTRING * string
Definition: vsrc_gfxcapture_winrt.cpp:89
load_functions
static av_cold int load_functions(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:995
NULL
#define NULL
Definition: coverity.c:32
GfxCaptureContext::crop_bottom
int crop_bottom
Definition: vsrc_gfxcapture.h:65
av_buffer_unref
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
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
process_frame_if_exists
static int process_frame_if_exists(AVFilterLink *outlink)
Definition: vsrc_gfxcapture_winrt.cpp:1397
GfxCaptureContext::capture_border
int capture_border
Definition: vsrc_gfxcapture.h:61
AVCOL_PRI_BT709
@ AVCOL_PRI_BT709
also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
Definition: pixfmt.h:638
GetProcAddressTyped
static av_cold void GetProcAddressTyped(const hmodule_ptr_t &hModule, LPCSTR lpProcName, T *out)
Definition: vsrc_gfxcapture_winrt.cpp:991
options
Definition: swscale.c:50
GfxCaptureContext::user_hwnd
uint64_t user_hwnd
Definition: vsrc_gfxcapture.h:57
GfxCaptureFunctions::d3dcompiler_handle
hmodule_ptr_t d3dcompiler_handle
Definition: vsrc_gfxcapture_winrt.cpp:106
gfxcapture_activate
static int gfxcapture_activate(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:1462
build_regex
static int build_regex(AVFilterContext *avctx, const char *pattern, std::regex *out)
Definition: vsrc_gfxcapture_winrt.cpp:775
gfxcapture_uninit
static av_cold void gfxcapture_uninit(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:971
time.h
GfxCaptureContext
Definition: vsrc_gfxcapture.h:47
GFX_SCALE_POINT
@ GFX_SCALE_POINT
Definition: vsrc_gfxcapture.h:36
gfxcapture_init
static av_cold int gfxcapture_init(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:1047
GfxCaptureContextCpp::wgc_thread_uninit_mutex
std::recursive_mutex wgc_thread_uninit_mutex
Definition: vsrc_gfxcapture_winrt.cpp:151
AVD3D11VAFramesContext
This struct is allocated as AVHWFramesContext.hwctx.
Definition: hwcontext_d3d11va.h:145
WM_WGC_THREAD_SHUTDOWN
@ WM_WGC_THREAD_SHUTDOWN
Definition: vsrc_gfxcapture_winrt.cpp:77
options
const OptionDef options[]
wgc_setup_gfxcapture_capture
static int wgc_setup_gfxcapture_capture(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:406
GfxCaptureContextD3D
Definition: vsrc_gfxcapture_winrt.cpp:133
make_win32_callback
auto make_win32_callback(const std::function< Ret(Args...)> &&fn)
Definition: vsrc_gfxcapture_winrt.hpp:182
GfxCaptureContextCpp::wgc_thread_init_cond
std::condition_variable wgc_thread_init_cond
Definition: vsrc_gfxcapture_winrt.cpp:149
LOAD_DLL
#define LOAD_DLL(handle, name)
GfxCaptureFunctions::pTarget
SIZE_T LPCSTR const D3D10_SHADER_MACRO ID3DInclude LPCSTR LPCSTR pTarget
Definition: vsrc_gfxcapture_winrt.cpp:108
wgc_closed_handler
static void wgc_closed_handler(const std::unique_ptr< GfxCaptureContextWgc > &wgctx)
Definition: vsrc_gfxcapture_winrt.cpp:207
ff_gfxcapture_init
av_cold int ff_gfxcapture_init(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:1512
GfxCaptureContextD3D::deferred_ctx
ComPtr< ID3D11DeviceContext > deferred_ctx
Definition: vsrc_gfxcapture_winrt.cpp:138
GfxCaptureFunctions
Definition: vsrc_gfxcapture_winrt.cpp:82
GfxCaptureContext::canvas_height
int canvas_height
Definition: vsrc_gfxcapture.h:64
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
handle_ptr_t
std::unique_ptr< HANDLE, HANDLEDeleter > handle_ptr_t
Definition: vsrc_gfxcapture_winrt.hpp:209
GfxCaptureContext::ctx
GfxCaptureContextCpp * ctx
Definition: vsrc_gfxcapture.h:50
AV_PIX_FMT_D3D11
@ AV_PIX_FMT_D3D11
Hardware surfaces for Direct3D11.
Definition: pixfmt.h:336
wgc_thread_entry
static void wgc_thread_entry(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:599
hmodule_ptr_t
std::unique_ptr< HMODULE, HMODULEDeleter > hmodule_ptr_t
Definition: vsrc_gfxcapture_winrt.hpp:200
get_activation_factory
static HRESULT get_activation_factory(GfxCaptureContextCpp *ctx, PCWSTR clsid, T **factory)
Definition: vsrc_gfxcapture_winrt.cpp:171
prepare_render_resources
static int prepare_render_resources(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:1143
lock
static pthread_mutex_t lock
Definition: ffjni.c:39
DXGI_FORMAT_R16G16B16A16_FLOAT
@ DXGI_FORMAT_R16G16B16A16_FLOAT
Definition: dds.c:63
LOAD_FUNC
#define LOAD_FUNC(handle, name)
internal.h
AVD3D11VADeviceContext
This struct is allocated as AVHWDeviceContext.hwctx.
Definition: hwcontext_d3d11va.h:45
frame_lock
static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
Definition: qsvvpp.c:216
GfxCaptureFunctions::graphicscapture_handle
hmodule_ptr_t graphicscapture_handle
Definition: vsrc_gfxcapture_winrt.cpp:83
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
av_inv_q
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
GFX_RESIZE_SCALE
@ GFX_RESIZE_SCALE
Definition: vsrc_gfxcapture.h:30
len
int len
Definition: vorbis_enc_data.h:426
GfxCaptureContextCpp::wgc
std::unique_ptr< GfxCaptureContextWgc > wgc
Definition: vsrc_gfxcapture_winrt.cpp:143
last_pts
static int64_t last_pts
Definition: decode_filter_video.c:52
GfxCaptureFunctions::user32_handle
hmodule_ptr_t user32_handle
Definition: vsrc_gfxcapture_winrt.cpp:100
AVHWFramesContext
This struct describes a set or pool of "hardware" frames (i.e.
Definition: hwcontext.h:118
ret
ret
Definition: filter_design.txt:187
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:265
wgc_frame_arrived_handler
static void wgc_frame_arrived_handler(const std::unique_ptr< GfxCaptureContextWgc > &wgctx)
Definition: vsrc_gfxcapture_winrt.cpp:199
GFX_RESIZE_SCALE_ASPECT
@ GFX_RESIZE_SCALE_ASPECT
Definition: vsrc_gfxcapture.h:31
av_hwdevice_ctx_create
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
CAPTURE_POOL_SIZE
#define CAPTURE_POOL_SIZE
Definition: vsrc_gfxcapture_winrt.cpp:74
pos
unsigned int pos
Definition: spdifenc.c:414
GfxCaptureContextCpp::d3d
std::unique_ptr< GfxCaptureContextD3D > d3d
Definition: vsrc_gfxcapture_winrt.cpp:144
AVFrame::hw_frames_ctx
AVBufferRef * hw_frames_ctx
For hwaccel-format frames, this should be a reference to the AVHWFramesContext describing the frame.
Definition: frame.h:763
status
ov_status_e status
Definition: dnn_backend_openvino.c:100
GfxCaptureFunctions::coremsg_handle
hmodule_ptr_t coremsg_handle
Definition: vsrc_gfxcapture_winrt.cpp:97
GfxCaptureContext::scale_mode
int scale_mode
Definition: vsrc_gfxcapture.h:68
GfxCaptureFunctions::SrcDataSize
SIZE_T SrcDataSize
Definition: vsrc_gfxcapture_winrt.cpp:107
avfilter.h
GfxCaptureContext::out_fmt
int out_fmt
Definition: vsrc_gfxcapture.h:66
wgc_calculate_client_area
static int wgc_calculate_client_area(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:255
GfxCaptureContextWgc::dispatcher_queue
ComPtr< IDispatcherQueue > dispatcher_queue
Definition: vsrc_gfxcapture_winrt.cpp:114
GfxCaptureContext::window_text
const char * window_text
Definition: vsrc_gfxcapture.h:52
L
#define L(x)
Definition: vpx_arith.h:36
CCTX
#define CCTX(ctx)
Definition: vsrc_gfxcapture_winrt.cpp:80
vsrc_gfxcapture.h
GfxCaptureContextD3D::sampler_state
ComPtr< ID3D11SamplerState > sampler_state
Definition: vsrc_gfxcapture_winrt.cpp:136
wgc_stop_capture_session
static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:215
AVFilterContext
An instance of a filter.
Definition: avfilter.h:273
TIMESPAN_RES
#define TIMESPAN_RES
Definition: vsrc_gfxcapture_winrt.cpp:71
mem.h
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
map
const VDPAUPixFmtMap * map
Definition: hwcontext_vdpau.c:71
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
AV_PIX_FMT_RGBAF16
#define AV_PIX_FMT_RGBAF16
Definition: pixfmt.h:624
init_hwframes_ctx
static int init_hwframes_ctx(AVFilterContext *avctx)
Definition: vsrc_gfxcapture_winrt.cpp:1068
GfxCaptureFunctions::dispatcherQueueController
PDISPATCHERQUEUECONTROLLER * dispatcherQueueController
Definition: vsrc_gfxcapture_winrt.cpp:98
GfxCaptureContextCpp::wgc_thread
std::thread wgc_thread
Definition: vsrc_gfxcapture_winrt.cpp:146
GfxCaptureContextCpp::wgc_thread_cb_data
std::shared_ptr< void > wgc_thread_cb_data
Definition: vsrc_gfxcapture_winrt.cpp:153
GfxCaptureContext::window_exe
const char * window_exe
Definition: vsrc_gfxcapture.h:54
hwcontext.h
AVERROR_BUG
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:52
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
ID3D11Device
void ID3D11Device
Definition: nvenc.h:28
ff_outlink_frame_wanted
the definition of that something depends on the semantic of the filter The callback must examine the status of the filter s links and proceed accordingly The status of output links is stored in the status_in and status_out fields and tested by the ff_outlink_frame_wanted() function. If this function returns true
wgc_thread_uninit
static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:501
GfxCaptureContext::canvas_width
int canvas_width
Definition: vsrc_gfxcapture.h:64
GfxCaptureContextWgc::frame_pool
ComPtr< IDirect3D11CaptureFramePool > frame_pool
Definition: vsrc_gfxcapture_winrt.cpp:118
hwcontext_d3d11va.h
GfxCaptureContext::crop_top
int crop_top
Definition: vsrc_gfxcapture.h:65
cond
int(* cond)(enum AVPixelFormat pix_fmt)
Definition: pixdesc_query.c:28
GfxCaptureContext::window_class
const char * window_class
Definition: vsrc_gfxcapture.h:53
GfxCaptureFunctions::dwmapi_handle
hmodule_ptr_t dwmapi_handle
Definition: vsrc_gfxcapture_winrt.cpp:91
GFX_MONITOR_IDX_WINDOW
@ GFX_MONITOR_IDX_WINDOW
Definition: vsrc_gfxcapture.h:43
ff_gfxcapture_activate
int ff_gfxcapture_activate(AVFilterContext *avctx) noexcept
Definition: vsrc_gfxcapture_winrt.cpp:1527
AVFilterContext::outputs
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:285