mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-24 23:31:36 +08:00
Merge pull request #2433 from jpark37/wgc-crop
WGC improvements: context-specific options and client area toggle
This commit is contained in:
@@ -24,11 +24,13 @@ target_precompile_headers(libobs-winrt
|
||||
[["../libobs/util/windows/ComPtr.hpp"]]
|
||||
<obs-module.h>
|
||||
<d3d11.h>
|
||||
<dwmapi.h>
|
||||
<Windows.Graphics.Capture.Interop.h>
|
||||
<winrt/Windows.Foundation.Metadata.h>
|
||||
<winrt/Windows.Graphics.Capture.h>)
|
||||
target_link_libraries(libobs-winrt
|
||||
libobs
|
||||
Dwmapi
|
||||
windowsapp)
|
||||
|
||||
install_obs_core(libobs-winrt)
|
||||
|
||||
@@ -31,8 +31,56 @@ static winrt::com_ptr<T> GetDXGIInterfaceFromObject(
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool get_client_box(HWND window, uint32_t width, uint32_t height,
|
||||
D3D11_BOX *client_box)
|
||||
{
|
||||
RECT client_rect, window_rect{};
|
||||
POINT upper_left{};
|
||||
|
||||
const bool client_box_available =
|
||||
GetClientRect(window, &client_rect) &&
|
||||
(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&window_rect,
|
||||
sizeof(window_rect)) == S_OK) &&
|
||||
ClientToScreen(window, &upper_left);
|
||||
if (client_box_available) {
|
||||
const uint32_t left =
|
||||
(upper_left.x > window_rect.left)
|
||||
? (upper_left.x - window_rect.left)
|
||||
: 0;
|
||||
client_box->left = left;
|
||||
|
||||
const uint32_t top = (upper_left.y > window_rect.top)
|
||||
? (upper_left.y - window_rect.top)
|
||||
: 0;
|
||||
client_box->top = top;
|
||||
|
||||
uint32_t texture_width = 1;
|
||||
if (width > left) {
|
||||
texture_width =
|
||||
min(width - left, (uint32_t)client_rect.right);
|
||||
}
|
||||
|
||||
uint32_t texture_height = 1;
|
||||
if (height > top) {
|
||||
texture_height =
|
||||
min(height - top, (uint32_t)client_rect.bottom);
|
||||
}
|
||||
|
||||
client_box->right = left + texture_width;
|
||||
client_box->bottom = top + texture_height;
|
||||
|
||||
client_box->front = 0;
|
||||
client_box->back = 1;
|
||||
}
|
||||
|
||||
return client_box_available;
|
||||
}
|
||||
|
||||
struct winrt_capture {
|
||||
bool capture_cursor;
|
||||
HWND window;
|
||||
bool client_area;
|
||||
|
||||
gs_texture_t *texture;
|
||||
bool texture_written;
|
||||
@@ -48,6 +96,11 @@ struct winrt_capture {
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
||||
FrameArrived_revoker frame_arrived;
|
||||
|
||||
uint32_t texture_width;
|
||||
uint32_t texture_height;
|
||||
D3D11_BOX client_box;
|
||||
bool client_box_available;
|
||||
|
||||
bool thread_changed;
|
||||
struct winrt_capture *next;
|
||||
|
||||
@@ -70,23 +123,45 @@ struct winrt_capture {
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
frame_surface->GetDesc(&desc);
|
||||
|
||||
client_box_available = false;
|
||||
if (client_area) {
|
||||
client_box_available = get_client_box(
|
||||
window, desc.Width, desc.Height, &client_box);
|
||||
}
|
||||
|
||||
if (client_box_available) {
|
||||
texture_width = client_box.right - client_box.left;
|
||||
texture_height = client_box.bottom - client_box.top;
|
||||
} else {
|
||||
texture_width = desc.Width;
|
||||
texture_height = desc.Height;
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
if (desc.Width != gs_texture_get_width(texture) ||
|
||||
desc.Height != gs_texture_get_height(texture)) {
|
||||
if (texture_width != gs_texture_get_width(texture) ||
|
||||
texture_height != gs_texture_get_height(texture)) {
|
||||
gs_texture_destroy(texture);
|
||||
texture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
texture = gs_texture_create(desc.Width, desc.Height,
|
||||
GS_BGRA, 1, nullptr, 0);
|
||||
texture = gs_texture_create(texture_width,
|
||||
texture_height, GS_BGRA, 1,
|
||||
nullptr, 0);
|
||||
}
|
||||
|
||||
/* if they gave an SRV, we could avoid this copy */
|
||||
context->CopyResource(
|
||||
(ID3D11Texture2D *)gs_texture_get_obj(texture),
|
||||
frame_surface.get());
|
||||
if (client_box_available) {
|
||||
context->CopySubresourceRegion(
|
||||
(ID3D11Texture2D *)gs_texture_get_obj(texture),
|
||||
0, 0, 0, 0, frame_surface.get(), 0,
|
||||
&client_box);
|
||||
} else {
|
||||
/* if they gave an SRV, we could avoid this copy */
|
||||
context->CopyResource(
|
||||
(ID3D11Texture2D *)gs_texture_get_obj(texture),
|
||||
frame_surface.get());
|
||||
}
|
||||
|
||||
texture_written = true;
|
||||
|
||||
@@ -160,8 +235,8 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
|
||||
|
||||
thread_local bool initialized_tls;
|
||||
|
||||
extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
|
||||
HWND window)
|
||||
extern "C" EXPORT struct winrt_capture *
|
||||
winrt_capture_init(bool cursor, HWND window, bool client_area)
|
||||
{
|
||||
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
|
||||
ComPtr<IDXGIDevice> dxgi_device;
|
||||
@@ -192,8 +267,9 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
|
||||
IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
||||
} catch (winrt::hresult_invalid_argument &) {
|
||||
blog(LOG_WARNING, "[winrt_capture_init] Failed to "
|
||||
"create GraphicsCaptureItem");
|
||||
/* too spammy */
|
||||
//blog(LOG_WARNING, "[winrt_capture_init] Failed to "
|
||||
// "create GraphicsCaptureItem");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -216,6 +292,8 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
|
||||
|
||||
struct winrt_capture *capture = new winrt_capture{};
|
||||
capture->capture_cursor = cursor;
|
||||
capture->window = window;
|
||||
capture->client_area = client_area;
|
||||
capture->item = item;
|
||||
capture->device = device;
|
||||
d3d_device->GetImmediateContext(&capture->context);
|
||||
@@ -315,14 +393,14 @@ extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" EXPORT int32_t
|
||||
extern "C" EXPORT uint32_t
|
||||
winrt_capture_width(const struct winrt_capture *capture)
|
||||
{
|
||||
return capture ? capture->last_size.Width : 0;
|
||||
return capture ? capture->texture_width : 0;
|
||||
}
|
||||
|
||||
extern "C" EXPORT int32_t
|
||||
extern "C" EXPORT uint32_t
|
||||
winrt_capture_height(const struct winrt_capture *capture)
|
||||
{
|
||||
return capture ? capture->last_size.Height : 0;
|
||||
return capture ? capture->texture_height : 0;
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
EXPORT bool winrt_capture_supported();
|
||||
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window);
|
||||
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window,
|
||||
bool client_area);
|
||||
EXPORT void winrt_capture_free(struct winrt_capture *capture);
|
||||
|
||||
EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||
gs_effect_t *effect);
|
||||
EXPORT int32_t winrt_capture_width(const struct winrt_capture *capture);
|
||||
EXPORT int32_t winrt_capture_height(const struct winrt_capture *capture);
|
||||
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
|
||||
EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ WindowCapture.Priority.Class="Match title, otherwise find window of same type"
|
||||
WindowCapture.Priority.Exe="Match title, otherwise find window of same executable"
|
||||
CaptureCursor="Capture Cursor"
|
||||
Compatibility="Multi-adapter Compatibility"
|
||||
ClientArea="Client Area"
|
||||
SLIFix="SLI/Crossfire Capture Mode (Slow)"
|
||||
AllowTransparency="Allow Transparency"
|
||||
Monitor="Display"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define TEXT_MATCH_EXE obs_module_text("WindowCapture.Priority.Exe")
|
||||
#define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
|
||||
#define TEXT_COMPATIBILITY obs_module_text("Compatibility")
|
||||
#define TEXT_CLIENT_AREA obs_module_text("ClientArea")
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@@ -26,12 +27,13 @@
|
||||
|
||||
struct winrt_exports {
|
||||
bool *(*winrt_capture_supported)();
|
||||
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window);
|
||||
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window,
|
||||
bool client_area);
|
||||
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);
|
||||
uint32_t (*winrt_capture_width)(const struct winrt_capture *capture);
|
||||
uint32_t (*winrt_capture_height)(const struct winrt_capture *capture);
|
||||
};
|
||||
|
||||
enum window_capture_method {
|
||||
@@ -48,9 +50,9 @@ struct window_capture {
|
||||
char *executable;
|
||||
enum window_capture_method method;
|
||||
enum window_priority priority;
|
||||
bool auto_choose_method;
|
||||
bool cursor;
|
||||
bool compatibility;
|
||||
bool client_area;
|
||||
bool use_wildcards; /* TODO */
|
||||
|
||||
struct dc_capture capture;
|
||||
@@ -68,6 +70,53 @@ struct window_capture {
|
||||
RECT last_rect;
|
||||
};
|
||||
|
||||
static const char *wgc_partial_match_classes[] = {
|
||||
"Chrome",
|
||||
"Mozilla",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *wgc_whole_match_classes[] = {
|
||||
"ApplicationFrameWindow",
|
||||
"Windows.UI.Core.CoreWindow",
|
||||
"XLMAIN", /* excel*/
|
||||
"PPTFrameClass", /* powerpoint */
|
||||
"OpusApp", /* word */
|
||||
NULL,
|
||||
};
|
||||
|
||||
static enum window_capture_method
|
||||
choose_method(enum window_capture_method method, bool wgc_supported,
|
||||
const char *current_class)
|
||||
{
|
||||
if (method != METHOD_AUTO) {
|
||||
if (method == METHOD_WGC && !wgc_supported)
|
||||
method = METHOD_BITBLT;
|
||||
return method;
|
||||
}
|
||||
|
||||
if (!current_class)
|
||||
return METHOD_BITBLT;
|
||||
|
||||
const char **class = wgc_partial_match_classes;
|
||||
while (*class) {
|
||||
if (astrstri(current_class, *class) != NULL) {
|
||||
return METHOD_WGC;
|
||||
}
|
||||
class ++;
|
||||
}
|
||||
|
||||
class = wgc_whole_match_classes;
|
||||
while (*class) {
|
||||
if (astrcmpi(current_class, *class) == 0) {
|
||||
return METHOD_WGC;
|
||||
}
|
||||
class ++;
|
||||
}
|
||||
|
||||
return METHOD_BITBLT;
|
||||
}
|
||||
|
||||
static void update_settings(struct window_capture *wc, obs_data_t *s)
|
||||
{
|
||||
int method = (int)obs_data_get_int(s, "method");
|
||||
@@ -88,16 +137,12 @@ static void update_settings(struct window_capture *wc, obs_data_t *s)
|
||||
blog(LOG_DEBUG, "\tclass: %s", wc->class);
|
||||
}
|
||||
|
||||
if (!wc->wgc_supported) {
|
||||
method = METHOD_BITBLT;
|
||||
}
|
||||
|
||||
wc->method = method;
|
||||
wc->method = choose_method(method, wc->wgc_supported, wc->class);
|
||||
wc->priority = (enum window_priority)priority;
|
||||
wc->auto_choose_method = (method == METHOD_AUTO);
|
||||
wc->cursor = obs_data_get_bool(s, "cursor");
|
||||
wc->use_wildcards = obs_data_get_bool(s, "use_wildcards");
|
||||
wc->compatibility = obs_data_get_bool(s, "compatibility");
|
||||
wc->client_area = obs_data_get_bool(s, "client_area");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@@ -211,19 +256,43 @@ static void wc_defaults(obs_data_t *defaults)
|
||||
obs_data_set_default_int(defaults, "method", METHOD_AUTO);
|
||||
obs_data_set_default_bool(defaults, "cursor", true);
|
||||
obs_data_set_default_bool(defaults, "compatibility", false);
|
||||
obs_data_set_default_bool(defaults, "client_area", true);
|
||||
}
|
||||
|
||||
static void update_settings_visibility(obs_properties_t *props,
|
||||
enum window_capture_method method)
|
||||
{
|
||||
const bool bitblt_options = method == METHOD_BITBLT;
|
||||
const bool wgc_options = method == METHOD_WGC;
|
||||
|
||||
obs_property_t *p = obs_properties_get(props, "cursor");
|
||||
obs_property_set_visible(p, bitblt_options);
|
||||
|
||||
p = obs_properties_get(props, "compatibility");
|
||||
obs_property_set_visible(p, bitblt_options);
|
||||
|
||||
p = obs_properties_get(props, "client_area");
|
||||
obs_property_set_visible(p, wgc_options);
|
||||
}
|
||||
|
||||
static bool wc_capture_method_changed(obs_properties_t *props,
|
||||
obs_property_t *p, obs_data_t *settings)
|
||||
{
|
||||
const int method = (int)obs_data_get_int(settings, "method");
|
||||
const bool show_options = method != METHOD_WGC;
|
||||
struct window_capture *wc = obs_properties_get_param(props);
|
||||
update_settings(wc, settings);
|
||||
|
||||
p = obs_properties_get(props, "cursor");
|
||||
obs_property_set_visible(p, show_options);
|
||||
update_settings_visibility(props, wc->method);
|
||||
|
||||
p = obs_properties_get(props, "compatibility");
|
||||
obs_property_set_visible(p, show_options);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool wc_window_changed(obs_properties_t *props, obs_property_t *p,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
struct window_capture *wc = obs_properties_get_param(props);
|
||||
update_settings(wc, settings);
|
||||
|
||||
update_settings_visibility(props, wc->method);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -233,6 +302,8 @@ static obs_properties_t *wc_properties(void *data)
|
||||
struct window_capture *wc = data;
|
||||
|
||||
obs_properties_t *ppts = obs_properties_create();
|
||||
obs_properties_set_param(ppts, wc, NULL);
|
||||
|
||||
obs_property_t *p;
|
||||
|
||||
p = obs_properties_add_list(ppts, "method", TEXT_METHOD,
|
||||
@@ -247,6 +318,7 @@ static obs_properties_t *wc_properties(void *data)
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
fill_window_list(p, EXCLUDE_MINIMIZED, NULL);
|
||||
obs_property_set_modified_callback(p, wc_window_changed);
|
||||
|
||||
p = obs_properties_add_list(ppts, "priority", TEXT_MATCH_PRIORITY,
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
@@ -258,6 +330,8 @@ static obs_properties_t *wc_properties(void *data)
|
||||
|
||||
obs_properties_add_bool(ppts, "compatibility", TEXT_COMPATIBILITY);
|
||||
|
||||
obs_properties_add_bool(ppts, "client_area", TEXT_CLIENT_AREA);
|
||||
|
||||
return ppts;
|
||||
}
|
||||
|
||||
@@ -273,48 +347,6 @@ static void wc_hide(void *data)
|
||||
memset(&wc->last_rect, 0, sizeof(wc->last_rect));
|
||||
}
|
||||
|
||||
static const char *wgc_partial_match_classes[] = {
|
||||
"Chrome",
|
||||
"Mozilla",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *wgc_whole_match_classes[] = {
|
||||
"ApplicationFrameWindow",
|
||||
"Windows.UI.Core.CoreWindow",
|
||||
"XLMAIN", /* excel*/
|
||||
"PPTFrameClass", /* powerpoint */
|
||||
"OpusApp", /* word */
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void auto_choose_method(struct window_capture *wc)
|
||||
{
|
||||
wc->method = METHOD_BITBLT;
|
||||
|
||||
if (!wc->class) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char **class = wgc_partial_match_classes;
|
||||
while (*class) {
|
||||
if (astrstri(wc->class, *class) != NULL) {
|
||||
wc->method = METHOD_WGC;
|
||||
return;
|
||||
}
|
||||
class ++;
|
||||
}
|
||||
|
||||
class = wgc_whole_match_classes;
|
||||
while (*class) {
|
||||
if (astrcmpi(wc->class, *class) == 0) {
|
||||
wc->method = METHOD_WGC;
|
||||
return;
|
||||
}
|
||||
class ++;
|
||||
}
|
||||
}
|
||||
|
||||
#define RESIZE_CHECK_TIME 0.2f
|
||||
#define CURSOR_CHECK_TIME 0.2f
|
||||
|
||||
@@ -344,10 +376,6 @@ static void wc_tick(void *data, float seconds)
|
||||
wc->capture_winrt = NULL;
|
||||
}
|
||||
|
||||
if (wc->auto_choose_method) {
|
||||
auto_choose_method(wc);
|
||||
}
|
||||
|
||||
wc->check_window_timer = 0.0f;
|
||||
|
||||
wc->window = (wc->method == METHOD_WGC)
|
||||
@@ -424,7 +452,7 @@ static void wc_tick(void *data, float seconds)
|
||||
} 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);
|
||||
wc->cursor, wc->window, wc->client_area);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user