win-capture: Windows Graphics Capture support
Users will now have the option of legacy window capture via BitBlt, or Windows Graphics Capture, which is new to Windows 10. There are two annoyances with the new capture method though. One is that there is a bright, yellow border added to the original window (but not the OBS view of it). The other is that the mouse cursor is always captured, and we won't be able to capture without cursor until a later version of Windows 10 is released. It should also be noted that DPI scaling is now applied, which may result in blurrier images caused by Windows rescaling.
This commit is contained in:
parent
d6d27cfab1
commit
ffc48dbbab
@ -1,6 +1,9 @@
|
||||
MonitorCapture="Display Capture"
|
||||
WindowCapture="Window Capture"
|
||||
WindowCapture.Window="Window"
|
||||
WindowCapture.Method="Capture Method"
|
||||
WindowCapture.Method.BitBlt="BitBlt (Windows 7 and up)"
|
||||
WindowCapture.Method.WindowsGraphicsCapture="Windows Graphics Capture (Windows 10 1903 and up)"
|
||||
WindowCapture.Priority="Window Match Priority"
|
||||
WindowCapture.Priority.Title="Window title must match"
|
||||
WindowCapture.Priority.Class="Match title, otherwise find window of same type"
|
||||
|
@ -2,11 +2,16 @@
|
||||
#include <util/dstr.h>
|
||||
#include "dc-capture.h"
|
||||
#include "window-helpers.h"
|
||||
#include "../../libobs/util/platform.h"
|
||||
#include "../../libobs-winrt/winrt-capture.h"
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define TEXT_WINDOW_CAPTURE obs_module_text("WindowCapture")
|
||||
#define TEXT_WINDOW obs_module_text("WindowCapture.Window")
|
||||
#define TEXT_METHOD obs_module_text("WindowCapture.Method")
|
||||
#define TEXT_METHOD_BITBLT obs_module_text("WindowCapture.Method.BitBlt")
|
||||
#define TEXT_METHOD_WGC obs_module_text("WindowCapture.Method.WindowsGraphicsCapture")
|
||||
#define TEXT_MATCH_PRIORITY obs_module_text("WindowCapture.Priority")
|
||||
#define TEXT_MATCH_TITLE obs_module_text("WindowCapture.Priority.Title")
|
||||
#define TEXT_MATCH_CLASS obs_module_text("WindowCapture.Priority.Class")
|
||||
@ -18,12 +23,28 @@
|
||||
|
||||
#define WC_CHECK_TIMER 1.0f
|
||||
|
||||
struct winrt_exports {
|
||||
bool *(*winrt_capture_supported)();
|
||||
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window);
|
||||
void (*winrt_capture_free)(struct winrt_capture *capture);
|
||||
void (*winrt_capture_render)(struct winrt_capture *capture,
|
||||
gs_effect_t *effect);
|
||||
int32_t (*winrt_capture_width)(const struct winrt_capture *capture);
|
||||
int32_t (*winrt_capture_height)(const struct winrt_capture *capture);
|
||||
};
|
||||
|
||||
enum window_capture_method {
|
||||
METHOD_BITBLT,
|
||||
METHOD_WGC,
|
||||
};
|
||||
|
||||
struct window_capture {
|
||||
obs_source_t *source;
|
||||
|
||||
char *title;
|
||||
char *class;
|
||||
char *executable;
|
||||
enum window_capture_method method;
|
||||
enum window_priority priority;
|
||||
bool cursor;
|
||||
bool compatibility;
|
||||
@ -31,6 +52,11 @@ struct window_capture {
|
||||
|
||||
struct dc_capture capture;
|
||||
|
||||
bool wgc_supported;
|
||||
void *winrt_module;
|
||||
struct winrt_exports exports;
|
||||
struct winrt_capture *capture_winrt;
|
||||
|
||||
float resize_timer;
|
||||
float check_window_timer;
|
||||
float cursor_check_time;
|
||||
@ -41,6 +67,7 @@ struct window_capture {
|
||||
|
||||
static void update_settings(struct window_capture *wc, obs_data_t *s)
|
||||
{
|
||||
int method = (int)obs_data_get_int(s, "method");
|
||||
const char *window = obs_data_get_string(s, "window");
|
||||
int priority = (int)obs_data_get_int(s, "priority");
|
||||
|
||||
@ -58,6 +85,10 @@ static void update_settings(struct window_capture *wc, obs_data_t *s)
|
||||
blog(LOG_DEBUG, "\tclass: %s", wc->class);
|
||||
}
|
||||
|
||||
if ((method == METHOD_WGC) && !wc->wgc_supported)
|
||||
method = METHOD_BITBLT;
|
||||
|
||||
wc->method = method;
|
||||
wc->priority = (enum window_priority)priority;
|
||||
wc->cursor = obs_data_get_bool(s, "cursor");
|
||||
wc->use_wildcards = obs_data_get_bool(s, "use_wildcards");
|
||||
@ -72,11 +103,54 @@ static const char *wc_getname(void *unused)
|
||||
return TEXT_WINDOW_CAPTURE;
|
||||
}
|
||||
|
||||
#define WINRT_IMPORT(func) \
|
||||
do { \
|
||||
exports->func = os_dlsym(module, #func); \
|
||||
if (!exports->func) { \
|
||||
success = false; \
|
||||
blog(LOG_ERROR, \
|
||||
"Could not load function '%s' from " \
|
||||
"module '%s'", \
|
||||
#func, module_name); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
static bool load_winrt_imports(struct winrt_exports *exports, void *module,
|
||||
const char *module_name)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
WINRT_IMPORT(winrt_capture_supported);
|
||||
WINRT_IMPORT(winrt_capture_init);
|
||||
WINRT_IMPORT(winrt_capture_free);
|
||||
WINRT_IMPORT(winrt_capture_render);
|
||||
WINRT_IMPORT(winrt_capture_width);
|
||||
WINRT_IMPORT(winrt_capture_height);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void *wc_create(obs_data_t *settings, obs_source_t *source)
|
||||
{
|
||||
struct window_capture *wc = bzalloc(sizeof(struct window_capture));
|
||||
wc->source = source;
|
||||
|
||||
obs_enter_graphics();
|
||||
const bool uses_d3d11 = gs_get_device_type() == GS_DEVICE_DIRECT3D_11;
|
||||
obs_leave_graphics();
|
||||
|
||||
if (uses_d3d11) {
|
||||
static const char *const module = "libobs-winrt";
|
||||
bool use_winrt_capture = false;
|
||||
wc->winrt_module = os_dlopen(module);
|
||||
if (wc->winrt_module &&
|
||||
load_winrt_imports(&wc->exports, wc->winrt_module,
|
||||
module) &&
|
||||
wc->exports.winrt_capture_supported()) {
|
||||
wc->wgc_supported = true;
|
||||
}
|
||||
}
|
||||
|
||||
update_settings(wc, settings);
|
||||
return wc;
|
||||
}
|
||||
@ -94,6 +168,9 @@ static void wc_destroy(void *data)
|
||||
bfree(wc->class);
|
||||
bfree(wc->executable);
|
||||
|
||||
if (wc->winrt_module)
|
||||
os_dlclose(wc->winrt_module);
|
||||
|
||||
bfree(wc);
|
||||
}
|
||||
}
|
||||
@ -111,28 +188,55 @@ static void wc_update(void *data, obs_data_t *settings)
|
||||
static uint32_t wc_width(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
return wc->capture.width;
|
||||
return (wc->method == METHOD_WGC)
|
||||
? wc->exports.winrt_capture_width(wc->capture_winrt)
|
||||
: wc->capture.width;
|
||||
}
|
||||
|
||||
static uint32_t wc_height(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
return wc->capture.height;
|
||||
return (wc->method == METHOD_WGC)
|
||||
? wc->exports.winrt_capture_height(wc->capture_winrt)
|
||||
: wc->capture.height;
|
||||
}
|
||||
|
||||
static void wc_defaults(obs_data_t *defaults)
|
||||
{
|
||||
obs_data_set_default_int(defaults, "method", METHOD_BITBLT);
|
||||
obs_data_set_default_bool(defaults, "cursor", true);
|
||||
obs_data_set_default_bool(defaults, "compatibility", false);
|
||||
}
|
||||
|
||||
static obs_properties_t *wc_properties(void *unused)
|
||||
static bool wc_capture_method_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
{
|
||||
UNUSED_PARAMETER(unused);
|
||||
const int method = (int)obs_data_get_int(settings, "method");
|
||||
const bool use_bitblt = method == METHOD_BITBLT;
|
||||
|
||||
p = obs_properties_get(props, "cursor");
|
||||
obs_property_set_visible(p, use_bitblt);
|
||||
|
||||
p = obs_properties_get(props, "compatibility");
|
||||
obs_property_set_visible(p, use_bitblt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static obs_properties_t *wc_properties(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
|
||||
obs_properties_t *ppts = obs_properties_create();
|
||||
obs_property_t *p;
|
||||
|
||||
p = obs_properties_add_list(ppts, "method", TEXT_METHOD,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(p, TEXT_METHOD_BITBLT, METHOD_BITBLT);
|
||||
obs_property_list_add_int(p, TEXT_METHOD_WGC, METHOD_WGC);
|
||||
obs_property_list_item_disable(p, 1, !wc->wgc_supported);
|
||||
obs_property_set_modified_callback(p, wc_capture_method_changed);
|
||||
|
||||
p = obs_properties_add_list(ppts, "window", TEXT_WINDOW,
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
@ -151,6 +255,18 @@ static obs_properties_t *wc_properties(void *unused)
|
||||
return ppts;
|
||||
}
|
||||
|
||||
static void wc_hide(void *data)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
|
||||
if (wc->capture_winrt) {
|
||||
wc->exports.winrt_capture_free(wc->capture_winrt);
|
||||
wc->capture_winrt = NULL;
|
||||
}
|
||||
|
||||
memset(&wc->last_rect, 0, sizeof(wc->last_rect));
|
||||
}
|
||||
|
||||
#define RESIZE_CHECK_TIME 0.2f
|
||||
#define CURSOR_CHECK_TIME 0.2f
|
||||
|
||||
@ -175,10 +291,22 @@ static void wc_tick(void *data, float seconds)
|
||||
return;
|
||||
}
|
||||
|
||||
if (wc->capture_winrt) {
|
||||
wc->exports.winrt_capture_free(wc->capture_winrt);
|
||||
wc->capture_winrt = NULL;
|
||||
}
|
||||
|
||||
wc->check_window_timer = 0.0f;
|
||||
|
||||
wc->window = find_window(EXCLUDE_MINIMIZED, wc->priority,
|
||||
wc->class, wc->title, wc->executable);
|
||||
wc->window = (wc->method == METHOD_WGC)
|
||||
? find_window_top_level(EXCLUDE_MINIMIZED,
|
||||
wc->priority,
|
||||
wc->class,
|
||||
wc->title,
|
||||
wc->executable)
|
||||
: find_window(EXCLUDE_MINIMIZED,
|
||||
wc->priority, wc->class,
|
||||
wc->title, wc->executable);
|
||||
if (!wc->window) {
|
||||
if (wc->capture.valid)
|
||||
dc_capture_free(&wc->capture);
|
||||
@ -203,47 +331,62 @@ static void wc_tick(void *data, float seconds)
|
||||
if (!GetWindowThreadProcessId(wc->window, &target_pid))
|
||||
target_pid = 0;
|
||||
|
||||
if (foreground_pid && target_pid &&
|
||||
foreground_pid != target_pid)
|
||||
wc->capture.cursor_hidden = true;
|
||||
else
|
||||
wc->capture.cursor_hidden = false;
|
||||
wc->capture.cursor_hidden = foreground_pid && target_pid &&
|
||||
foreground_pid != target_pid;
|
||||
|
||||
wc->cursor_check_time = 0.0f;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
GetClientRect(wc->window, &rect);
|
||||
if (wc->method == METHOD_BITBLT) {
|
||||
GetClientRect(wc->window, &rect);
|
||||
|
||||
if (!reset_capture) {
|
||||
wc->resize_timer += seconds;
|
||||
if (!reset_capture) {
|
||||
wc->resize_timer += seconds;
|
||||
|
||||
if (wc->resize_timer >= RESIZE_CHECK_TIME) {
|
||||
if (rect.bottom != wc->last_rect.bottom ||
|
||||
rect.right != wc->last_rect.right)
|
||||
reset_capture = true;
|
||||
if (wc->resize_timer >= RESIZE_CHECK_TIME) {
|
||||
if ((rect.bottom - rect.top) !=
|
||||
(wc->last_rect.bottom -
|
||||
wc->last_rect.top) ||
|
||||
(rect.right - rect.left) !=
|
||||
(wc->last_rect.right -
|
||||
wc->last_rect.left))
|
||||
reset_capture = true;
|
||||
|
||||
wc->resize_timer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_capture) {
|
||||
wc->resize_timer = 0.0f;
|
||||
wc->last_rect = rect;
|
||||
dc_capture_free(&wc->capture);
|
||||
dc_capture_init(&wc->capture, 0, 0,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top, wc->cursor,
|
||||
wc->compatibility);
|
||||
}
|
||||
|
||||
dc_capture_capture(&wc->capture, wc->window);
|
||||
} else if (wc->method == METHOD_WGC) {
|
||||
if (wc->window && (wc->capture_winrt == NULL)) {
|
||||
wc->capture_winrt = wc->exports.winrt_capture_init(
|
||||
wc->cursor, wc->window);
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_capture) {
|
||||
wc->resize_timer = 0.0f;
|
||||
wc->last_rect = rect;
|
||||
dc_capture_free(&wc->capture);
|
||||
dc_capture_init(&wc->capture, 0, 0, rect.right, rect.bottom,
|
||||
wc->cursor, wc->compatibility);
|
||||
}
|
||||
|
||||
dc_capture_capture(&wc->capture, wc->window);
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
static void wc_render(void *data, gs_effect_t *effect)
|
||||
{
|
||||
struct window_capture *wc = data;
|
||||
dc_capture_render(&wc->capture, obs_get_base_effect(OBS_EFFECT_OPAQUE));
|
||||
gs_effect_t *const opaque = obs_get_base_effect(OBS_EFFECT_OPAQUE);
|
||||
if (wc->method == METHOD_WGC)
|
||||
wc->exports.winrt_capture_render(wc->capture_winrt, opaque);
|
||||
else
|
||||
dc_capture_render(&wc->capture, opaque);
|
||||
|
||||
UNUSED_PARAMETER(effect);
|
||||
}
|
||||
@ -257,6 +400,7 @@ struct obs_source_info window_capture_info = {
|
||||
.destroy = wc_destroy,
|
||||
.update = wc_update,
|
||||
.video_render = wc_render,
|
||||
.hide = wc_hide,
|
||||
.video_tick = wc_tick,
|
||||
.get_width = wc_width,
|
||||
.get_height = wc_height,
|
||||
|
Loading…
x
Reference in New Issue
Block a user