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