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