The branch, master has been updated
       via  36e374efb05a2ad81369980de98a0d78381ec81f (commit)
       via  7856f57533b14c18e888e275770a0ca26685d6e4 (commit)
       via  5146b2fb8b700c5376244859dcd1731e4470e3b7 (commit)
       via  9c8a6ac85c3e56f8168a7813e4c825aff7044939 (commit)
       via  0ce413af9cc8040095a6b714600d6a8dceaca514 (commit)
       via  0362cb38062b2a8cead50840aaefb7257570e3f0 (commit)
      from  bdb81d93474ecfc575a88b4028151940b6c8e3c8 (commit)


- Log -----------------------------------------------------------------
commit 36e374efb05a2ad81369980de98a0d78381ec81f
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Sat Aug 30 00:45:22 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    avfilter: add gfxcapture, Windows.Graphics.Capture based window/monitor 
capture

diff --git a/Changelog b/Changelog
index aaab8d9b54..88e1ce6659 100644
--- a/Changelog
+++ b/Changelog
@@ -4,6 +4,7 @@ releases are sorted from youngest to oldest.
 version <next>:
 - ffprobe -codec option
 - EXIF Metadata Parsing
+- gfxcapture: Windows.Graphics.Capture based window/monitor capture
 
 
 version 8.0:
diff --git a/configure b/configure
index 2364e00d08..73531a498a 100755
--- a/configure
+++ b/configure
@@ -2516,6 +2516,8 @@ TOOLCHAIN_FEATURES="
 TYPES_LIST="
     DPI_AWARENESS_CONTEXT
     IDXGIOutput5
+    __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession5
+    IDirect3DDxgiInterfaceAccess
     kCMVideoCodecType_HEVC
     kCMVideoCodecType_HEVCWithAlpha
     kCMVideoCodecType_VP9
@@ -3412,6 +3414,8 @@ pad_cuda_filter_deps_any="cuda_nvcc cuda_llvm"
 sharpen_npp_filter_deps="ffnvcodec libnpp"
 
 ddagrab_filter_deps="d3d11va IDXGIOutput1 DXGI_OUTDUPL_FRAME_INFO"
+gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop 
__x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3"
+gfxcapture_filter_extralibs="-lstdc++"
 scale_d3d11_filter_deps="d3d11va"
 
 amf_deps_any="libdl LoadLibrary"
@@ -6940,6 +6944,10 @@ check_type "windows.h" "DPI_AWARENESS_CONTEXT" 
-D_WIN32_WINNT=0x0A00
 check_type "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo 
-DSECURITY_WIN32
 check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602
 check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat
+check_type "windows.h windows.graphics.capture.h" 
__x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3 
-D_WIN32_WINNT=0x0A00 
-DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS
+check_type "windows.h windows.graphics.capture.h" 
__x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession5 
-D_WIN32_WINNT=0x0A00 
-DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS
+check_type "windows.h windows.graphics.capture.interop.h" 
IGraphicsCaptureItemInterop -D_WIN32_WINNT=0x0A00 
-DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS
+check_type "windows.h windows.graphics.directx.direct3d11.interop.h" 
IDirect3DDxgiInterfaceAccess -D_WIN32_WINNT=0x0A00 
-DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS
 
 check_type "vdpau/vdpau.h" "VdpPictureInfoHEVC"
 check_type "vdpau/vdpau.h" "VdpPictureInfoVP9"
diff --git a/doc/filters.texi b/doc/filters.texi
index 5b52fc5521..726c1d97da 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -29614,6 +29614,165 @@ ddagrab=video_size=800x600:offset_x=100:offset_y=100
 @end example
 
 
+@section gfxcapture
+
+Capture windows or monitors using Windows.Graphics.Capture API.
+
+This source provides low overhead capture of application windows or entire 
monitors.
+The filter outputs hardware frames in @code{d3d11} format; use 
@code{hwdownload,format=}
+if system memory frames are required.
+
+The window to be captured can be selected via regular expressions on its title,
+class name or backing executable name, by explicit native handles, or by 
monitor
+index or explicit native handle. A window must match all provided expressions 
to be
+selected. The first matching window will be picked, in whatever order Windows
+returns them.
+
+Explicit handles (@option{hwnd}, @option{hmonitor}) override pattern or index
+based selection. If neither handles nor a monitor index are given, the first
+window matching the provided regular expressions is captured.
+
+This source does NOT hold a stable FPS. It returns frames at whatever rate the 
compositor
+provides them, only capped by the @option{max_framerate}.
+If you need a stable rate, you need to add an fps filter to drop/duplicate 
frames as needed.
+
+If the capture source disappears mid-capture (window closed, monitor 
disconnected), the filter will return EOF.
+
+This source accepts the following options:
+@table @option
+@item window_title
+ECMAScript regular expression matched against the window title. Supports a
+PCRE style @code{(?i)} prefix for case-insensitive matching.
+
+@item window_class
+As @option{window_title}, but matched against the window class name.
+
+@item window_exe
+As @option{window_title}, but matched against the executable file name of the
+window's process.
+
+@item monitor_idx
+Zero-based index of the monitor to capture.
+
+Can also be set to @code{window} to capture the monitor the selected window
+is displayed on at filter initialization time.
+
+@item hwnd
+Explicit native window handle (HWND).
+
+@item hmonitor
+Explicit native monitor handle (HMONITOR).
+
+@item capture_cursor
+Capture the mouse cursor. Enabled by default.
+
+@item capture_border
+Capture the full area of the window, including its window decorarions/border.
+Disabled by default.
+
+@item display_border
+Draw a yellow highlight border around the captured window. Disabled by default.
+
+@item max_framerate
+Maximum capture frame rate. Accepts a video rate (e.g. @code{30}, @code{60/1},
+@code{24000/1001}). The default is @code{60} FPS.
+The actual rate is the rate at which the compositor renders the window/monitor,
+capped by this option.
+
+@item width
+Force the output canvas width. If zero (default) the initial captured source
+width is used. If a negative number is provided, the width will be rounded
+down to the next multiple of that number.
+
+See @option{resize_mode}.
+
+@item height
+Force the output canvas height. If zero (default) the initial captured source
+height is used. If a negative number is provided, the height will be rounded
+down to the next multiple of that number.
+
+See @option{resize_mode}.
+
+@item crop_left
+Crop this many pixels from the left side of the captured frames.
+
+@item crop_top
+Crop this many pixels from the left side of the captured frames.
+
+@item crop_right
+Crop this many pixels from the left side of the captured frames.
+
+@item crop_bottom
+Crop this many pixels from the left side of the captured frames.
+
+@item premultiplied
+If set to 1, return frames with premultiplied alpha. Default is 0 (straight
+alpha).
+
+@item resize_mode
+Defines how the captured content is fitted into the output canvas size.
+Possible values:
+@table @samp
+@item crop
+Crop (or pad with black) to the canvas size. (default)
+@item scale
+Scale the source to fill the canvas, potentially altering aspect ratio.
+@item scale_aspect
+Scale the source to fit inside the canvas while preserving aspect ratio.
+Remaining area is filled with black.
+@end table
+
+@item scale_mode
+Scaling algorithm used when resizing is required.
+
+Possible values:
+@table @samp
+@item point
+Nearest neighbour (pixelated) scaling.
+@item bilinear
+Bilinear filtering. (default)
+@item bicubic
+Bicubic filtering. Potentially more blurry, but fewer scaling artifacts 
depending on contents.
+@end table
+
+@item output_fmt
+Desired output pixel format inside the D3D11 hardware frames.
+
+Possible values:
+@table @samp
+@item bgra
+@item 8bit
+8 bit BGRA output (default)
+@item x2bgr10
+@item 10bit
+10 bit BGR output
+@item rgbaf16
+@item 16bit
+16bit float RGBA output
+@end table
+
+@end table
+
+@subsection Examples
+@itemize
+@item Capture a window by title (case-insensitive) at a maximum of 60 fps:
+@example
+ffmpeg -filter_complex gfxcapture=window_text='(?i)My 
Application':max_framerate=60,hwdownload,format=bgra,format=yuv420p -c:v 
libx264 -crf 15 capture.mp4
+@end example
+
+@item Capture monitor 1 at native refresh, 10bit color depth, scale to 
1920x1080 preserving aspect:
+@example
+ffmpeg -filter_complex 
gfxcapture=monitor_idx=1:width=1920:height=1080:resize_mode=scale_aspect:output_fmt=10bit
 -c:v hevc_nvenc -cq 15 capture.mp4
+@end example
+
+@item Capture a window by executable name, draw border, force point scaling, 
fixed 60 fps:
+@example
+ffmpeg -filter_complex 
gfxcapture=window_exe='^firefox.exe$':display_border=1:scale_mode=point,fps=60 
-rc qvbr -qvbr_quality_level 15 -c:v h264_amf capture.mp4
+@end example
+
+@end itemize
+
+
 @section gradients
 Generate several gradients.
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index bd3f6da27d..70b100aff1 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -609,6 +609,7 @@ OBJS-$(CONFIG_COLORSPECTRUM_FILTER)          += 
vsrc_testsrc.o
 OBJS-$(CONFIG_COREIMAGESRC_FILTER)           += vf_coreimage.o
 OBJS-$(CONFIG_DDAGRAB_FILTER)                += vsrc_ddagrab.o
 OBJS-$(CONFIG_FREI0R_SRC_FILTER)             += vf_frei0r.o
+OBJS-$(CONFIG_GFXCAPTURE_FILTER)             += vsrc_gfxcapture.o 
vsrc_gfxcapture_winrt.o
 OBJS-$(CONFIG_GRADIENTS_FILTER)              += vsrc_gradients.o
 OBJS-$(CONFIG_HALDCLUTSRC_FILTER)            += vsrc_testsrc.o
 OBJS-$(CONFIG_LIFE_FILTER)                   += vsrc_life.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 3ac1502254..84f15f85c5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -571,6 +571,7 @@ extern const FFFilter ff_vsrc_colorspectrum;
 extern const FFFilter ff_vsrc_coreimagesrc;
 extern const FFFilter ff_vsrc_ddagrab;
 extern const FFFilter ff_vsrc_frei0r_src;
+extern const FFFilter ff_vsrc_gfxcapture;
 extern const FFFilter ff_vsrc_gradients;
 extern const FFFilter ff_vsrc_haldclutsrc;
 extern const FFFilter ff_vsrc_life;
diff --git a/libavfilter/version.h b/libavfilter/version.h
index ba8a6fdab2..77f38cb9b4 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
 
 #include "version_major.h"
 
-#define LIBAVFILTER_VERSION_MINOR   8
+#define LIBAVFILTER_VERSION_MINOR   9
 #define LIBAVFILTER_VERSION_MICRO 100
 
 
diff --git a/libavfilter/vsrc_gfxcapture.c b/libavfilter/vsrc_gfxcapture.c
new file mode 100644
index 0000000000..19d8a8a52c
--- /dev/null
+++ b/libavfilter/vsrc_gfxcapture.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "libavutil/internal.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "filters.h"
+
+#include "vsrc_gfxcapture.h"
+
+#define OFFSET(x) offsetof(GfxCaptureContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+static const AVOption gfxcapture_options[] = {
+    { "window_title",    "ECMAScript regular expression to match against the 
window title. "
+                         "Supports PCRE style (?i) prefix for 
case-insensitivity.",
+                                                              
OFFSET(window_text),    AV_OPT_TYPE_STRING,     { .str = NULL },   0, INT_MAX,  
  FLAGS },
+    { "window_class",    "as window_title, but against the window class",
+                                                              
OFFSET(window_class),   AV_OPT_TYPE_STRING,     { .str = NULL },   0, INT_MAX,  
  FLAGS },
+    { "window_exe",      "as window_title, but against the windows executable 
name",
+                                                              
OFFSET(window_exe),     AV_OPT_TYPE_STRING,     { .str = NULL },   0, INT_MAX,  
  FLAGS },
+    { "monitor_idx",     "index of the monitor to capture",   
OFFSET(monitor_idx),    AV_OPT_TYPE_INT,        { .i64 = 
GFX_MONITOR_IDX_DEFAULT }, GFX_MONITOR_IDX_DEFAULT, INT_MAX, FLAGS, .unit = 
"monitor_idx" },
+    { "window",          "derive from selected window",       0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_MONITOR_IDX_WINDOW }, 0, 0, FLAGS, 
.unit = "monitor_idx" },
+    { "capture_cursor",  "capture mouse cursor",              
OFFSET(capture_cursor), AV_OPT_TYPE_BOOL,       { .i64 = 1 },      0, 1,        
  FLAGS },
+    { "capture_border",  "capture full window border",        
OFFSET(capture_border), AV_OPT_TYPE_BOOL,       { .i64 = 0 },      0, 1,        
  FLAGS },
+    { "display_border",  "display yellow border around captured window",
+                                                              
OFFSET(display_border), AV_OPT_TYPE_BOOL,       { .i64 = 0 },      0, 1,        
  FLAGS },
+    { "max_framerate",   "set maximum capture frame rate",    
OFFSET(frame_rate),     AV_OPT_TYPE_VIDEO_RATE, { .str = "60" },   0.001, 1000, 
  FLAGS },
+    { "hwnd",            "pre-existing HWND handle",          
OFFSET(user_hwnd),      AV_OPT_TYPE_UINT64,     { .i64 = 0 },      0, 
UINT64_MAX, FLAGS },
+    { "hmonitor",        "pre-existing HMONITOR handle",      
OFFSET(user_hmonitor),  AV_OPT_TYPE_UINT64,     { .i64 = 0 },      0, 
UINT64_MAX, FLAGS },
+    { "width",           "force width of the output frames, negative values 
round down the width to the nearest multiple of that number",
+                                                              
OFFSET(canvas_width),   AV_OPT_TYPE_INT,        { .i64 = 0 },      INT_MIN, 
INT_MAX, FLAGS },
+    { "height",          "force height of the output frames, negative values 
round down the height to the nearest multiple of that number",
+                                                              
OFFSET(canvas_height),  AV_OPT_TYPE_INT,        { .i64 = 0 },      INT_MIN, 
INT_MAX, FLAGS },
+    { "crop_left",       "number of pixels to crop from the left of the 
captured area",
+                                                              
OFFSET(crop_left),      AV_OPT_TYPE_INT,        { .i64 = 0 },      0, INT_MAX,  
  FLAGS },
+    { "crop_top",        "number of pixels to crop from the top of the 
captured area",
+                                                              
OFFSET(crop_top),       AV_OPT_TYPE_INT,        { .i64 = 0 },      0, INT_MAX,  
  FLAGS },
+    { "crop_right",      "number of pixels to crop from the right of the 
captured area",
+                                                              
OFFSET(crop_right),     AV_OPT_TYPE_INT,        { .i64 = 0 },      0, INT_MAX,  
  FLAGS },
+    { "crop_bottom",     "number of pixels to crop from the bottom of the 
captured area",
+                                                              
OFFSET(crop_bottom),    AV_OPT_TYPE_INT,        { .i64 = 0 },      0, INT_MAX,  
  FLAGS },
+    { "premultiplied",   "return premultiplied alpha frames", 
OFFSET(premult_alpha),  AV_OPT_TYPE_BOOL,       { .i64 = 0 },      0, 1,        
  FLAGS },
+    { "resize_mode",     "capture source resize behavior",    
OFFSET(resize_mode),    AV_OPT_TYPE_INT,        { .i64 = GFX_RESIZE_CROP },  0, 
GFX_RESIZE_NB - 1, FLAGS, .unit = "resize_mode" },
+    { "crop",            "crop or add black bars into frame", 0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_RESIZE_CROP  }, 0, 0, FLAGS, .unit 
= "resize_mode" },
+    { "scale",           "scale source to fit initial size",  0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_RESIZE_SCALE }, 0, 0, FLAGS, .unit 
= "resize_mode" },
+    { "scale_aspect",    "scale source to fit initial size while preserving 
aspect ratio",
+                                                              0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_RESIZE_SCALE_ASPECT }, 0, 0, FLAGS, 
.unit = "resize_mode" },
+    { "scale_mode",      "scaling algorithm",                 
OFFSET(scale_mode),     AV_OPT_TYPE_INT,        { .i64 = GFX_SCALE_BILINEAR },  
    0, GFX_SCALE_NB - 1, FLAGS, .unit = "scale_mode" },
+    { "point",           "use point scaling",                 0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_SCALE_POINT },         0, 0, FLAGS, 
.unit = "scale_mode" },
+    { "bilinear",        "use bilinear scaling",              0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_SCALE_BILINEAR },      0, 0, FLAGS, 
.unit = "scale_mode" },
+    { "bicubic",         "use bicubic scaling",               0,               
       AV_OPT_TYPE_CONST,      { .i64 = GFX_SCALE_BICUBIC },       0, 0, FLAGS, 
.unit = "scale_mode" },
+    { "output_fmt",      "desired output format",             OFFSET(out_fmt), 
       AV_OPT_TYPE_INT,        { .i64 = AV_PIX_FMT_BGRA },   0, INT_MAX, FLAGS, 
.unit = "output_fmt" },
+    { "8bit",            "output default 8 Bit format",       0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_BGRA },         0, 0, FLAGS, 
.unit = "output_fmt" },
+    { "bgra",            "output 8 Bit BGRA",                 0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_BGRA },         0, 0, FLAGS, 
.unit = "output_fmt" },
+    { "10bit",           "output default 10 Bit format",      0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_X2BGR10 },      0, 0, FLAGS, 
.unit = "output_fmt" },
+    { "x2bgr10",         "output 10 Bit X2BGR10",             0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_X2BGR10 },      0, 0, FLAGS, 
.unit = "output_fmt" },
+    { "16bit",           "output default 16 Bit format",      0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_RGBAF16 },      0, 0, FLAGS, 
.unit = "output_fmt" },
+    { "rgbaf16",         "output 16 Bit RGBAF16",             0,               
       AV_OPT_TYPE_CONST,      { .i64 = AV_PIX_FMT_RGBAF16 },      0, 0, FLAGS, 
.unit = "output_fmt" },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(gfxcapture);
+
+static const AVFilterPad gfxcapture_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = ff_gfxcapture_config_props,
+    },
+};
+
+const FFFilter ff_vsrc_gfxcapture = {
+    .p.name        = "gfxcapture",
+    .p.description = NULL_IF_CONFIG_SMALL("Capture graphics/screen content as 
a video source"),
+    .p.priv_class  = &gfxcapture_class,
+    .p.inputs      = NULL,
+    .p.flags       = AVFILTER_FLAG_HWDEVICE,
+    .priv_size     = sizeof(GfxCaptureContext),
+    .init          = ff_gfxcapture_init,
+    .uninit        = ff_gfxcapture_uninit,
+    FILTER_OUTPUTS(gfxcapture_outputs),
+    FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11),
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .activate      = ff_gfxcapture_activate,
+};
diff --git a/libavfilter/vsrc_gfxcapture.h b/libavfilter/vsrc_gfxcapture.h
new file mode 100644
index 0000000000..617f48de22
--- /dev/null
+++ b/libavfilter/vsrc_gfxcapture.h
@@ -0,0 +1,81 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFILTER_VSRC_GFXCAPTURE_H
+#define AVFILTER_VSRC_GFXCAPTURE_H
+
+typedef struct GfxCaptureContextCpp GfxCaptureContextCpp;
+
+enum GfxResizeMode {
+    GFX_RESIZE_CROP = 0,
+    GFX_RESIZE_SCALE,
+    GFX_RESIZE_SCALE_ASPECT,
+    GFX_RESIZE_NB
+};
+
+enum GfxScaleMode {
+    GFX_SCALE_POINT = 0,
+    GFX_SCALE_BILINEAR,
+    GFX_SCALE_BICUBIC,
+    GFX_SCALE_NB
+};
+
+enum GfxMonitorIdx {
+    GFX_MONITOR_IDX_WINDOW = -1,
+    GFX_MONITOR_IDX_DEFAULT = -2
+};
+
+typedef struct GfxCaptureContext {
+    const AVClass *avclass;
+
+    GfxCaptureContextCpp *ctx;
+
+    const char *window_text;
+    const char *window_class;
+    const char *window_exe;
+    int monitor_idx;
+
+    uint64_t user_hwnd;
+    uint64_t user_hmonitor;
+
+    int capture_cursor;
+    int capture_border;
+    int display_border;
+    AVRational frame_rate;
+    int canvas_width, canvas_height;
+    int crop_left, crop_top, crop_right, crop_bottom;
+    int out_fmt;
+    int resize_mode;
+    int scale_mode;
+    int premult_alpha;
+} GfxCaptureContext;
+
+#ifdef __cplusplus
+#define NOEXCEPT noexcept
+#else
+#define NOEXCEPT
+#endif
+
+av_cold int ff_gfxcapture_init(AVFilterContext *avctx) NOEXCEPT;
+av_cold void ff_gfxcapture_uninit(AVFilterContext *avctx) NOEXCEPT;
+int ff_gfxcapture_activate(AVFilterContext *avctx) NOEXCEPT;
+int ff_gfxcapture_config_props(AVFilterLink *outlink) NOEXCEPT;
+
+#undef NOEXCEPT
+
+#endif /* AVFILTER_VSRC_GFXCAPTURE_H */
diff --git a/libavfilter/vsrc_gfxcapture_shader.h 
b/libavfilter/vsrc_gfxcapture_shader.h
new file mode 100644
index 0000000000..3fe9d5ec0d
--- /dev/null
+++ b/libavfilter/vsrc_gfxcapture_shader.h
@@ -0,0 +1,126 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFILTER_VSRC_GFXCAPTURE_SHADER_H
+#define AVFILTER_VSRC_GFXCAPTURE_SHADER_H
+
+#define HLSL_SHADER(shader) #shader
+
+static const char render_shader_src[] = HLSL_SHADER(
+    struct VSOut {
+        float4 pos : SV_Position;
+        float2 uv : TEXCOORD0;
+    };
+
+    cbuffer cb : register(b0) {
+        float2 tS;
+        float2 dS;
+        float2 uvMin;
+        float2 uvMax;
+        uint to_unpremult;
+        uint to_srgb;
+        uint2 pad;
+    };
+
+    Texture2D t0 : register(t0);
+    SamplerState s0 : register(s0);
+
+    VSOut main_vs(uint id : SV_VertexID) {
+        VSOut o;
+        o.pos = float4(id == 2 ? 3.0 : -1.0, id == 1 ? 3.0 : -1.0, 0, 1);
+        o.uv = lerp(uvMin, uvMax, float2((o.pos.x + 1) * 0.5, 1 - (o.pos.y + 
1) * 0.5));
+        return o;
+    }
+
+    float4 cubic(float v) {
+        float4 n = float4(1.0, 2.0, 3.0, 4.0) - v;
+        float4 s = n * n * n;
+        float  x = s.x;
+        float  y = s.y - 4.0 * s.x;
+        float  z = s.z - 4.0 * s.y + 6.0 * s.x;
+        float  w = 6.0 - x - y - z;
+        return float4(x, y, z, w) * (1.0 / 6.0);
+    }
+
+    float4 texBicubic(Texture2D t, SamplerState ss, float2 uv) {
+        float2 itS = 1.0 / tS;
+
+        float2 tc = uv * tS - 0.5;
+        float2 fxy = frac(tc);
+        tc -= fxy;
+
+        float4 xc = cubic(fxy.x);
+        float4 yc = cubic(fxy.y);
+
+        float4 s = float4(xc.xz + xc.yw, yc.xz + yc.yw);
+        float4 o = tc.xxyy + (float2(-0.5, 1.5)).xyxy + float4(xc.yw, yc.yw) / 
s;
+        o *= itS.xxyy;
+
+        float4 s0 = t.Sample(ss, o.xz);
+        float4 s1 = t.Sample(ss, o.yz);
+        float4 s2 = t.Sample(ss, o.xw);
+        float4 s3 = t.Sample(ss, o.yw);
+
+        float sx = s.x / (s.x + s.y);
+        float sy = s.z / (s.z + s.w);
+
+        return lerp(lerp(s3, s2, sx), lerp(s1, s0, sx), sy);
+    }
+
+    float4 unpremultiply(float4 c) {
+        if (c.a < 1e-6)
+            return float4(0.0, 0.0, 0.0, 0.0);
+        return float4(c.rgb / c.a, c.a);
+    }
+
+    float4 premultiply(float4 c) {
+        return float4(c.rgb * c.a, c.a);
+    }
+
+    float3 linear_to_srgb(float3 c) {
+        c = max(c, 0.0);
+        float3 lo = 12.92 * c;
+        float3 hi = 1.055 * pow(c, 1.0 / 2.4) - 0.055;
+        return saturate(lerp(hi, lo, step(c, 0.0031308)));
+    }
+
+    float4 fix_color(float4 c) {
+        if (to_unpremult || to_srgb)
+            c = unpremultiply(c);
+        if (to_srgb) {
+            c.rgb = linear_to_srgb(c.rgb);
+            if (!to_unpremult)
+                c = premultiply(c);
+        }
+        return c;
+    }
+
+    float4 main_ps(VSOut i) : SV_Target {
+        return fix_color(t0.Sample(s0, i.uv));
+    }
+
+    float4 main_ps_bicubic(VSOut i) : SV_Target {
+        if (all(tS == dS))
+            return main_ps(i);
+        return fix_color(texBicubic(t0, s0, i.uv));
+    }
+);
+
+#undef HLSL_SHADER
+
+#endif /* AVFILTER_VSRC_GFXCAPTURE_SHADER_H */
diff --git a/libavfilter/vsrc_gfxcapture_winrt.cpp 
b/libavfilter/vsrc_gfxcapture_winrt.cpp
new file mode 100644
index 0000000000..42977efcc5
--- /dev/null
+++ b/libavfilter/vsrc_gfxcapture_winrt.cpp
@@ -0,0 +1,1551 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+extern "C" {
+#include "config.h"
+}
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0A00
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0A00
+#endif
+
+#define WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION 0x130000
+
+// work around bug in mingw double-defining IReference<unsigned char> (BYTE == 
boolean)
+#define ____FIReference_1_boolean_INTERFACE_DEFINED__
+
+#include <windows.h>
+#include <initguid.h>
+#include <wrl.h>
+#include <roapi.h>
+#include <dwmapi.h>
+#include <d3d11.h>
+#include <d3dcompiler.h>
+#include <dispatcherqueue.h>
+#include <windows.foundation.h>
+#include <windows.graphics.capture.h>
+#include <windows.graphics.capture.interop.h>
+#include <windows.graphics.directx.direct3d11.h>
+#if HAVE_IDIRECT3DDXGIINTERFACEACCESS
+#include <windows.graphics.directx.direct3d11.interop.h>
+#endif
+
+extern "C" {
+#include "libavutil/avassert.h"
+#include "libavutil/internal.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/thread.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_d3d11va.h"
+#include "avfilter.h"
+#include "filters.h"
+#include "video.h"
+
+#include "vsrc_gfxcapture.h"
+}
+
+#include <atomic>
+#include <cinttypes>
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <regex>
+#include <string>
+#include <type_traits>
+
+#include "vsrc_gfxcapture_winrt.h"
+#include "vsrc_gfxcapture_shader.h"
+
+using namespace ABI::Windows::System;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Graphics::Capture;
+using namespace ABI::Windows::Graphics::DirectX::Direct3D11;
+using namespace Windows::Graphics::DirectX::Direct3D11;
+using Microsoft::WRL::ComPtr;
+using ABI::Windows::Graphics::SizeInt32;
+using ABI::Windows::Foundation::TimeSpan;
+using ABI::Windows::Graphics::DirectX::DirectXPixelFormat;
+
+#define TIMESPAN_RES 10000000
+#define TIMESPAN_RES64 INT64_C(10000000)
+
+#define CAPTURE_POOL_SIZE 2
+
+enum {
+    WM_WGC_THREAD_SHUTDOWN = WM_APP + 1
+};
+
+#define CCTX(ctx) static_cast<GfxCaptureContext*>(ctx)
+
+typedef struct GfxCaptureFunctions {
+    hmodule_ptr_t graphicscapture_handle;
+
+    hmodule_ptr_t combase_handle;
+    HRESULT (WINAPI *RoInitialize)(RO_INIT_TYPE initType);
+    void (WINAPI *RoUninitialize)(void);
+    HRESULT (WINAPI *RoGetActivationFactory)(HSTRING activatableClassId, 
REFIID iid, void **factory);
+    HRESULT (WINAPI *WindowsCreateStringReference)(PCWSTR sourceString, UINT32 
length, HSTRING_HEADER *hstringHeader, HSTRING *string);
+
+    hmodule_ptr_t dwmapi_handle;
+    HRESULT (WINAPI *DwmGetWindowAttribute)(HWND hwnd, DWORD dwAttribute, 
PVOID pvAttribute, DWORD cbAttribute);
+
+    hmodule_ptr_t d3d11_handle;
+    HRESULT (WINAPI *CreateDirect3D11DeviceFromDXGIDevice)(IDXGIDevice 
*dxgiDevice, IInspectable **graphicsDevice);
+
+    hmodule_ptr_t coremsg_handle;
+    HRESULT (WINAPI *CreateDispatcherQueueController)(DispatcherQueueOptions 
options, PDISPATCHERQUEUECONTROLLER *dispatcherQueueController);
+
+    hmodule_ptr_t user32_handle;
+    DPI_AWARENESS_CONTEXT (WINAPI 
*SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext);
+
+    hmodule_ptr_t d3dcompiler_handle;
+    HRESULT (WINAPI *D3DCompile)(LPCVOID pSrcData, SIZE_T SrcDataSize, LPCSTR 
pSourceName, const D3D10_SHADER_MACRO *pDefines, ID3DInclude *pInclude,
+                                 LPCSTR pEntrypoint, LPCSTR pTarget, UINT 
Flags1, UINT Flags2, ID3DBlob **ppCode, ID3DBlob **ppErrorMsgs);
+} GfxCaptureFunctions;
+
+// This struct contains all data handled by the capture thread
+struct GfxCaptureContextWgc {
+    ComPtr<IDispatcherQueueController> dispatcher_queue_controller;
+    ComPtr<IDispatcherQueue> dispatcher_queue;
+
+    ComPtr<IGraphicsCaptureItem> capture_item;
+    ComPtr<IDirect3DDevice> d3d_device;
+    ComPtr<IDirect3D11CaptureFramePool> frame_pool;
+    ComPtr<IGraphicsCaptureSession> capture_session;
+
+    EventRegistrationToken frame_arrived_token { 0 };
+    EventRegistrationToken closed_token { 0 };
+
+    std::mutex frame_arrived_mutex;
+    std::condition_variable frame_arrived_cond;
+    std::atomic<bool> window_closed { false };
+    std::atomic<uint64_t> frame_seq { 0 };
+
+    SizeInt32 cap_size { 0, 0 };
+    RECT client_area_offsets { 0, 0, 0, 0 };
+};
+
+struct GfxCaptureContextD3D {
+    ComPtr<ID3D11VertexShader> vertex_shader;
+    ComPtr<ID3D11PixelShader> pixel_shader;
+    ComPtr<ID3D11SamplerState> sampler_state;
+    ComPtr<ID3D11Buffer> shader_cb;
+    ComPtr<ID3D11DeviceContext> deferred_ctx;
+};
+
+struct GfxCaptureContextCpp {
+    GfxCaptureFunctions fn;
+    std::unique_ptr<GfxCaptureContextWgc> wgc;
+    std::unique_ptr<GfxCaptureContextD3D> d3d;
+
+    pthread_t wgc_thread;
+    bool wgc_thread_created { false };
+    DWORD wgc_thread_id { 0 };
+    std::mutex wgc_thread_init_mutex;
+    std::condition_variable wgc_thread_init_cond;
+    volatile int wgc_thread_init_res { INT_MAX };
+    std::recursive_mutex wgc_thread_uninit_mutex;
+
+    HWND capture_hwnd { nullptr };
+    HMONITOR capture_hmonitor { nullptr };
+
+    AVBufferRef *device_ref { nullptr };
+    AVHWDeviceContext *device_ctx { nullptr };
+    AVD3D11VADeviceContext *device_hwctx { nullptr };
+
+    AVBufferRef *frames_ref { nullptr };
+    AVHWFramesContext *frames_ctx { nullptr };
+    AVD3D11VAFramesContext *frames_hwctx { nullptr };
+
+    int64_t first_pts { 0 };
+    int64_t last_pts { 0 };
+};
+
+template <typename T>
+static HRESULT get_activation_factory(GfxCaptureContextCpp *ctx, PCWSTR clsid, 
T** factory) {
+    HSTRING_HEADER hsheader = { 0 };
+    HSTRING hs = NULL;
+
+    HRESULT hr = ctx->fn.WindowsCreateStringReference(clsid, 
(UINT32)wcslen(clsid), &hsheader, &hs);
+    if (FAILED(hr))
+        return hr;
+
+    return ctx->fn.RoGetActivationFactory(hs, IID_PPV_ARGS(factory));
+}
+
+#define CHECK_HR(fcall, action) \
+    do { \
+        HRESULT fhr = fcall; \
+        if (FAILED(fhr)) { \
+            av_log(avctx, AV_LOG_ERROR, #fcall " failed: 0x%08lX\n", fhr); \
+            action; \
+        } \
+    } while (0)
+#define CHECK_HR_RET(...) CHECK_HR((__VA_ARGS__), return AVERROR_EXTERNAL)
+#define CHECK_HR_FAIL(...) CHECK_HR((__VA_ARGS__), ret = AVERROR_EXTERNAL; 
goto fail)
+#define CHECK_HR_LOG(...) CHECK_HR((__VA_ARGS__), (void)0)
+
+/****************************************************
+ * Windows Graphics Capture Worker Thread           *
+ * All wgc_* functions must run only on WGC thread! *
+ ****************************************************/
+
+static void wgc_frame_arrived_handler(const 
std::unique_ptr<GfxCaptureContextWgc> &wgctx) {
+    wgctx->frame_seq.fetch_add(1, std::memory_order_release);
+    wgctx->frame_arrived_cond.notify_one();
+}
+
+static void wgc_closed_handler(const std::unique_ptr<GfxCaptureContextWgc> 
&wgctx) {
+    wgctx->window_closed.store(true, std::memory_order_release);
+    wgctx->frame_arrived_cond.notify_one();
+}
+
+static void wgc_stop_capture_session(AVFilterContext *avctx) noexcept
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    if (wgctx->closed_token.value && wgctx->capture_item) {
+        CHECK_HR_LOG(wgctx->capture_item->remove_Closed(wgctx->closed_token));
+        wgctx->closed_token.value = 0;
+    }
+
+    if (wgctx->frame_arrived_token.value && wgctx->frame_pool) {
+        
CHECK_HR_LOG(wgctx->frame_pool->remove_FrameArrived(wgctx->frame_arrived_token));
+        wgctx->frame_arrived_token.value = 0;
+    }
+
+    if (wgctx->capture_session) {
+        ComPtr<IClosable> closable;
+        if (SUCCEEDED(wgctx->capture_session.As(&closable))) {
+            closable->Close();
+        } else {
+            av_log(avctx, AV_LOG_ERROR, "Failed to get capture session 
IClosable interface\n");
+        }
+    }
+
+    if (wgctx->frame_pool) {
+        ComPtr<IClosable> closable;
+        if (SUCCEEDED(wgctx->frame_pool.As(&closable))) {
+            CHECK_HR_LOG(closable->Close());
+        } else {
+            av_log(avctx, AV_LOG_ERROR, "Failed to get frame pool IClosable 
interface\n");
+        }
+    }
+}
+
+static int wgc_calculate_client_area(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    if (!ctx->capture_hwnd) {
+        wgctx->client_area_offsets.left = 0;
+        wgctx->client_area_offsets.top = 0;
+        wgctx->client_area_offsets.right = 0;
+        wgctx->client_area_offsets.bottom = 0;
+        return 0;
+    }
+
+    RECT client_rect = {};
+    RECT frame_bounds = {};
+    RECT window_rect = {};
+
+    if (IsIconic(ctx->capture_hwnd)) {
+        av_log(avctx, AV_LOG_VERBOSE, "Capture window is iconic, no client 
area\n");
+        return 0;
+    }
+
+    if (!GetClientRect(ctx->capture_hwnd, &client_rect)) {
+        av_log(avctx, AV_LOG_ERROR, "GetClientRect failed\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    if (!MapWindowPoints(ctx->capture_hwnd, nullptr, (POINT*)&client_rect, 2)) 
{
+        av_log(avctx, AV_LOG_ERROR, "MapWindowPoints failed\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    if (FAILED(ctx->fn.DwmGetWindowAttribute(ctx->capture_hwnd, 
DWMWA_EXTENDED_FRAME_BOUNDS, &frame_bounds, sizeof(window_rect))))
+        av_log(avctx, AV_LOG_DEBUG, "DwmGetWindowAttribute failed\n");
+
+    if (!GetWindowRect(ctx->capture_hwnd, &window_rect))
+        av_log(avctx, AV_LOG_DEBUG, "GetWindowRect failed\n");
+
+    if (wgctx->cap_size.Width == frame_bounds.right - frame_bounds.left ||
+        wgctx->cap_size.Height == frame_bounds.bottom - frame_bounds.top) {
+        av_log(avctx, AV_LOG_DEBUG, "Using window rect from 
DWMWA_EXTENDED_FRAME_BOUNDS\n");
+    } else if (wgctx->cap_size.Width == window_rect.right - window_rect.left ||
+               wgctx->cap_size.Height == window_rect.bottom - window_rect.top) 
{
+        av_log(avctx, AV_LOG_DEBUG, "Using window rect from GetWindowRect\n");
+        frame_bounds = window_rect;
+    } else {
+        if ((frame_bounds.top == frame_bounds.bottom || frame_bounds.left == 
frame_bounds.right) &&
+            (window_rect.top == window_rect.bottom || window_rect.left == 
window_rect.right))
+        {
+            av_log(avctx, AV_LOG_ERROR, "No valid window rect found\n");
+            return AVERROR_EXTERNAL;
+        }
+        av_log(avctx, AV_LOG_VERBOSE, "Failed to get valid window rect, client 
area may be inaccurate\n");
+        return 0;
+    }
+
+    wgctx->client_area_offsets.left = FFMAX(client_rect.left - 
frame_bounds.left, 0);
+    wgctx->client_area_offsets.top = FFMAX(client_rect.top - frame_bounds.top, 
0);
+    wgctx->client_area_offsets.right = FFMAX(frame_bounds.right - 
client_rect.right, 0);
+    wgctx->client_area_offsets.bottom = FFMAX(frame_bounds.bottom - 
client_rect.bottom, 0);
+
+    av_log(avctx, AV_LOG_DEBUG, "Client area offsets: left=%ld top=%ld 
right=%ld bottom=%ld\n",
+           wgctx->client_area_offsets.left, wgctx->client_area_offsets.top,
+           wgctx->client_area_offsets.right, 
wgctx->client_area_offsets.bottom);
+
+    return 0;
+}
+
+static int wgc_setup_gfxcapture_session(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+    int ret;
+
+    ComPtr<IDirect3D11CaptureFramePoolStatics> frame_pool_statics;
+    ComPtr<ID3D11Device> d3d11_device = ctx->device_hwctx->device;
+    ComPtr<ID3D10Multithread> d3d10_multithread;
+    ComPtr<IDXGIDevice> dxgi_device;
+    ComPtr<IGraphicsCaptureSession2> session2;
+    ComPtr<IGraphicsCaptureSession3> session3;
+    ComPtr<IGraphicsCaptureSession5> session5;
+
+    DirectXPixelFormat fmt = 
DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
+    if (cctx->out_fmt != AV_PIX_FMT_BGRA)
+        fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
+
+    CHECK_HR_RET(wgctx->capture_item->get_Size(&wgctx->cap_size));
+    ret = wgc_calculate_client_area(avctx);
+    if (ret < 0)
+        return ret;
+
+    CHECK_HR_RET(d3d11_device.As(&d3d10_multithread));
+    d3d10_multithread->SetMultithreadProtected(TRUE);
+
+    CHECK_HR_RET(d3d11_device.As(&dxgi_device));
+    
CHECK_HR_RET(ctx->fn.CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), 
&wgctx->d3d_device));
+
+    
CHECK_HR_RET(get_activation_factory<IDirect3D11CaptureFramePoolStatics>(ctx, 
RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool, 
&frame_pool_statics));
+    CHECK_HR_RET(frame_pool_statics->Create(wgctx->d3d_device.Get(), fmt, 
CAPTURE_POOL_SIZE, wgctx->cap_size, &wgctx->frame_pool));
+    
CHECK_HR_RET(wgctx->frame_pool->CreateCaptureSession(wgctx->capture_item.Get(), 
&wgctx->capture_session));
+
+    if (SUCCEEDED(wgctx->capture_session.As(&session2))) {
+        if 
(FAILED(session2->put_IsCursorCaptureEnabled(cctx->capture_cursor))) {
+            av_log(avctx, AV_LOG_WARNING, "Failed setting cursor capture 
mode\n");
+        }
+    } else {
+        av_log(avctx, AV_LOG_WARNING, "Cursor capture unavailable\n");
+    }
+
+    if (SUCCEEDED(wgctx->capture_session.As(&session3))) {
+        // this one is weird, it can return failure but still work
+        if (FAILED(session3->put_IsBorderRequired(cctx->display_border))) {
+            av_log(avctx, AV_LOG_WARNING, "Failed setting border drawing 
mode\n");
+        }
+    } else {
+        av_log(avctx, AV_LOG_WARNING, "Disabling border drawing 
unavailable\n");
+    }
+
+    if (SUCCEEDED(wgctx->capture_session.As(&session5))) {
+        TimeSpan ivl = { av_rescale_q(1, av_inv_q(cctx->frame_rate), 
AVRational{1, TIMESPAN_RES}) };
+        if (FAILED(session5->put_MinUpdateInterval(ivl))) {
+            av_log(avctx, AV_LOG_WARNING, "Failed setting minimum update 
interval, framerate may be limited\n");
+        }
+    } else {
+        av_log(avctx, AV_LOG_WARNING, "Setting minimum update interval 
unavailable, framerate may be limited\n");
+    }
+
+    wgctx->window_closed = 0;
+
+    CHECK_HR_RET(wgctx->capture_item->add_Closed(
+        
create_cb_handler<ITypedEventHandler<GraphicsCaptureItem*,IInspectable*>, 
IGraphicsCaptureItem*, IInspectable*>(
+            [avctx, ctx](auto, auto) {
+                av_log(avctx, AV_LOG_INFO, "Capture item closed\n");
+                wgc_closed_handler(ctx->wgc);
+                return S_OK;
+            }).Get(), &wgctx->closed_token));
+
+    CHECK_HR_RET(wgctx->frame_pool->add_FrameArrived(
+        
create_cb_handler<ITypedEventHandler<Direct3D11CaptureFramePool*,IInspectable*>,
 IDirect3D11CaptureFramePool*, IInspectable*>(
+            [avctx, ctx](auto, auto) {
+                av_log(avctx, AV_LOG_TRACE, "Frame arrived\n");
+                wgc_frame_arrived_handler(ctx->wgc);
+                return S_OK;
+            }).Get(), &wgctx->frame_arrived_token));
+
+    return 0;
+}
+
+static int wgc_setup_gfxcapture_capture(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+    HRESULT hr;
+    int ret;
+
+    ComPtr<IGraphicsCaptureItemInterop> capture_item_interop;
+    CHECK_HR_RET(get_activation_factory<IGraphicsCaptureItemInterop>(ctx, 
RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem, 
&capture_item_interop));
+
+    if (ctx->capture_hmonitor) {
+        hr = capture_item_interop->CreateForMonitor(ctx->capture_hmonitor, 
IID_PPV_ARGS(&wgctx->capture_item));
+        if (FAILED(hr)) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for 
monitor (0x%08lX)\n", hr);
+            return AVERROR_EXTERNAL;
+        }
+    } else if (ctx->capture_hwnd) {
+        hr = capture_item_interop->CreateForWindow(ctx->capture_hwnd, 
IID_PPV_ARGS(&wgctx->capture_item));
+        if (FAILED(hr)) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture for 
window (0x%08lX)\n", hr);
+            return AVERROR_EXTERNAL;
+        }
+    }
+
+    ret = wgc_setup_gfxcapture_session(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture pool\n");
+        return ret;
+    }
+
+    hr = ctx->wgc->capture_session->StartCapture();
+    if (FAILED(hr)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to start graphics capture session 
(0x%08lX)\n", hr);
+        return AVERROR_EXTERNAL;
+    }
+
+    return 0;
+}
+
+static int wgc_try_get_next_frame(AVFilterContext *avctx, 
ComPtr<IDirect3D11CaptureFrame> *capture_frame)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    ComPtr<IDirect3DSurface> capture_surface;
+    ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
+    ComPtr<ID3D11Texture2D> frame_texture;
+    SizeInt32 frame_size = { 0, 0 };
+
+    
CHECK_HR_RET(wgctx->frame_pool->TryGetNextFrame(capture_frame->ReleaseAndGetAddressOf()));
+    if (!capture_frame->Get())
+        return AVERROR(EAGAIN);
+
+    CHECK_HR_RET(capture_frame->Get()->get_ContentSize(&frame_size));
+    if (frame_size.Width != wgctx->cap_size.Width || frame_size.Height != 
wgctx->cap_size.Height) {
+        av_log(avctx, AV_LOG_VERBOSE, "Capture size changed to %dx%d\n", 
frame_size.Width, frame_size.Height);
+
+        DirectXPixelFormat fmt = 
DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
+        if (cctx->out_fmt != AV_PIX_FMT_BGRA)
+            fmt = DirectXPixelFormat::DirectXPixelFormat_R16G16B16A16Float;
+
+        CHECK_HR_RET(wgctx->frame_pool->Recreate(wgctx->d3d_device.Get(), fmt, 
CAPTURE_POOL_SIZE, frame_size));
+        wgctx->cap_size = frame_size;
+
+        int ret = wgc_calculate_client_area(avctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static int wgc_setup_winrt(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+    MSG msg;
+
+    // pre-create the message-queue
+    PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE);
+
+    DispatcherQueueOptions options = { 0 };
+    options.dwSize = sizeof(DispatcherQueueOptions);
+    options.threadType = DISPATCHERQUEUE_THREAD_TYPE::DQTYPE_THREAD_CURRENT;
+    options.apartmentType = 
DISPATCHERQUEUE_THREAD_APARTMENTTYPE::DQTAT_COM_NONE;
+
+    CHECK_HR_RET(ctx->fn.CreateDispatcherQueueController(options, 
&wgctx->dispatcher_queue_controller));
+    
CHECK_HR_RET(wgctx->dispatcher_queue_controller->get_DispatcherQueue(&wgctx->dispatcher_queue));
+
+    return 0;
+}
+
+static void wgc_thread_uninit(AVFilterContext *avctx) noexcept
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+    wgc_stop_capture_session(avctx);
+
+    ctx->wgc.reset();
+    ctx->fn.RoUninitialize();
+}
+
+static int wgc_thread_init(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    HRESULT hr;
+    int ret;
+
+    ctx->wgc = std::make_unique<GfxCaptureContextWgc>();
+
+    
ctx->fn.SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+
+    hr = ctx->fn.RoInitialize(RO_INIT_MULTITHREADED);
+    if (FAILED(hr)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to initialize WinRT\n");
+        ctx->wgc.reset();
+        return AVERROR_EXTERNAL;
+    }
+
+    ret = wgc_setup_winrt(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to setup WinRT\n");
+        goto fail;
+    }
+
+    ret = wgc_setup_gfxcapture_capture(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    wgc_thread_uninit(avctx);
+    return ret;
+}
+
+static int wgc_thread_worker(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+    ComPtr<IAsyncAction> async;
+    MSG msg;
+
+    av_log(avctx, AV_LOG_DEBUG, "Starting message loop\n");
+
+    while (BOOL res = GetMessage(&msg, NULL, 0, 0)) {
+        if (res == -1) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to get message\n");
+            return AVERROR(EIO);
+        }
+
+        if (!msg.hwnd && msg.message == WM_WGC_THREAD_SHUTDOWN) {
+            av_log(avctx, AV_LOG_DEBUG, "Initializing WGC thread shutdown\n");
+            if 
(FAILED(wgctx->dispatcher_queue_controller->ShutdownQueueAsync(&async))) {
+                av_log(avctx, AV_LOG_ERROR, "Failed to shutdown dispatcher 
queue\n");
+                return AVERROR_EXTERNAL;
+            }
+            
async->put_Completed(create_cb_handler<IAsyncActionCompletedHandler, 
IAsyncAction*, AsyncStatus>(
+                [avctx, ctx](auto, auto status) {
+                    PostThreadMessage(ctx->wgc_thread_id, WM_QUIT, 0, 0);
+                    av_log(avctx, AV_LOG_DEBUG, "WGC thread async shutdown 
completed: %d\n", (int)status);
+                    return S_OK;
+                }).Get());
+            continue;
+        }
+
+        av_log(avctx, AV_LOG_TRACE, "Got message: %u\n", msg.message);
+
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+    if (!async) {
+        av_log(avctx, AV_LOG_ERROR, "WGC Thread message loop ended without 
proper shutdown\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    av_log(avctx, AV_LOG_DEBUG, "Message loop ended\n");
+
+    return msg.wParam;
+}
+
+static void *wgc_thread_entry(void *arg) noexcept
+{
+    AVFilterContext *avctx = static_cast<AVFilterContext*>(arg);
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+    {
+        static const char name_prefix[] = "wgc_winrt@0x";
+        char thread_name[sizeof(name_prefix) + sizeof(void*) * 2];
+        snprintf(thread_name, sizeof(thread_name), "%s%" PRIxPTR, name_prefix, 
(uintptr_t)avctx);
+        ff_thread_setname(thread_name);
+
+        std::lock_guard init_lock(ctx->wgc_thread_init_mutex);
+        ctx->wgc_thread_id = GetCurrentThreadId();
+
+        try {
+            ctx->wgc_thread_init_res = wgc_thread_init(avctx);
+        } catch (const std::bad_alloc &) {
+            ctx->wgc_thread_init_res = AVERROR(ENOMEM);
+        } catch (const std::exception &e) {
+            av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread 
init: %s\n", e.what());
+            ctx->wgc_thread_init_res = AVERROR_BUG;
+        } catch (...) {
+            av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread 
init\n");
+            ctx->wgc_thread_init_res = AVERROR_BUG;
+        }
+
+        ctx->wgc_thread_init_cond.notify_all();
+        if (ctx->wgc_thread_init_res < 0)
+            return (void*)(intptr_t)AVERROR(ENOSYS);
+    }
+
+    int ret;
+
+    try {
+        ret = wgc_thread_worker(avctx);
+    } catch (const std::bad_alloc &) {
+        ret = AVERROR(ENOMEM);
+    } catch (const std::exception &e) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception in WGC thread worker: 
%s\n", e.what());
+        ret = AVERROR_BUG;
+    } catch (...) {
+        av_log(avctx, AV_LOG_ERROR, "Unhandled exception in WGC thread 
worker\n");
+        ret = AVERROR_BUG;
+    }
+
+    std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
+    wgc_thread_uninit(avctx);
+
+    return (void*)(intptr_t)ret;
+}
+
+/***********************************
+ * WGC Thread Management Functions *
+ ***********************************/
+
+static int stop_wgc_thread(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    int ret = 0;
+
+    if (ctx->wgc_thread_created) {
+        if (ctx->wgc_thread_id && !PostThreadMessage(ctx->wgc_thread_id, 
WM_WGC_THREAD_SHUTDOWN, 0, 0))
+            av_log(avctx, AV_LOG_ERROR, "Failed to post shutdown message to 
WGC thread\n");
+
+        void *wgc_res = nullptr;
+        pthread_join(ctx->wgc_thread, &wgc_res);
+        ret = (int)(intptr_t)wgc_res;
+
+        ctx->wgc_thread_id = 0;
+        ctx->wgc_thread_created = false;
+    }
+
+    return ret;
+}
+
+static int start_wgc_thread(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+    if (ctx->wgc_thread_created || ctx->wgc_thread_id) {
+        av_log(avctx, AV_LOG_ERROR, "Double-creation of WGC thread\n");
+        return AVERROR_BUG;
+    }
+
+    std::unique_lock wgc_lock(ctx->wgc_thread_init_mutex);
+    ctx->wgc_thread_init_res = INT_MAX;
+
+    int ret = pthread_create(&ctx->wgc_thread, nullptr, wgc_thread_entry, 
avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to create WGC thread\n");
+        return ret;
+    }
+
+    ctx->wgc_thread_created = true;
+
+    if (!ctx->wgc_thread_init_cond.wait_for(wgc_lock, std::chrono::seconds(1), 
[&]() {
+        return ctx->wgc_thread_init_res != INT_MAX;
+    })) {
+        av_log(avctx, AV_LOG_ERROR, "WGC thread init timed out\n");
+        return AVERROR(ETIMEDOUT);
+    }
+
+    return ctx->wgc_thread_init_res;
+}
+
+template <typename F>
+static int run_on_wgc_thread(AVFilterContext *avctx, F &&cb)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    std::lock_guard uninit_lock(ctx->wgc_thread_uninit_mutex);
+    if (!wgctx) {
+        av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
+        return AVERROR(ENOSYS);
+    }
+
+    struct CBData {
+        std::mutex mutex;
+        std::condition_variable cond;
+        std::atomic<bool> done { false };
+        std::atomic<bool> cancel { false };
+        int ret = AVERROR_BUG;
+    };
+    auto cbdata = std::make_shared<CBData>();
+
+    std::unique_lock cblock(cbdata->mutex);
+
+    boolean res = 0;
+    CHECK_HR_RET(wgctx->dispatcher_queue->TryEnqueue(
+        create_cb_handler<IDispatcherQueueHandler>(
+            [cb = std::forward<F>(cb), cbdata]() {
+                {
+                    std::lock_guard lock(cbdata->mutex);
+                    if (cbdata->cancel.load(std::memory_order_acquire))
+                        return S_OK;
+
+                    try {
+                        cbdata->ret = cb();
+                    } catch (const std::bad_alloc &) {
+                        cbdata->ret = AVERROR(ENOMEM);
+                    } catch (...) {
+                        cbdata->ret = AVERROR_BUG;
+                    }
+
+                    cbdata->done.store(true, std::memory_order_release);
+                }
+
+                cbdata->cond.notify_one();
+                return S_OK;
+            }).Get(), &res));
+    if (!res) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to enqueue WGC thread callback\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    if (!cbdata->cond.wait_for(cblock, std::chrono::seconds(1), [&]() { return 
cbdata->done.load(std::memory_order_acquire); })) {
+        cbdata->cancel.store(true, std::memory_order_release);
+        av_log(avctx, AV_LOG_ERROR, "WGC thread callback timed out\n");
+        return AVERROR(ETIMEDOUT);
+    }
+
+    return cbdata->ret;
+}
+
+/*******************************
+ * Standard AVFilter functions *
+ *******************************/
+
+static int build_regex(AVFilterContext *avctx, const char *pattern, std::regex 
*out)
+{
+    if (!pattern)
+        return 0;
+
+    std::string pat(pattern);
+
+    auto flags = std::regex::ECMAScript | std::regex::optimize;
+    if (pat.rfind("(?i)", 0) == 0 || pat.rfind("(?I)", 0) == 0) {
+        pat.erase(0, 4);
+        flags |= std::regex::icase;
+    } else if(pat.rfind("(?c)", 0) == 0 || pat.rfind("(?C)", 0) == 0) {
+        pat.erase(0, 4);
+    }
+
+    try {
+        *out = std::regex(pat, flags);
+    } catch (const std::regex_error &e) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to compile regex '%s': %s\n", 
pat.c_str(), e.what());
+        return AVERROR(EINVAL);
+    }
+
+    av_log(avctx, AV_LOG_DEBUG, "Built regex: %s\n", pattern);
+
+    return 0;
+}
+
+static int wstring_to_utf8(const wchar_t *in, std::string *out)
+{
+    int utf8size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, 
nullptr, nullptr);
+    if (utf8size <= 0)
+        return AVERROR(EINVAL);
+
+    // 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
+    out->resize(utf8size - 1);
+
+    if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out->data(), utf8size, 
nullptr, nullptr) != utf8size)
+        return AVERROR_EXTERNAL;
+
+    return 0;
+}
+
+static int get_window_exe_name(HWND hwnd, std::string *out)
+{
+    out->clear();
+
+    DWORD pid = 0;
+    if (!GetWindowThreadProcessId(hwnd, &pid))
+        return AVERROR(ENOENT);
+
+    handle_ptr_t proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 
pid));
+    if (!proc)
+        return AVERROR(EACCES);
+
+    std::wstring image_name;
+    DWORD image_name_size = 512;
+
+    for (;;) {
+        DWORD len = image_name_size;
+        image_name.resize(len);
+        if (QueryFullProcessImageNameW(proc.get(), 0, image_name.data(), 
&len)) {
+            image_name.resize(len);
+            break;
+        }
+        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+            image_name_size *= 2;
+            continue;
+        }
+        return AVERROR_EXTERNAL;
+    }
+
+    if (image_name.empty())
+        return AVERROR_EXTERNAL;
+
+    const wchar_t *base = image_name.c_str();
+    size_t pos = image_name.find_last_of(L"\\/");
+    if (pos != std::string::npos)
+        base += pos + 1;
+
+    return wstring_to_utf8(base, out);
+}
+
+static int find_capture_source(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    int cur_idx = 0;
+
+    ctx->capture_hwnd = NULL;
+    ctx->capture_hmonitor = NULL;
+
+    if (cctx->user_hmonitor) {
+        ctx->capture_hmonitor = (HMONITOR)(uintptr_t)cctx->user_hmonitor;
+        return 0;
+    } else if (cctx->user_hwnd) {
+        ctx->capture_hwnd = (HWND)(uintptr_t)cctx->user_hwnd;
+        return 0;
+    } else if (cctx->monitor_idx >= 0) {
+        auto cb = make_win32_callback([&](HMONITOR hmonitor, HDC, LPRECT) {
+            if (cur_idx++ == cctx->monitor_idx) {
+                av_log(avctx, AV_LOG_DEBUG, "Found capture monitor: %d\n", 
cctx->monitor_idx);
+                ctx->capture_hmonitor = hmonitor;
+                return FALSE;
+            }
+            return TRUE;
+        });
+        if (EnumDisplayMonitors(NULL, NULL, cb->proc, cb->lparam) || 
!ctx->capture_hmonitor)
+            return AVERROR(ENOENT);
+        return 0;
+    } else if (cctx->window_text || cctx->window_class || cctx->window_exe) {
+        std::regex text_regex;
+        if (build_regex(avctx, cctx->window_text, &text_regex) < 0)
+            return AVERROR(EINVAL);
+
+        std::regex class_regex;
+        if (build_regex(avctx, cctx->window_class, &class_regex) < 0)
+            return AVERROR(EINVAL);
+
+        std::regex exe_regex;
+        if (build_regex(avctx, cctx->window_exe, &exe_regex) < 0)
+            return AVERROR(EINVAL);
+
+        std::string window_text;
+        std::wstring window_text_w;
+        std::string window_class;
+        std::wstring window_class_w;
+        std::string window_exe;
+        auto cb = make_win32_callback([&](HWND hwnd) {
+            RECT r = { 0 };
+            if (!GetWindowRect(hwnd, &r) || r.right <= r.left || r.bottom <= 
r.top || !IsWindowVisible(hwnd))
+                return TRUE;
+
+            window_text_w.resize(GetWindowTextLengthW(hwnd) + 1);
+            int len = GetWindowTextW(hwnd, window_text_w.data(), 
(int)window_text_w.size());
+            if (len >= 0) {
+                window_text_w.resize(len);
+                if (wstring_to_utf8(window_text_w.c_str(), &window_text) < 0)
+                    window_text.clear();
+            } else {
+                window_text.clear();
+            }
+
+            window_class_w.resize(256);
+            len = GetClassNameW(hwnd, window_class_w.data(), 
(int)window_class_w.size());
+            if (len >= 0) {
+                window_class_w.resize(len);
+                if (wstring_to_utf8(window_class_w.c_str(), &window_class) < 0)
+                    window_class.clear();
+            } else {
+                window_class.clear();
+            }
+
+            get_window_exe_name(hwnd, &window_exe);
+
+            av_log(avctx, AV_LOG_TRACE, "Checking window: hwnd=%p text=%s 
class=%s exe=%s\n",
+                   hwnd, window_text.c_str(), window_class.c_str(), 
window_exe.c_str());
+
+            if (cctx->window_text) {
+                if (window_text.empty() || !std::regex_search(window_text, 
text_regex))
+                    return TRUE;
+            }
+
+            if (cctx->window_class) {
+                if (window_class.empty() || !std::regex_search(window_class, 
class_regex))
+                    return TRUE;
+            }
+
+            if (cctx->window_exe) {
+                if (window_exe.empty() || !std::regex_search(window_exe, 
exe_regex))
+                    return TRUE;
+            }
+
+            av_log(avctx, AV_LOG_VERBOSE, "Found capture window: %s (Class: 
%s, Exe: %s)\n",
+                   window_text.c_str(), window_class.c_str(), 
window_exe.c_str());
+            ctx->capture_hwnd = hwnd;
+            return FALSE;
+        });
+        if (EnumWindows(cb->proc, cb->lparam) || !ctx->capture_hwnd)
+            return AVERROR(ENOENT);
+
+        if (cctx->monitor_idx == GFX_MONITOR_IDX_WINDOW) {
+            ctx->capture_hmonitor = MonitorFromWindow(ctx->capture_hwnd, 
MONITOR_DEFAULTTONEAREST);
+            ctx->capture_hwnd = NULL;
+            if (!ctx->capture_hmonitor) {
+                av_log(avctx, AV_LOG_ERROR, "Failed to get monitor for capture 
window\n");
+                return AVERROR(ENOENT);
+            }
+        }
+
+        return 0;
+    }
+
+    av_log(avctx, AV_LOG_ERROR, "No capture source specified\n");
+    return AVERROR(EINVAL);
+}
+
+static av_cold void gfxcapture_uninit(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+    if (!ctx)
+        return;
+
+    stop_wgc_thread(avctx);
+
+    ctx->d3d.reset();
+
+    av_buffer_unref(&ctx->frames_ref);
+    av_buffer_unref(&ctx->device_ref);
+
+    delete ctx;
+    cctx->ctx = nullptr;
+}
+
+template<typename T>
+static av_cold void GetProcAddressTyped(const hmodule_ptr_t &hModule, LPCSTR 
lpProcName, T *out) {
+    *out = reinterpret_cast<T>(GetProcAddress(hModule.get(), lpProcName));
+}
+
+static av_cold int load_functions(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+#define LOAD_DLL(handle, name) \
+    handle = hmodule_ptr_t(LoadLibraryExW(L##name, NULL, 
LOAD_LIBRARY_SEARCH_SYSTEM32)); \
+    if (!handle) { \
+        av_log(avctx, AV_LOG_ERROR, "Failed opening " #name "\n"); \
+        return AVERROR(ENOSYS); \
+    }
+
+#define LOAD_FUNC(handle, name) \
+    GetProcAddressTyped(handle, #name, &ctx->fn.name); \
+    if (!ctx->fn.name) { \
+        av_log(avctx, AV_LOG_ERROR, "Failed loading " #name "\n"); \
+        return AVERROR(ENOSYS); \
+    }
+
+    // this handle is not used anywhere, but letting it get auto-freed during 
RoUninit causes crashes
+    LOAD_DLL(ctx->fn.graphicscapture_handle, "graphicscapture.dll");
+
+    LOAD_DLL(ctx->fn.combase_handle, "combase.dll");
+    LOAD_DLL(ctx->fn.dwmapi_handle, "dwmapi.dll");
+    LOAD_DLL(ctx->fn.d3d11_handle, "d3d11.dll");
+    LOAD_DLL(ctx->fn.coremsg_handle, "coremessaging.dll");
+    LOAD_DLL(ctx->fn.user32_handle, "user32.dll");
+    LOAD_DLL(ctx->fn.d3dcompiler_handle, "d3dcompiler_47.dll");
+
+    LOAD_FUNC(ctx->fn.combase_handle, RoInitialize);
+    LOAD_FUNC(ctx->fn.combase_handle, RoUninitialize);
+    LOAD_FUNC(ctx->fn.combase_handle, RoGetActivationFactory);
+    LOAD_FUNC(ctx->fn.combase_handle, WindowsCreateStringReference);
+
+    LOAD_FUNC(ctx->fn.dwmapi_handle, DwmGetWindowAttribute);
+
+    LOAD_FUNC(ctx->fn.d3d11_handle, CreateDirect3D11DeviceFromDXGIDevice);
+
+    LOAD_FUNC(ctx->fn.coremsg_handle, CreateDispatcherQueueController);
+
+    LOAD_FUNC(ctx->fn.user32_handle, SetThreadDpiAwarenessContext);
+
+    LOAD_FUNC(ctx->fn.d3dcompiler_handle, D3DCompile);
+
+#undef LOAD_FUNC
+#undef LOAD_DLL
+    return 0;
+}
+
+static av_cold int gfxcapture_init(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    int ret = 0;
+
+    GfxCaptureContextCpp *ctx = cctx->ctx = new GfxCaptureContextCpp();
+    ctx->d3d = std::make_unique<GfxCaptureContextD3D>();
+
+    ret = load_functions(avctx);
+    if (ret < 0) {
+        ctx->fn.RoUninitialize = nullptr;
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    gfxcapture_uninit(avctx);
+    return ret;
+}
+
+static int init_hwframes_ctx(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    int ret = 0;
+
+    ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
+    if (!ctx->frames_ref)
+        return AVERROR(ENOMEM);
+    ctx->frames_ctx = (AVHWFramesContext*)ctx->frames_ref->data;
+    ctx->frames_hwctx = (AVD3D11VAFramesContext*)ctx->frames_ctx->hwctx;
+
+    ctx->frames_ctx->format    = AV_PIX_FMT_D3D11;
+    ctx->frames_ctx->width     = cctx->canvas_width;
+    ctx->frames_ctx->height    = cctx->canvas_height;
+    ctx->frames_ctx->sw_format = (AVPixelFormat)cctx->out_fmt;
+    if (avctx->extra_hw_frames > 0)
+        ctx->frames_ctx->initial_pool_size = 8 + avctx->extra_hw_frames;
+
+    ctx->frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
+
+    ret = av_hwframe_ctx_init(ctx->frames_ref);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to initialise hardware frames 
context: %d.\n", ret);
+        goto fail;
+    }
+
+    return 0;
+fail:
+    av_buffer_unref(&ctx->frames_ref);
+    return ret;
+}
+
+static int setup_gfxcapture_capture(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+    int ret = 0;
+
+    stop_wgc_thread(avctx);
+
+    ret = find_capture_source(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to find capture source\n");
+        return ret;
+    }
+
+    ret = start_wgc_thread(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to start WGC thread\n");
+        return ret;
+    }
+
+    int cap_w = wgctx->cap_size.Width - cctx->crop_left - cctx->crop_right;
+    int cap_h = wgctx->cap_size.Height - cctx->crop_top - cctx->crop_bottom;
+
+    if (!cctx->capture_border) {
+        cap_w -= wgctx->client_area_offsets.left + 
wgctx->client_area_offsets.right;
+        cap_h -= wgctx->client_area_offsets.top + 
wgctx->client_area_offsets.bottom;
+    }
+
+    if (cctx->canvas_width == 0)
+        cctx->canvas_width = cap_w;
+    else if (cctx->canvas_width < 0)
+        cctx->canvas_width = (cap_w / cctx->canvas_width) * cctx->canvas_width;
+
+    if (cctx->canvas_height == 0)
+        cctx->canvas_height = cap_h;
+    else if (cctx->canvas_height < 0)
+        cctx->canvas_height = (cap_h / cctx->canvas_height) * 
cctx->canvas_height;
+
+    return 0;
+}
+
+static int prepare_render_resources(AVFilterContext *avctx)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
+    HRESULT hr;
+
+    ComPtr<ID3DBlob> vs_blob, ps_blob, err_blob;
+    CD3D11_SAMPLER_DESC sampler_desc(CD3D11_DEFAULT{});
+    UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
+
+    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);
+    if (FAILED(hr)) {
+        if (err_blob) {
+            av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: 
%.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
+        } else {
+            av_log(avctx, AV_LOG_ERROR, "Failed compiling vertex shader: 
0x%08lX\n", hr);
+        }
+        return AVERROR_EXTERNAL;
+    }
+
+    const char *ps_entry = "main_ps_bicubic";
+    if (cctx->resize_mode == GFX_RESIZE_CROP || cctx->scale_mode == 
GFX_SCALE_POINT) {
+        ps_entry = "main_ps";
+        sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+    }
+
+    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);
+    if (FAILED(hr)) {
+        if (err_blob) {
+            av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: 
%.*s\n", (int)err_blob->GetBufferSize(), (char*)err_blob->GetBufferPointer());
+        } else {
+            av_log(avctx, AV_LOG_ERROR, "Failed compiling pixel shader: 
0x%08lX\n", hr);
+        }
+        return AVERROR_EXTERNAL;
+    }
+
+    
CHECK_HR_RET(ctx->device_hwctx->device->CreateVertexShader(vs_blob->GetBufferPointer(),
 vs_blob->GetBufferSize(), NULL, &d3dctx->vertex_shader));
+    
CHECK_HR_RET(ctx->device_hwctx->device->CreatePixelShader(ps_blob->GetBufferPointer(),
 ps_blob->GetBufferSize(), NULL, &d3dctx->pixel_shader));
+
+    CHECK_HR_RET(ctx->device_hwctx->device->CreateSamplerState(&sampler_desc, 
&d3dctx->sampler_state));
+
+    D3D11_BUFFER_DESC cb_desc = { 0 };
+    cb_desc.ByteWidth = 48;
+    cb_desc.Usage = D3D11_USAGE_DYNAMIC;
+    cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+    cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+    CHECK_HR_RET(ctx->device_hwctx->device->CreateBuffer(&cb_desc, NULL, 
&d3dctx->shader_cb));
+
+    CHECK_HR_RET(ctx->device_hwctx->device->CreateDeferredContext(0, 
&d3dctx->deferred_ctx));
+
+    return 0;
+}
+
+static int gfxcapture_config_props(AVFilterLink *outlink)
+{
+    AVFilterContext *avctx = outlink->src;
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+
+    FilterLink *link = ff_filter_link(outlink);
+    int ret;
+
+    if (avctx->hw_device_ctx) {
+        ctx->device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
+
+        if (ctx->device_ctx->type != AV_HWDEVICE_TYPE_D3D11VA) {
+            av_log(avctx, AV_LOG_ERROR, "Non-D3D11VA input hw_device_ctx\n");
+            return AVERROR(EINVAL);
+        }
+
+        ctx->device_ref = av_buffer_ref(avctx->hw_device_ctx);
+        if (!ctx->device_ref)
+            return AVERROR(ENOMEM);
+
+        av_log(avctx, AV_LOG_VERBOSE, "Using provided hw_device_ctx\n");
+    } else {
+        ret = av_hwdevice_ctx_create(&ctx->device_ref, 
AV_HWDEVICE_TYPE_D3D11VA, NULL, NULL, 0);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to create D3D11VA device.\n");
+            return ret;
+        }
+
+        ctx->device_ctx = (AVHWDeviceContext*)ctx->device_ref->data;
+
+        av_log(avctx, AV_LOG_VERBOSE, "Created internal hw_device_ctx\n");
+    }
+
+    ctx->device_hwctx = (AVD3D11VADeviceContext*)ctx->device_ctx->hwctx;
+
+    ret = prepare_render_resources(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to prepare render resources\n");
+        return ret;
+    }
+
+    ret = setup_gfxcapture_capture(avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to setup graphics capture\n");
+        return ret;
+    }
+
+    ret = init_hwframes_ctx(avctx);
+    if (ret < 0)
+        return ret;
+
+    link->hw_frames_ctx = av_buffer_ref(ctx->frames_ref);
+    if (!link->hw_frames_ctx)
+        return AVERROR(ENOMEM);
+
+    std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
+    if (!ctx->wgc) {
+        av_log(avctx, AV_LOG_ERROR, "WGC thread died prematurely\n");
+        return AVERROR(ENOSYS);
+    }
+
+    outlink->w = ctx->frames_ctx->width;
+    outlink->h = ctx->frames_ctx->height;
+    outlink->time_base = AVRational{1, TIMESPAN_RES};
+    outlink->alpha_mode = cctx->premult_alpha ? AVALPHA_MODE_PREMULTIPLIED : 
AVALPHA_MODE_STRAIGHT;
+    link->frame_rate = cctx->frame_rate;
+
+    av_log(avctx, AV_LOG_DEBUG, "Capture setup with res %dx%d\n", outlink->w, 
outlink->h);
+
+    return 0;
+}
+
+static int render_capture_to_frame(AVFilterContext *avctx, AVFrame *frame, 
const ComPtr<ID3D11Texture2D> &src_tex)
+{
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextD3D> &d3dctx = ctx->d3d;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    ID3D11Device *dev = ctx->device_hwctx->device;
+    ID3D11DeviceContext *dev_ctx = ctx->device_hwctx->device_context;
+    ComPtr<ID3D11DeviceContext> &def_ctx = d3dctx->deferred_ctx;
+
+    D3D11_TEXTURE2D_DESC dst_tex_desc;
+    reinterpret_cast<ID3D11Texture2D*>(frame->data[0])->GetDesc(&dst_tex_desc);
+
+    D3D11_TEXTURE2D_DESC src_tex_desc;
+    src_tex->GetDesc(&src_tex_desc);
+
+    D3D11_RENDER_TARGET_VIEW_DESC target_desc = {};
+    target_desc.Format = dst_tex_desc.Format;
+
+    if (dst_tex_desc.ArraySize > 1) {
+        target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+        target_desc.Texture2DArray.ArraySize = 1;
+        target_desc.Texture2DArray.FirstArraySlice = (uintptr_t)frame->data[1];
+        target_desc.Texture2DArray.MipSlice = 0;
+    } else {
+        target_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+        target_desc.Texture2D.MipSlice = 0;
+    }
+
+    ComPtr<ID3D11RenderTargetView> rtv;
+    CHECK_HR_RET(dev->CreateRenderTargetView(
+        reinterpret_cast<ID3D11Resource*>(frame->data[0]), &target_desc, 
&rtv));
+
+    ComPtr<ID3D11ShaderResourceView> srv;
+    CHECK_HR_RET(dev->CreateShaderResourceView(src_tex.Get(), nullptr, &srv));
+
+    int crop_left = cctx->crop_left;
+    int crop_top = cctx->crop_top;
+    int crop_right = cctx->crop_right;
+    int crop_bottom = cctx->crop_bottom;
+
+    if (!cctx->capture_border) {
+        crop_left   += wgctx->client_area_offsets.left;
+        crop_top    += wgctx->client_area_offsets.top;
+        crop_right  += wgctx->client_area_offsets.right;
+        crop_bottom += wgctx->client_area_offsets.bottom;
+    }
+
+    // Using the actual capture frame size here adjusts for jank that can 
happen during rapid
+    // resizing of the source window. The capture frame pool is only recreated 
once a frame
+    // of changed size came out of it, so we need to cut/pad such frames to 
fit.
+    // Just discarding such frames can lead to visible stutter if the source 
window is being
+    // resized continuously, so this code does its best to adjust them 
instead. With the risk
+    // of slight clamping artifacts when enlarging rapidly.
+    int cropped_w = wgctx->cap_size.Width - crop_left - crop_right;
+    int cropped_h = wgctx->cap_size.Height - crop_top - crop_bottom;
+
+    D3D11_VIEWPORT viewport = { 0 };
+    viewport.MinDepth = 0.f;
+    viewport.MaxDepth = 1.f;
+
+    switch (cctx->resize_mode) {
+    case GFX_RESIZE_CROP:
+        viewport.Width  = (float)cropped_w;
+        viewport.Height = (float)cropped_h;
+        break;
+    case GFX_RESIZE_SCALE:
+        viewport.Width  = dst_tex_desc.Width;
+        viewport.Height = dst_tex_desc.Height;
+        break;
+    case GFX_RESIZE_SCALE_ASPECT: {
+        float scale = FFMIN(dst_tex_desc.Width / (float)cropped_w,
+                            dst_tex_desc.Height / (float)cropped_h);
+        viewport.Width  = cropped_w * scale;
+        viewport.Height = cropped_h * scale;
+        break;
+    }
+    default:
+        av_log(avctx, AV_LOG_ERROR, "Invalid scaling mode\n");
+        return AVERROR_BUG;
+    };
+
+    def_ctx->RSSetViewports(1, &viewport);
+
+    D3D11_MAPPED_SUBRESOURCE map;
+    CHECK_HR_RET(def_ctx->Map(d3dctx->shader_cb.Get(), 0, 
D3D11_MAP_WRITE_DISCARD, 0, &map));
+    {
+        float *cb_f = static_cast<float*>(map.pData);
+        uint32_t *cb_u = static_cast<uint32_t*>(map.pData);
+        cb_f[0] = (float)cropped_w;
+        cb_f[1] = (float)cropped_h;
+        cb_f[2] = viewport.Width;
+        cb_f[3] = viewport.Height;
+        cb_f[4] = crop_left / (float)src_tex_desc.Width; // min_u
+        cb_f[5] = crop_top / (float)src_tex_desc.Height; // min_v
+        cb_f[6] = (crop_left + cropped_w) / (float)src_tex_desc.Width; // max_u
+        cb_f[7] = (crop_top + cropped_h) / (float)src_tex_desc.Height; // max_v
+        cb_u[8] = !cctx->premult_alpha; // to_unpremult
+        cb_u[9] = src_tex_desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT &&
+                  dst_tex_desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT; // 
to_srgb
+    }
+    def_ctx->Unmap(d3dctx->shader_cb.Get(), 0);
+
+    def_ctx->OMSetRenderTargets(1, rtv.GetAddressOf(), nullptr);
+
+    const float clear_color[4] = {0.f, 0.f, 0.f, 1.f};
+    def_ctx->ClearRenderTargetView(rtv.Get(), clear_color);
+
+    def_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    def_ctx->VSSetShader(d3dctx->vertex_shader.Get(), nullptr, 0);
+    def_ctx->VSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
+    def_ctx->PSSetShader(d3dctx->pixel_shader.Get(), nullptr, 0);
+    def_ctx->PSSetSamplers(0, 1, d3dctx->sampler_state.GetAddressOf());
+    def_ctx->PSSetShaderResources(0, 1, srv.GetAddressOf());
+    def_ctx->PSSetConstantBuffers(0, 1, d3dctx->shader_cb.GetAddressOf());
+
+    def_ctx->Draw(3, 0);
+
+    ComPtr<ID3D11CommandList> cmd_list;
+    CHECK_HR_RET(def_ctx->FinishCommandList(FALSE, &cmd_list));
+    dev_ctx->ExecuteCommandList(cmd_list.Get(), FALSE);
+
+    return 0;
+}
+
+static int process_frame_if_exists(AVFilterLink *outlink)
+{
+    AVFilterContext *avctx = outlink->src;
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    int ret;
+
+    AVFrame *frame = nullptr;
+
+    ret = run_on_wgc_thread(avctx, [&]() {
+        ComPtr<IDirect3D11CaptureFrame> capture_frame;
+        ComPtr<IDirect3DSurface> capture_surface;
+        ComPtr<IDirect3DDxgiInterfaceAccess> dxgi_interface_access;
+        ComPtr<ID3D11Texture2D> frame_texture;
+        TimeSpan frame_time = { 0 };
+
+        ret = wgc_try_get_next_frame(avctx, &capture_frame);
+        if (ret < 0)
+            return ret;
+
+        CHECK_HR_RET(capture_frame->get_SystemRelativeTime(&frame_time));
+
+        CHECK_HR_RET(capture_frame->get_Surface(&capture_surface));
+        CHECK_HR_RET(capture_surface.As(&dxgi_interface_access));
+        
CHECK_HR_RET(dxgi_interface_access->GetInterface(IID_PPV_ARGS(&frame_texture)));
+
+        if (!frame_texture)
+            return AVERROR(EAGAIN);
+
+        frame = ff_get_video_buffer(outlink, cctx->canvas_width, 
cctx->canvas_height);
+        if (!frame)
+            return AVERROR(ENOMEM);
+
+        frame->pts = frame_time.Duration;
+
+        return render_capture_to_frame(avctx, frame, frame_texture);
+    });
+    if (ret < 0)
+        return ret;
+
+    frame->sample_aspect_ratio = AVRational{1, 1};
+
+    if (ctx->frames_ctx->sw_format == AV_PIX_FMT_RGBAF16) {
+        // According to MSDN, all floating point formats contain sRGB image 
data with linear 1.0 gamma.
+        frame->color_range     = AVCOL_RANGE_JPEG;
+        frame->color_primaries = AVCOL_PRI_BT709;
+        frame->color_trc       = AVCOL_TRC_LINEAR;
+        frame->colorspace      = AVCOL_SPC_RGB;
+    } else {
+        // According to MSDN, all integer formats contain sRGB image data
+        frame->color_range     = AVCOL_RANGE_JPEG;
+        frame->color_primaries = AVCOL_PRI_BT709;
+        frame->color_trc       = AVCOL_TRC_IEC61966_2_1;
+        frame->colorspace      = AVCOL_SPC_RGB;
+    }
+
+    ctx->last_pts = frame->pts;
+
+    if (!ctx->first_pts)
+        ctx->first_pts = frame->pts;
+    frame->pts -= ctx->first_pts;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+static int gfxcapture_activate(AVFilterContext *avctx)
+{
+    AVFilterLink *outlink = avctx->outputs[0];
+    GfxCaptureContext *cctx = CCTX(avctx->priv);
+    GfxCaptureContextCpp *ctx = cctx->ctx;
+    std::unique_ptr<GfxCaptureContextWgc> &wgctx = ctx->wgc;
+
+    std::lock_guard wgc_lock(ctx->wgc_thread_uninit_mutex);
+    if (!wgctx) {
+        av_log(avctx, AV_LOG_ERROR, "WGC thread not initialized\n");
+        return AVERROR(ENOSYS);
+    }
+
+    if (!ff_outlink_frame_wanted(outlink))
+        return FFERROR_NOT_READY;
+
+    std::unique_lock frame_lock(wgctx->frame_arrived_mutex);
+
+    for (;;) {
+        uint64_t last_seq = wgctx->frame_seq.load(std::memory_order_acquire);
+
+        int ret = process_frame_if_exists(outlink);
+        if (ret != AVERROR(EAGAIN))
+            return ret;
+
+        if (wgctx->window_closed.load(std::memory_order_acquire)) {
+            ff_outlink_set_status(outlink, AVERROR_EOF, ctx->last_pts - 
ctx->first_pts + 1);
+            break;
+        }
+
+        if (!wgctx->frame_arrived_cond.wait_for(frame_lock, 
std::chrono::seconds(1), [&]() {
+            return wgctx->frame_seq.load(std::memory_order_acquire) != 
last_seq ||
+                   wgctx->window_closed.load(std::memory_order_acquire);
+        }))
+            break;
+    }
+
+    return 0;
+}
+
+av_cold void ff_gfxcapture_uninit(AVFilterContext *avctx) noexcept
+{
+    try {
+        gfxcapture_uninit(avctx);
+    } catch (const std::exception &e) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit: %s\n", 
e.what());
+    } catch (...) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during uninit\n");
+    }
+}
+
+av_cold int ff_gfxcapture_init(AVFilterContext *avctx) noexcept
+{
+    try {
+        return gfxcapture_init(avctx);
+    } catch (const std::bad_alloc&) {
+        return AVERROR(ENOMEM);
+    } catch (const std::exception &e) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during init: %s\n", 
e.what());
+        return AVERROR_BUG;
+    } catch (...) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during init\n");
+        return AVERROR_BUG;
+    }
+}
+
+int ff_gfxcapture_activate(AVFilterContext *avctx) noexcept
+{
+    try {
+        return gfxcapture_activate(avctx);
+    } catch (const std::bad_alloc&) {
+        return AVERROR(ENOMEM);
+    } catch (const std::exception &e) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate: 
%s\n", e.what());
+        return AVERROR_BUG;
+    } catch (...) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during activate\n");
+        return AVERROR_BUG;
+    }
+}
+
+int ff_gfxcapture_config_props(AVFilterLink *outlink) noexcept
+{
+    AVFilterContext *avctx = outlink->src;
+
+    try {
+        return gfxcapture_config_props(outlink);
+    } catch (const std::bad_alloc&) {
+        return AVERROR(ENOMEM);
+    } catch (const std::exception &e) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during config_props: 
%s\n", e.what());
+        return AVERROR_BUG;
+    } catch (...) {
+        av_log(avctx, AV_LOG_ERROR, "unhandled exception during 
config_props\n");
+        return AVERROR_BUG;
+    }
+}
diff --git a/libavfilter/vsrc_gfxcapture_winrt.h 
b/libavfilter/vsrc_gfxcapture_winrt.h
new file mode 100644
index 0000000000..d6f11e4ef5
--- /dev/null
+++ b/libavfilter/vsrc_gfxcapture_winrt.h
@@ -0,0 +1,189 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFILTER_VSRC_GFXCAPTURE_WINRT_H
+#define AVFILTER_VSRC_GFXCAPTURE_WINRT_H
+
+// Forward-declare IDirect3DDxgiInterfaceAccess if headers too old
+#if !HAVE_IDIRECT3DDXGIINTERFACEACCESS
+namespace Windows::Graphics::DirectX::Direct3D11 {
+    MIDL_INTERFACE("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")
+    IDirect3DDxgiInterfaceAccess : public IUnknown
+    {
+        public:
+        IFACEMETHOD(GetInterface)(REFIID iid, _COM_Outptr_ void** p) = 0;
+    };
+}
+#ifdef __MINGW32__
+__CRT_UUID_DECL(Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess,
+                0xa9b3d012, 0x3df2, 0x4ee3, 0xb8, 0xd1, 0x86, 0x95, 0xf4, 
0x57, 0xd3, 0xc1)
+#endif
+#endif /* !HAVE_IDIRECT3DDXGIINTERFACEACCESS */
+
+// Forward-declare IGraphicsCaptureSession5 if headers too old
+#if !HAVE___X_ABI_CWINDOWS_CGRAPHICS_CCAPTURE_CIGRAPHICSCAPTURESESSION5
+namespace ABI::Windows ::Graphics::Capture {
+    MIDL_INTERFACE("67C0EA62-1F85-5061-925A-239BE0AC09CB")
+    IGraphicsCaptureSession5 : public IInspectable
+    {
+        public:
+        IFACEMETHOD(get_MinUpdateInterval)(ABI::Windows::Foundation::TimeSpan* 
value) = 0;
+        IFACEMETHOD(put_MinUpdateInterval)(ABI::Windows::Foundation::TimeSpan 
value) = 0;
+    };
+}
+#ifdef __MINGW32__
+__CRT_UUID_DECL(ABI::Windows ::Graphics::Capture::IGraphicsCaptureSession5,
+                0x67c0ea62, 0x1f85, 0x5061, 0x92, 0x5a, 0x23, 0x9b, 0xe0, 
0xac, 0x09, 0xcb)
+#endif
+#endif /* !HAVE___X_ABI_CWINDOWS_CGRAPHICS_CCAPTURE_CIGRAPHICSCAPTURESESSION5 
*/
+
+/****************************************************
+ * Helper class to implement refcounted COM objects *
+ ****************************************************/
+template<typename... Interfaces>
+struct FFComObject : Interfaces...
+{
+    virtual ~FFComObject() = default;
+
+    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) 
override
+    {
+        if (!ppvObject)
+            return E_POINTER;
+
+        if (query_all<Interfaces...>(riid, ppvObject))
+        {
+            AddRef();
+            return S_OK;
+        }
+
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+
+    ULONG STDMETHODCALLTYPE AddRef() override
+    {
+        return ++ref_count;
+    }
+
+    ULONG STDMETHODCALLTYPE Release() override
+    {
+        ULONG rc = --ref_count;
+        if (rc == 0)
+            delete this;
+        return rc;
+    }
+
+private:
+    template <typename Iface, typename... IFaces>
+    bool query_all(REFIID riid, void** ppvObject)
+    {
+        if (riid == __uuidof(Iface)) {
+            *ppvObject = static_cast<Iface*>(this);
+            return true;
+        }
+        if constexpr (sizeof...(IFaces)) {
+            return query_all<IFaces...>(riid, ppvObject);
+        } else if (riid == __uuidof(IUnknown)) {
+            *ppvObject = static_cast<IUnknown*>(static_cast<Iface*>(this));
+            return true;
+        }
+        return false;
+    }
+
+    std::atomic<ULONG> ref_count { 1 };
+};
+
+/*******************************************
+ * Helper to implement COM/WinRT callbacks *
+ *******************************************/
+template<class Iface, typename F, typename... Args>
+struct FFTypedCBHandler : FFComObject<Iface, IAgileObject>
+{
+    using Func = std::decay_t<F>;
+
+    explicit FFTypedCBHandler(F&& f) : cb_func(std::forward<F>(f)) {}
+
+    std::invoke_result_t<Func&, Args...> STDMETHODCALLTYPE Invoke(Args... 
args) override
+    {
+        if constexpr (std::is_same_v<std::invoke_result_t<Func&, Args...>, 
HRESULT>) {
+            return cb_func(std::forward<Args>(args)...);
+        } else {
+            cb_func(std::forward<Args>(args)...);
+            return S_OK;
+        }
+    }
+
+private:
+    Func cb_func;
+};
+
+template<class Iface, typename... Args, typename F>
+static Microsoft::WRL::ComPtr<Iface> create_cb_handler(F&& cb_func)
+{
+    return Microsoft::WRL::ComPtr<Iface>(
+        new FFTypedCBHandler<Iface, F, Args...>(std::forward<F>(cb_func))
+    );
+}
+
+/******************************************
+ * Helpers to implement C style callbacks *
+ ******************************************/
+template <typename Ret, typename... Args>
+struct Win32Callback {
+    std::function<Ret(Args...)> fn;
+    static Ret CALLBACK thunk(Args... args, LPARAM lparam) {
+        auto self = reinterpret_cast<Win32Callback*>(lparam);
+        return self->fn(std::forward<Args>(args)...);
+    }
+    decltype(&Win32Callback::thunk) proc = &Win32Callback::thunk;
+    LPARAM lparam = 0;
+};
+
+template <typename Ret, typename... Args>
+auto make_win32_callback(const std::function<Ret(Args...)> &&fn) {
+    using T = Win32Callback<Ret, Args...>;
+    auto res = std::make_unique<T>(T{ std::forward<decltype(fn)>(fn) });
+    res->lparam = reinterpret_cast<LPARAM>(res.get());
+    return res;
+}
+#define make_win32_callback(...) 
make_win32_callback(std::function(__VA_ARGS__))
+
+/*****************************
+ * Small convenience helpers *
+ *****************************/
+struct HMODULEDeleter {
+    typedef HMODULE pointer;
+    void operator()(HMODULE handle) const {
+        if (handle)
+            FreeLibrary(handle);
+    }
+};
+typedef std::unique_ptr<HMODULE, HMODULEDeleter> hmodule_ptr_t;
+
+struct HANDLEDeleter {
+    typedef HANDLE pointer;
+    void operator()(HANDLE handle) const {
+        if (handle && handle != INVALID_HANDLE_VALUE)
+            CloseHandle(handle);
+    }
+};
+typedef std::unique_ptr<HANDLE, HANDLEDeleter> handle_ptr_t;
+
+#define HLSL(shader) #shader
+
+#endif /* AVFILTER_VSRC_GFXCAPTURE_WINRT_H */

commit 7856f57533b14c18e888e275770a0ca26685d6e4
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Sun Sep 7 22:03:50 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    compat/w32pthreads: fix compatibility with C++

diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h
index 6f2734b470..61f3dfcdd9 100644
--- a/compat/w32pthreads.h
+++ b/compat/w32pthreads.h
@@ -46,7 +46,7 @@
 #include "libavutil/time.h"
 #include "libavutil/wchar_filename.h"
 
-typedef struct pthread_t {
+typedef struct w32pthread_t {
     void *handle;
     void *(*func)(void* arg);
     void *arg;
@@ -85,7 +85,7 @@ static av_unused int pthread_create(pthread_t *thread, const 
void *unused_attr,
 {
     pthread_t ret;
 
-    ret = av_mallocz(sizeof(*ret));
+    ret = (pthread_t)av_mallocz(sizeof(*ret));
     if (!ret)
         return EAGAIN;
 

commit 5146b2fb8b700c5376244859dcd1731e4470e3b7
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Tue Sep 2 01:00:24 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    configure: don't use MinGW ANSI stdio when using UCRT
    
    MinGWs ANSI stdio is quite slow, and when using UCRT, its extra features
    are not needed.
    The only troublesome part with ucrt printf is that is disagrees about
    the size of "long double", but FFmpeg does not use that anywhere, let
    alone prints it.

diff --git a/configure b/configure
index 2db65d0153..2364e00d08 100755
--- a/configure
+++ b/configure
@@ -6200,7 +6200,9 @@ probe_libc(){
             add_allcflags "-include $source_path/compat/msvcrt/snprintf.h"
         fi
         add_${pfx}cflags -U__STRICT_ANSI__
-        add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
+        if ! test_${pfx}cpp_condition crtdefs.h "defined(_UCRT)"; then
+            add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
+        fi
         eval test \$${pfx_no_}cc_type = "gcc" &&
             add_${pfx}cppflags -D__printf__=__gnu_printf__
         test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || 
_WIN32_WINNT < 0x0600" &&
@@ -6213,7 +6215,9 @@ probe_libc(){
             (__MINGW32_MAJOR_VERSION == 3 && __MINGW32_MINOR_VERSION >= 15)" ||
             die "ERROR: MinGW32 runtime version must be >= 3.15."
         add_${pfx}cflags -U__STRICT_ANSI__
-        add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
+        if ! test_${pfx}cpp_condition crtdefs.h "defined(_UCRT)"; then
+            add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
+        fi
         test_${pfx}cpp_condition _mingw.h "__MSVCRT_VERSION__ < 0x0700" &&
             add_${pfx}cppflags -D__MSVCRT_VERSION__=0x0700
         test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || 
_WIN32_WINNT < 0x0600" &&

commit 9c8a6ac85c3e56f8168a7813e4c825aff7044939
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Thu Sep 4 20:51:58 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    configure: fix naming of some C++/cxx functions being mislabeled as cpp

diff --git a/configure b/configure
index 5f84299339..2db65d0153 100755
--- a/configure
+++ b/configure
@@ -1445,8 +1445,8 @@ check_func_headers(){
     } | test_ld "cc" "$@" && enable $funcs && enable_sanitized $headers
 }
 
-check_class_headers_cpp(){
-    log check_class_headers_cpp "$@"
+check_class_headers_cxx(){
+    log check_class_headers_cxx "$@"
     headers=$1
     classes=$2
     shift 2
@@ -1538,14 +1538,14 @@ check_lib(){
         enable $name && eval ${name}_extralibs="\$@"
 }
 
-check_lib_cpp(){
-    log check_lib_cpp "$@"
+check_lib_cxx(){
+    log check_lib_cxx "$@"
     name="$1"
     headers="$2"
     classes="$3"
     shift 3
     disable $name
-    check_class_headers_cpp "$headers" "$classes" "$@" &&
+    check_class_headers_cxx "$headers" "$classes" "$@" &&
         enable $name && eval ${name}_extralibs="\$@"
 }
 
@@ -1716,12 +1716,12 @@ require_cc(){
     check_cc "$@" || die "ERROR: $name failed"
 }
 
-require_cpp(){
-    log require_cpp "$@"
+require_cxx(){
+    log require_cxx "$@"
     name_version="$1"
     name="${1%% *}"
     shift
-    check_lib_cpp "$name" "$@" || die "ERROR: $name_version not found"
+    check_lib_cxx "$name" "$@" || die "ERROR: $name_version not found"
 }
 
 require_headers(){
@@ -7207,7 +7207,7 @@ enabled libtensorflow     && require libtensorflow 
tensorflow/c/c_api.h TF_Versi
 enabled libtesseract      && require_pkg_config libtesseract tesseract 
tesseract/capi.h TessBaseAPICreate
 enabled libtheora         && require libtheora theora/theoraenc.h th_info_init 
-ltheoraenc -ltheoradec -logg
 enabled libtls            && require_pkg_config libtls libtls tls.h 
tls_configure
-enabled libtorch          && check_cxxflags -std=c++17 && require_cpp libtorch 
torch/torch.h "torch::Tensor" -ltorch -lc10 -ltorch_cpu -lstdc++ -lpthread
+enabled libtorch          && check_cxxflags -std=c++17 && require_cxx libtorch 
torch/torch.h "torch::Tensor" -ltorch -lc10 -ltorch_cpu -lstdc++ -lpthread
 enabled libtwolame        && require libtwolame twolame.h twolame_init 
-ltwolame &&
                              { check_lib libtwolame twolame.h 
twolame_encode_buffer_float32_interleaved -ltwolame ||
                                die "ERROR: libtwolame must be installed and 
version must be >= 0.3.10"; }

commit 0ce413af9cc8040095a6b714600d6a8dceaca514
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Tue Sep 2 00:53:55 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    configure: properly split C/CXX and CPP flags
    
    Right now, CFLAGS like -std=c17 leak onto the C++ compilers commandline,
    leading to tons of warnings and even some errors.
    
    Undefining __STRICT_ANSI__ causes strong warnings from the stdlib
    headers. So only set it for C.

diff --git a/configure b/configure
index c6afcd7494..5f84299339 100755
--- a/configure
+++ b/configure
@@ -976,6 +976,12 @@ add_objcflags(){
     append OBJCFLAGS $($objcflags_filter "$@")
 }
 
+add_allcflags(){
+    add_cflags "$@"
+    add_cxxflags "$@"
+    add_objcflags "$@"
+}
+
 add_asflags(){
     append ASFLAGS $($asflags_filter "$@")
 }
@@ -1036,6 +1042,14 @@ cc_o(){
     eval printf '%s\\n' $CC_O
 }
 
+cxx_e(){
+    eval printf '%s\\n' $CXX_E
+}
+
+cxx_o(){
+    eval printf '%s\\n' $CXX_O
+}
+
 as_o(){
     eval printf '%s\\n' $AS_O
 }
@@ -1071,14 +1085,14 @@ test_cxx(){
     log test_cxx "$@"
     cat > $TMPCPP
     log_file $TMPCPP
-    test_cmd $cxx $CPPFLAGS $CFLAGS $CXXFLAGS "$@" $CXX_C -o $TMPO $TMPCPP
+    test_cmd $cxx $CPPFLAGS $CXXFLAGS "$@" $CXX_C $(cxx_o $TMPO) $TMPCPP
 }
 
 test_objcc(){
     log test_objcc "$@"
     cat > $TMPM
     log_file $TMPM
-    test_cmd $objcc -Werror=missing-prototypes $CPPFLAGS $CFLAGS $OBJCFLAGS 
"$@" $OBJCC_C $(cc_o $TMPO) $TMPM
+    test_cmd $objcc -Werror=missing-prototypes $CPPFLAGS $OBJCFLAGS "$@" 
$OBJCC_C $(cc_o $TMPO) $TMPM
 }
 
 test_nvcc(){
@@ -1288,14 +1302,19 @@ check_cflags(){
     test_cflags "$@" && add_cflags "$@"
 }
 
-check_cxxflags(){
-    log check_cxxflags "$@"
+test_cxxflags(){
+    log test_cxxflags "$@"
     set -- $($cflags_filter "$@")
-    test_cxx "$@" <<EOF && append CXXFLAGS "$@"
+    test_cxx "$@" <<EOF
 int x;
 EOF
 }
 
+check_cxxflags(){
+    log check_cxxflags "$@"
+    test_cxxflags "$@" && add_cxxflags "$@"
+}
+
 test_objcflags(){
     log test_objcflags "$@"
     set -- $($objcflags_filter "$@")
@@ -1309,6 +1328,14 @@ check_objcflags(){
     test_objcflags "$@" && add_objcflags "$@"
 }
 
+check_allcflags(){
+    allcret=0
+    check_cflags "$@" || allcret=$?
+    check_cxxflags "$@" || allcret=$?
+    check_objcflags "$@" || allcret=$?
+    return $allcret
+}
+
 test_ldflags(){
     log test_ldflags "$@"
     set -- $($ldflags_filter "$@")
@@ -4180,7 +4207,7 @@ mandir_default='${prefix}/share/man'
 ar_default="ar"
 cc_default="gcc"
 stdc_default="c17"
-stdcxx_default="c++11"
+stdcxx_default="c++17"
 cxx_default="g++"
 host_cc_default="gcc"
 doxygen_default="doxygen"
@@ -4648,28 +4675,28 @@ test -n "$valgrind" && toolchain="valgrind-memcheck"
 add_sanitizer_flags(){
     case "$1" in
         asan)
-            add_cflags  -fsanitize=address
+            add_allcflags -fsanitize=address
             add_ldflags -fsanitize=address
         ;;
         fuzz)
-            add_cflags  -fsanitize=fuzzer-no-link
+            add_allcflags -fsanitize=fuzzer-no-link
             add_ldflags -fsanitize=fuzzer-no-link
             : "${libfuzzer_path:=-fsanitize=fuzzer}"
         ;;
         lsan)
-            add_cflags  -fsanitize=leak
+            add_allcflags -fsanitize=leak
             add_ldflags -fsanitize=leak
         ;;
         msan)
-            add_cflags  -fsanitize=memory -fsanitize-memory-track-origins
+            add_allcflags -fsanitize=memory -fsanitize-memory-track-origins
             add_ldflags -fsanitize=memory
         ;;
         tsan)
-            add_cflags  -fsanitize=thread
+            add_allcflags -fsanitize=thread
             add_ldflags -fsanitize=thread
         ;;
         usan|ubsan)
-            add_cflags  -fsanitize=undefined
+            add_allcflags -fsanitize=undefined
             add_ldflags -fsanitize=undefined
         ;;
         ?*)
@@ -4685,7 +4712,7 @@ add_sanitizers(){
     for sanitizer; do
         add_sanitizer_flags "$sanitizer"
     done
-    add_cflags -fno-omit-frame-pointer
+    add_allcflags -fno-omit-frame-pointer
 }
 
 case "$toolchain" in
@@ -4700,7 +4727,7 @@ case "$toolchain" in
         cxx_default="g++"
         # In case of tsan with gcc, PIC has to be enabled
         if [ "${toolchain#gcc-}" = "tsan" ]; then
-            add_cflags  -fPIC
+            add_allcflags -fPIC
             add_ldflags -fPIC
         fi
     ;;
@@ -4751,18 +4778,18 @@ case "$toolchain" in
         TMPDIR=.
     ;;
     gcov)
-        add_cflags  -fprofile-arcs -ftest-coverage
+        add_allcflags  -fprofile-arcs -ftest-coverage
         add_ldflags -fprofile-arcs -ftest-coverage
     ;;
     llvm-cov)
-        add_cflags -fprofile-arcs -ftest-coverage
+        add_allcflags -fprofile-arcs -ftest-coverage
         add_ldflags --coverage
     ;;
     hardened)
         add_cppflags -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
-        add_cflags   -fno-strict-overflow -fstack-protector-all
+        add_allcflags -fno-strict-overflow -fstack-protector-all
         add_ldflags  -Wl,-z,relro -Wl,-z,now
-        add_cflags   -fPIE
+        add_allcflags -fPIE
         add_ldexeflags -fPIE -pie
     ;;
     ?*)
@@ -5058,9 +5085,9 @@ probe_cc(){
     _cc=$2
     first=$3
 
-    unset _type _ident _cc_c _cc_e _cc_o _flags _cflags
+    unset _type _ident _cc_c _cc_e _cc_o _flags _cflags _cxxflags
     unset _ld_o _ldflags _ld_lib _ld_path
-    unset _depflags _DEPCMD _DEPFLAGS
+    unset _depflags _DEPCMD _DEPFLAGS _DEPCCFLAGS _DEPCXXFLAGS
     _flags_filter=echo
 
     if $_cc --version 2>&1 | grep -q '^GNU assembler'; then
@@ -5207,7 +5234,9 @@ probe_cc(){
         else
             _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk 
'\''/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($$0, / /)) 
print "$@:", $$0 }'\'' > $(@:.o=.d)'
         fi
-        _DEPFLAGS='$(CPPFLAGS) $(CFLAGS) -showIncludes -Zs'
+        _DEPFLAGS='$(CPPFLAGS) -showIncludes -Zs'
+        _DEPCCFLAGS='$(CFLAGS)'
+        _DEPCXXFLAGS='$(CXXFLAGS)'
         _cflags_speed="-O2"
         _cflags_size="-O1"
         _cflags_noopt="-O1"
@@ -5223,6 +5252,7 @@ probe_cc(){
         _ld_lib='%.lib'
         _ld_path='-libpath:'
         _flags='-nologo'
+        _cxxflags='-Zc:__cplusplus -EHsc'
         disable stripping
     elif $_cc --version 2>/dev/null | grep -q ^cparser; then
         _type=cparser
@@ -5243,11 +5273,11 @@ set_ccvars(){
     eval ${1}_O=\${_cc_o-\${${1}_O}}
 
     if [ -n "$_depflags" ]; then
-        eval ${1}_DEPFLAGS=\$_depflags
+        eval "${1}_DEPFLAGS=\"\$_depflags\""
     else
-        eval ${1}DEP=\${_DEPCMD:-\$DEPCMD}
-        eval ${1}DEP_FLAGS=\${_DEPFLAGS:-\$DEPFLAGS}
-        eval DEP${1}FLAGS=\$_flags
+        eval "${1}DEP=\"\${_DEPCMD:-\$DEPCMD}\""
+        eval "${1}DEP_FLAGS=\"\${_DEPFLAGS:-\$DEPFLAGS} 
\${_DEP${1}FLAGS:-\$DEP${1}FLAGS}\""
+        eval "DEP${1}FLAGS=\"\$_flags\""
     fi
 }
 
@@ -5257,6 +5287,7 @@ cflags_speed=$_cflags_speed
 cflags_size=$_cflags_size
 cflags_noopt=$_cflags_noopt
 add_cflags $_flags $_cflags
+add_cxxflags $_flags $_cxxflags
 cc_ldflags=$_ldflags
 set_ccvars CC
 set_ccvars CXX
@@ -5303,8 +5334,11 @@ HOSTLD_O=${_ld_o-$HOSTLD_O}
 if [ -z "$CC_DEPFLAGS" ] && [ "$dep_cc" != "$cc" ]; then
     probe_cc depcc "$dep_cc"
     CCDEP=${_DEPCMD:-$DEPCMD}
+    CXXDEP=${CCDEP}
     CCDEP_FLAGS=${_DEPFLAGS:=$DEPFLAGS}
-    DEPCCFLAGS=$_flags
+    CXXDEP_FLAGS=${CCDEP_FLAGS}
+    DEPCCFLAGS=$_flags $_cflags
+    DEPCXXFLAGS=$_flags $_cxxflags
 fi
 
 if VSLANG=1033 $ar 2>&1 | grep -q ^Microsoft; then
@@ -5502,7 +5536,7 @@ elif enabled arm; then
             ;;
     esac
 
-    test_cflags -mfp16-format=ieee && add_cflags -mfp16-format=ieee
+    check_allcflags -mfp16-format=ieee
 
 elif enabled loongarch; then
 
@@ -5737,7 +5771,7 @@ else
 fi
 
 if [ "$cpu" != generic ]; then
-    add_cflags  $cpuflags
+    add_allcflags $cpuflags
     add_asflags $cpuflags
     test "$cc_type" = "$ld_type" && add_ldflags $cpuflags
 fi
@@ -5760,6 +5794,8 @@ add_cxxflags -D__STDC_CONSTANT_MACROS
 check_cxxflags_cc -std=$stdcxx ctype.h "__cplusplus >= 201103L" ||
     { check_cxxflags -std=c++11 && stdcxx="c++11" || { check_cxxflags 
-std=c++0x && stdcxx="c++0x"; }; }
 
+test_cxxflags_cc -std=$stdcxx ctype.h "__cplusplus >= 201703L" && enable cxx17
+
 # some compilers silently accept -std=c11, so we also need to check that the
 # version macro is defined properly
 check_cflags_cc -std=$stdc ctype.h "__STDC_VERSION__ >= 201112L" ||
@@ -5854,7 +5890,7 @@ case $target_os in
     android)
         disable symver
         enable section_data_rel_ro
-        add_cflags -fPIE
+        add_allcflags -fPIE
         add_ldexeflags -fPIE -pie
         SLIB_INSTALL_NAME='$(SLIBNAME)'
         SLIB_INSTALL_LINKS=
@@ -5935,7 +5971,7 @@ case $target_os in
         # Workaround for Xcode 11 -fstack-check bug
         if enabled clang; then
             clang_version=$($cc -dumpversion)
-            test ${clang_version%%.*} -eq 11 && add_cflags -fno-stack-check
+            test ${clang_version%%.*} -eq 11 && add_allcflags -fno-stack-check
         fi
 
         # Xcode Clang doesn't default to -fno-common while upstream llvm.org
@@ -6154,15 +6190,17 @@ probe_libc(){
     # MinGW headers can be installed on Cygwin, so check for newlib first.
     elif test_${pfx}cpp_condition newlib.h "defined _NEWLIB_VERSION"; then
         eval ${pfx}libc_type=newlib
-        add_${pfx}cppflags -U__STRICT_ANSI__ -D_XOPEN_SOURCE=600
+        add_${pfx}cflags -U__STRICT_ANSI__
+        add_${pfx}cppflags -D_XOPEN_SOURCE=600
     # MinGW64 is backwards compatible with MinGW32, so check for it first.
     elif test_${pfx}cpp_condition _mingw.h "defined __MINGW64_VERSION_MAJOR"; 
then
         eval ${pfx}libc_type=mingw64
         if test_${pfx}cpp_condition _mingw.h "__MINGW64_VERSION_MAJOR < 3"; 
then
             add_compat msvcrt/snprintf.o
-            add_cflags "-include $source_path/compat/msvcrt/snprintf.h"
+            add_allcflags "-include $source_path/compat/msvcrt/snprintf.h"
         fi
-        add_${pfx}cppflags -U__STRICT_ANSI__ -D__USE_MINGW_ANSI_STDIO=1
+        add_${pfx}cflags -U__STRICT_ANSI__
+        add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
         eval test \$${pfx_no_}cc_type = "gcc" &&
             add_${pfx}cppflags -D__printf__=__gnu_printf__
         test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || 
_WIN32_WINNT < 0x0600" &&
@@ -6174,7 +6212,8 @@ probe_libc(){
         test_${pfx}cpp_condition _mingw.h "__MINGW32_MAJOR_VERSION > 3 || \
             (__MINGW32_MAJOR_VERSION == 3 && __MINGW32_MINOR_VERSION >= 15)" ||
             die "ERROR: MinGW32 runtime version must be >= 3.15."
-        add_${pfx}cppflags -U__STRICT_ANSI__ -D__USE_MINGW_ANSI_STDIO=1
+        add_${pfx}cflags -U__STRICT_ANSI__
+        add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1
         test_${pfx}cpp_condition _mingw.h "__MSVCRT_VERSION__ < 0x0700" &&
             add_${pfx}cppflags -D__MSVCRT_VERSION__=0x0700
         test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || 
_WIN32_WINNT < 0x0600" &&
@@ -6215,8 +6254,8 @@ probe_libc(){
 #endif
 EOF
         if [ "$pfx" = "" ]; then
-            check_func strtoll || add_cflags -Dstrtoll=_strtoi64
-            check_func strtoull || add_cflags -Dstrtoull=_strtoui64
+            check_func strtoll || add_allcflags -Dstrtoll=_strtoi64
+            check_func strtoull || add_allcflags -Dstrtoull=_strtoui64
         fi
     elif test_${pfx}cpp_condition stddef.h "defined __KLIBC__"; then
         eval ${pfx}libc_type=klibc
@@ -6228,7 +6267,7 @@ EOF
     elif test_${pfx}cpp_condition sys/version.h "defined __DJGPP__"; then
         eval ${pfx}libc_type=djgpp
         add_cppflags -U__STRICT_ANSI__
-        add_cflags "-include $source_path/compat/djgpp/math.h"
+        add_allcflags "-include $source_path/compat/djgpp/math.h"
         add_compat djgpp/math.o
     fi
     test_${pfx}cc <<EOF
@@ -6266,7 +6305,7 @@ set_default libdir
 set_default $PATHS_LIST
 set_default nm
 
-disabled optimizations || enabled ossfuzz || echo "$CFLAGS" | grep -q -- 
'-fsanitize=' || check_cflags -fomit-frame-pointer
+disabled optimizations || enabled ossfuzz || echo "$CFLAGS" | grep -q -- 
'-fsanitize=' || check_allcflags -fomit-frame-pointer
 
 enable_weak_pic() {
     disabled pic && return
@@ -6276,7 +6315,7 @@ enable_weak_pic() {
     mingw*|cygwin*|win*)
         ;;
     *)
-        add_cflags -fPIC
+        add_allcflags -fPIC
         add_asflags -fPIC
         ;;
     esac
@@ -6952,13 +6991,13 @@ fi
 if ! disabled pthreads && ! enabled w32threads && ! enabled os2threads; then
     if check_lib pthreads pthread.h pthread_join   -pthread &&
        check_lib pthreads pthread.h pthread_create -pthread; then
-        add_cflags -pthread
+        add_allcflags -pthread
     elif check_lib pthreads pthread.h pthread_join   -pthreads &&
          check_lib pthreads pthread.h pthread_create -pthreads; then
-        add_cflags -pthreads
+        add_allcflags -pthreads
     elif check_lib pthreads pthread.h pthread_join   -ldl -pthread &&
          check_lib pthreads pthread.h pthread_create -ldl -pthread; then
-        add_cflags -ldl -pthread
+        add_allcflags -ldl -pthread
     elif check_lib pthreads pthread.h pthread_join   -lpthreadGC2 &&
          check_lib pthreads pthread.h pthread_create -lpthreadGC2; then
         :
@@ -7097,7 +7136,7 @@ elif enabled libmfx; then
 #   includedir=/usr/include
 #   Cflags: -I${includedir}
 # So add -I${includedir}/mfx to CFLAGS
-      { check_pkg_config libmfx "libmfx >= 1.28 libmfx < 2.0" "mfx/mfxvideo.h" 
MFXInit && add_cflags -I${libmfx_incdir}/mfx; } ||
+      { check_pkg_config libmfx "libmfx >= 1.28 libmfx < 2.0" "mfx/mfxvideo.h" 
MFXInit && add_cppflags -I${libmfx_incdir}/mfx; } ||
       { require libmfx "mfxvideo.h mfxdefs.h" MFXInit "-llibmfx 
$advapi32_extralibs" &&
         { test_cpp_condition mfxdefs.h "MFX_VERSION >= 1028 && MFX_VERSION < 
2000" || die "ERROR: libmfx version must be >= 1.28 and < 2.0"; }  &&
         warn "using libmfx without pkg-config"; } } &&
@@ -7108,7 +7147,7 @@ elif enabled libvpl; then
 # is extracted from "vpl >= 2.6"
     check_pkg_config libmfx "vpl >= 2.6" "mfxvideo.h mfxdispatcher.h" MFXLoad 
|| \
             die "ERROR: libvpl >= 2.6 not found"
-    add_cflags -DMFX_DEPRECATED_OFF
+    add_cppflags -DMFX_DEPRECATED_OFF
     check_type "vpl/mfxdefs.h vpl/mfxvideo.h" "struct mfxConfigInterface"
 fi
 
@@ -7584,29 +7623,31 @@ elif enabled iconv; then
     check_func_headers iconv.h iconv || check_lib iconv iconv.h iconv -liconv
 fi
 
-enabled debug && add_cflags -g"$debuglevel" && add_asflags -g"$debuglevel"
+enabled debug && add_allcflags -g"$debuglevel" && add_asflags -g"$debuglevel"
 
 # add some useful compiler flags if supported
-check_cflags -Wall
-check_cflags -Wdisabled-optimization
-check_cflags -Wpointer-arith
-check_cflags -Wredundant-decls
-check_cflags -Wwrite-strings
-check_cflags -Wtype-limits
-check_cflags -Wundef
+check_allcflags -Wall
+check_allcflags -Wdisabled-optimization
+check_allcflags -Wpointer-arith
+check_allcflags -Wredundant-decls
+check_allcflags -Wwrite-strings
+check_allcflags -Wtype-limits
+check_allcflags -Wundef
+check_allcflags -Wempty-body
 check_cflags -Wmissing-prototypes
 check_cflags -Wstrict-prototypes
-check_cflags -Wempty-body
 
 if enabled extra_warnings; then
-    check_cflags -Wcast-qual
-    check_cflags -Wextra
-    check_cflags -Wpedantic
+    check_allcflags -Wcast-qual
+    check_allcflags -Wextra
+    check_allcflags -Wpedantic
 fi
 
 check_disable_warning(){
     warning_flag=-W${1#-Wno-}
     test_cflags $unknown_warning_flags $warning_flag && add_cflags $1
+    test_cxxflags -Werror $unknown_warning_flags $warning_flag && add_cxxflags 
$1
+    test_objcflags $unknown_warning_flags $warning_flag && add_objcflags $1
 }
 
 test_cflags -Werror=unused-command-line-argument &&
@@ -7700,7 +7741,7 @@ if [ -z "$optflags" ]; then
 fi
 
 check_optflags(){
-    check_cflags "$@"
+    check_allcflags "$@"
     [ -n "$lto" ] && check_ldflags "$@"
 }
 
@@ -7734,7 +7775,7 @@ EOF
 
 if enabled icc; then
     # Just warnings, no remarks
-    check_cflags -w1
+    check_allcflags -w1
     # -wd: Disable following warnings
     # 144, 167, 556: -Wno-pointer-sign
     # 188: enumerated type mixed with another type
@@ -7745,7 +7786,7 @@ if enabled icc; then
     # 10156: ignoring option '-W'; no argument required
     # 13200: No EMMS instruction before call to function
     # 13203: No EMMS instruction before return from function
-    check_cflags -wd144,167,188,556,1292,1419,10006,10148,10156,13200,13203
+    check_allcflags -wd144,167,188,556,1292,1419,10006,10148,10156,13200,13203
     # 11030: Warning unknown option --as-needed
     # 10156: ignoring option '-export'; no argument required
     check_ldflags -wd10156,11030
@@ -7756,7 +7797,7 @@ if enabled icc; then
     if enabled x86_32; then
         icc_version=$($cc -dumpversion)
         test ${icc_version%%.*} -ge 11 &&
-            check_cflags -falign-stack=maintain-16-byte ||
+            check_cppflags -falign-stack=maintain-16-byte ||
             disable aligned_stack
     fi
 elif enabled gcc; then
@@ -7779,13 +7820,13 @@ elif enabled gcc; then
             ;;
         esac
     fi
-    check_cflags -Werror=format-security
     check_cflags -Werror=implicit-function-declaration
     check_cflags -Werror=missing-prototypes
-    check_cflags -Werror=return-type
-    check_cflags -Werror=vla
-    check_cflags -Wformat
-    check_cflags -fdiagnostics-color=auto
+    check_allcflags -Werror=format-security
+    check_allcflags -Werror=return-type
+    check_allcflags -Werror=vla
+    check_allcflags -Wformat
+    check_allcflags -fdiagnostics-color=auto
     enabled extra_warnings || check_disable_warning -Wno-maybe-uninitialized
     if enabled x86_32; then
         case $target_os in
@@ -7794,12 +7835,12 @@ elif enabled gcc; then
             # request GCC to try to maintain 16 byte alignment throughout
             # function calls. Library entry points that might call assembly
             # functions align the stack. (The parameter means 2^4 bytes.)
-            check_cflags -mpreferred-stack-boundary=4
+            check_allcflags -mpreferred-stack-boundary=4
             ;;
         esac
     fi
 elif enabled llvm_gcc; then
-    check_cflags -mllvm -stack-alignment=16
+    check_allcflags -mllvm -stack-alignment=16
 elif enabled clang; then
     if enabled x86_32; then
         # Clang doesn't support maintaining alignment without assuming the
@@ -7812,18 +7853,18 @@ elif enabled clang; then
             disable aligned_stack
             ;;
         *)
-            check_cflags -mllvm -stack-alignment=16
-            check_cflags -mstack-alignment=16
+            check_allcflags -mllvm -stack-alignment=16
+            check_allcflags -mstack-alignment=16
             ;;
         esac
     else
-        check_cflags -mllvm -stack-alignment=16
-        check_cflags -mstack-alignment=16
+        check_allcflags -mllvm -stack-alignment=16
+        check_allcflags -mstack-alignment=16
     fi
-    check_cflags -Qunused-arguments
-    check_cflags -Werror=implicit-function-declaration
-    check_cflags -Werror=missing-prototypes
-    check_cflags -Werror=return-type
+    check_allcflags -Qunused-arguments
+    check_allcflags -Werror=implicit-function-declaration
+    check_allcflags -Werror=missing-prototypes
+    check_allcflags -Werror=return-type
 elif enabled cparser; then
     add_cflags -Wno-missing-variable-declarations
     add_cflags -Wno-empty-statement
@@ -8267,6 +8308,8 @@ OBJCC=$objcc
 LD=$ld
 DEPCC=$dep_cc
 DEPCCFLAGS=$DEPCCFLAGS \$(CPPFLAGS)
+DEPCXX=$dep_cc
+DEPCXXFLAGS=$DEPCXXFLAGS \$(CPPFLAGS)
 DEPAS=$as
 DEPASFLAGS=$DEPASFLAGS \$(CPPFLAGS)
 X86ASM=$x86asmexe
@@ -8334,6 +8377,7 @@ EXTRA_VERSION=$extra_version
 CCDEP=$CCDEP
 CXXDEP=$CXXDEP
 CCDEP_FLAGS=$CCDEP_FLAGS
+CXXDEP_FLAGS=$CXXDEP_FLAGS
 ASDEP=$ASDEP
 ASDEP_FLAGS=$ASDEP_FLAGS
 X86ASMDEP=$X86ASMDEP
diff --git a/ffbuild/common.mak b/ffbuild/common.mak
index 0dcf38bcfc..4ed1c44afb 100644
--- a/ffbuild/common.mak
+++ b/ffbuild/common.mak
@@ -51,7 +51,7 @@ OBJCCFLAGS  = $(CPPFLAGS) $(CFLAGS) $(OBJCFLAGS)
 ASFLAGS    := $(CPPFLAGS) $(ASFLAGS)
 # Use PREPEND here so that later (target-dependent) additions to CPPFLAGS
 # end up in CXXFLAGS.
-$(call PREPEND,CXXFLAGS, CPPFLAGS CFLAGS)
+$(call PREPEND,CXXFLAGS, CPPFLAGS)
 X86ASMFLAGS += $(IFLAGS:%=%/) -I$(<D)/ -Pconfig.asm
 
 HOSTCCFLAGS = $(IFLAGS) $(HOSTCPPFLAGS) $(HOSTCFLAGS)

commit 0362cb38062b2a8cead50840aaefb7257570e3f0
Author:     Timo Rothenpieler <t...@rothenpieler.org>
AuthorDate: Wed Sep 3 01:34:57 2025 +0200
Commit:     Timo Rothenpieler <t...@rothenpieler.org>
CommitDate: Sun Sep 14 11:45:11 2025 +0000

    build: link with CXX when -lstdc++ on linker commandline

diff --git a/Makefile b/Makefile
index 877b0071f6..17fadd58c2 100644
--- a/Makefile
+++ b/Makefile
@@ -53,31 +53,31 @@ FF_DEP_LIBS  := $(DEP_LIBS)
 FF_STATIC_DEP_LIBS := $(STATIC_DEP_LIBS)
 
 $(TOOLS): %$(EXESUF): %.o
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter-out $(FF_DEP_LIBS), $^) 
$(EXTRALIBS-$(*F)) $(EXTRALIBS) $(ELIBS)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter-out 
$(FF_DEP_LIBS), $^) $(EXTRALIBS-$(*F)) $(EXTRALIBS) $(ELIBS))
 
 target_dec_%_fuzzer$(EXESUF): target_dec_%_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 target_enc_%_fuzzer$(EXESUF): target_enc_%_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/target_bsf_%_fuzzer$(EXESUF): tools/target_bsf_%_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 target_dem_%_fuzzer$(EXESUF): target_dem_%_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o 
$(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/target_swr_fuzzer$(EXESUF): tools/target_swr_fuzzer.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) 
$(LIBFUZZER_PATH)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) 
$(FF_EXTRALIBS) $(LIBFUZZER_PATH))
 
 tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
 tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
@@ -144,7 +144,7 @@ else
 endif
 
 %$(PROGSSUF)_g$(EXESUF): $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS))
 
 VERSION_SH  = $(SRC_PATH)/ffbuild/version.sh
 ifeq ($(VERSION_TRACKING),yes)
diff --git a/ffbuild/common.mak b/ffbuild/common.mak
index 0a60d01623..0dcf38bcfc 100644
--- a/ffbuild/common.mak
+++ b/ffbuild/common.mak
@@ -12,13 +12,22 @@ endif
 
 ifndef SUBDIR
 
+LINK = $(LD) $(1)
+
+ifeq ($(LD),$(CC))
+ifneq ($(CXX),)
+LDXX := $(CXX)
+LINK = $(if $(filter -lstdc++,$(1)),$(LDXX) $(filter-out -lstdc++,$(1)),$(LD) 
$(1))
+endif
+endif
+
 BIN2CEXE = ffbuild/bin2c$(HOSTEXESUF)
 BIN2C = $(BIN2CEXE)
 
 ifndef V
 Q      = @
 ECHO   = printf "$(1)\t%s\n" $(2)
-BRIEF  = CC CXX OBJCC HOSTCC HOSTLD AS X86ASM AR LD STRIP CP WINDRES NVCC 
BIN2C METALCC METALLIB
+BRIEF  = CC CXX OBJCC HOSTCC HOSTLD AS X86ASM AR LD LDXX STRIP CP WINDRES NVCC 
BIN2C METALCC METALLIB
 SILENT = DEPCC DEPHOSTCC DEPAS DEPX86ASM RANLIB RM
 
 MSG    = $@
diff --git a/ffbuild/library.mak b/ffbuild/library.mak
index dee05c5acd..91daa9c25f 100644
--- a/ffbuild/library.mak
+++ b/ffbuild/library.mak
@@ -55,8 +55,12 @@ $(TESTPROGS): THISLIB = $(SUBDIR)$(LIBNAME)
 
 $(LIBOBJS): CPPFLAGS += -DBUILDING_$(NAME)
 
+$(NAME)LINK_EXE_ARGS = $(LDFLAGS) $(LDEXEFLAGS)
+$(NAME)LINK_SO_ARGS = $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS)
+$(NAME)LINK_EXTRA = $(FFEXTRALIBS)
+
 $(TESTPROGS) $(TOOLS): %$(EXESUF): %.o
-       $$(LD) $(LDFLAGS) $(LDEXEFLAGS) $$(LD_O) $$(filter %.o,$$^) $$(THISLIB) 
$(FFEXTRALIBS) $$(EXTRALIBS-$$(*F)) $$(ELIBS)
+       $$(call LINK,$$(call $(NAME)LINK_EXE_ARGS) $$(LD_O) $$(filter %.o,$$^) 
$$(THISLIB) $$(call $(NAME)LINK_EXTRA) $$(EXTRALIBS-$$(*F)) $$(ELIBS))
 
 $(SUBDIR)lib$(NAME).version: $(SUBDIR)version.h $(SUBDIR)version_major.h | 
$(SUBDIR)
        $$(M) $$(SRC_PATH)/ffbuild/libversion.sh $(NAME) $$^ > $$@
@@ -74,9 +78,9 @@ $(SUBDIR)$(SLIBNAME_WITH_MAJOR): $(OBJS) $(SHLIBOBJS) 
$(SUBDIR)lib$(NAME).ver
        $(SLIB_CREATE_DEF_CMD)
 ifeq ($(RESPONSE_FILES),yes)
        $(Q)echo $$(filter %.o,$$^) > $$@.objs
-       $$(LD) $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS) $$(LD_O) @$$@.objs 
$(FFEXTRALIBS)
+       $$(call LINK,$$(call $(NAME)LINK_SO_ARGS) $$(LD_O) @$$@.objs $$(call 
$(NAME)LINK_EXTRA))
 else
-       $$(LD) $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS) $$(LD_O) $$(filter %.o,$$^) 
$(FFEXTRALIBS)
+       $$(call LINK,$$(call $(NAME)LINK_SO_ARGS) $$(LD_O) $$(filter %.o,$$^) 
$$(call $(NAME)LINK_EXTRA))
 endif
        $(SLIB_EXTRA_CMD)
        -$(RM) $$@.objs
diff --git a/tests/api/Makefile b/tests/api/Makefile
index a2cb06a729..899aeb1f54 100644
--- a/tests/api/Makefile
+++ b/tests/api/Makefile
@@ -15,7 +15,7 @@ $(APITESTOBJS) $(APITESTOBJS:.o=.i): CPPFLAGS += -DTEST
 $(APITESTOBJS) $(APITESTOBJS:.o=.i): CFLAGS += -Umain
 
 $(APITESTPROGS): %$(EXESUF): %.o $(FF_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter %.o,$^) $(FF_EXTRALIBS) 
$(ELIBS)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter %.o,$^) 
$(FF_EXTRALIBS) $(ELIBS))
 
 testclean::
        $(RM) $(addprefix $(APITESTSDIR)/,$(CLEANSUFFIXES) *-test$(EXESUF))
diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
index 5ce4725543..9f1dd57fa6 100644
--- a/tests/checkasm/Makefile
+++ b/tests/checkasm/Makefile
@@ -107,7 +107,7 @@ tests/checkasm/checkasm.o: CFLAGS += -Umain
 CHECKASM := tests/checkasm/checkasm$(EXESUF)
 
 $(CHECKASM): $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
-       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) 
$(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) 
$(EXTRALIBS-avformat) $(EXTRALIBS-avutil) $(EXTRALIBS-swresample) $(EXTRALIBS)
+       $(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) 
$(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) 
$(EXTRALIBS-avformat) $(EXTRALIBS-avutil) $(EXTRALIBS-swresample) $(EXTRALIBS))
 
 run-checkasm: $(CHECKASM)
 run-checkasm:

-----------------------------------------------------------------------

Summary of changes:
 Changelog                             |    1 +
 Makefile                              |   20 +-
 compat/w32pthreads.h                  |    4 +-
 configure                             |  224 +++--
 doc/filters.texi                      |  159 ++++
 ffbuild/common.mak                    |   13 +-
 ffbuild/library.mak                   |   10 +-
 libavfilter/Makefile                  |    1 +
 libavfilter/allfilters.c              |    1 +
 libavfilter/version.h                 |    2 +-
 libavfilter/vsrc_gfxcapture.c         |  102 +++
 libavfilter/vsrc_gfxcapture.h         |   81 ++
 libavfilter/vsrc_gfxcapture_shader.h  |  126 +++
 libavfilter/vsrc_gfxcapture_winrt.cpp | 1551 +++++++++++++++++++++++++++++++++
 libavfilter/vsrc_gfxcapture_winrt.h   |  189 ++++
 tests/api/Makefile                    |    2 +-
 tests/checkasm/Makefile               |    2 +-
 17 files changed, 2384 insertions(+), 104 deletions(-)
 create mode 100644 libavfilter/vsrc_gfxcapture.c
 create mode 100644 libavfilter/vsrc_gfxcapture.h
 create mode 100644 libavfilter/vsrc_gfxcapture_shader.h
 create mode 100644 libavfilter/vsrc_gfxcapture_winrt.cpp
 create mode 100644 libavfilter/vsrc_gfxcapture_winrt.h


hooks/post-receive
-- 

_______________________________________________
ffmpeg-cvslog mailing list -- ffmpeg-cvslog@ffmpeg.org
To unsubscribe send an email to ffmpeg-cvslog-le...@ffmpeg.org

Reply via email to