Merge pull request #2208 from jpark37/screen-capture
Windows Graphics Capture support
This commit is contained in:
commit
4d15e76c5b
@ -17,13 +17,13 @@ set build_config=RelWithDebInfo
|
||||
mkdir build build32 build64
|
||||
if "%TWITCH-CLIENTID%"=="$(twitch_clientid)" (
|
||||
cd ./build32
|
||||
cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% ..
|
||||
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% ..
|
||||
cd ../build64
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% ..
|
||||
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% ..
|
||||
) else (
|
||||
cd ./build32
|
||||
cmake -G "Visual Studio 15 2017" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" ..
|
||||
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_32% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" ..
|
||||
cd ../build64
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" ..
|
||||
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true -DBUILD_CAPTIONS=true -DCOMPILE_D3D12_HOOK=true -DBUILD_BROWSER=true -DCEF_ROOT_DIR=%CEF_64% -DTWITCH_CLIENTID="%TWITCH-CLIENTID%" -DTWITCH_HASH="%TWITCH-HASH%" -DMIXER_CLIENTID="%MIXER-CLIENTID%" -DMIXER_HASH="%MIXER-HASH%" -DRESTREAM_CLIENTID="%RESTREAM-CLIENTID%" -DRESTREAM_HASH="%RESTREAM-HASH%" ..
|
||||
)
|
||||
cd ..
|
||||
|
@ -10,6 +10,35 @@ project(obs-studio)
|
||||
option(BUILD_CAPTIONS "Build captions" FALSE)
|
||||
|
||||
if(WIN32)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Check for Win SDK version 10.0.18362 or above
|
||||
if(MSVC AND MSVC_VERSION LESS 1920)
|
||||
message(STATUS "Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
|
||||
string(REPLACE "." ";" WINAPI_VER "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
|
||||
|
||||
list(GET WINAPI_VER 0 WINAPI_VER_MAJOR)
|
||||
list(GET WINAPI_VER 1 WINAPI_VER_MINOR)
|
||||
list(GET WINAPI_VER 2 WINAPI_VER_BUILD)
|
||||
|
||||
set(WINAPI_COMPATIBLE FALSE)
|
||||
if(WINAPI_VER_MAJOR EQUAL 10)
|
||||
if (WINAPI_VER_MINOR EQUAL 0)
|
||||
if (WINAPI_VER_BUILD GREATER_EQUAL 18362)
|
||||
set(WINAPI_COMPATIBLE TRUE)
|
||||
endif()
|
||||
else()
|
||||
set(WINAPI_COMPATIBLE TRUE)
|
||||
endif()
|
||||
elseif(WINAPI_VER_MAJOR GREATER 10)
|
||||
set(WINAPI_COMPATIBLE TRUE)
|
||||
endif()
|
||||
|
||||
if(NOT WINAPI_COMPATIBLE)
|
||||
message(FATAL_ERROR "OBS requires Windows 10 SDK version 10.0.18362.0 and above to compile.\nPlease download the most recent Windows 10 SDK in order to compile (or update to Visual Studio 2019).")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (QTDIR OR DEFINED ENV{QTDIR} OR DEFINED ENV{QTDIR32} OR DEFINED ENV{QTDIR64})
|
||||
# Qt path set by user or env var
|
||||
else()
|
||||
@ -191,6 +220,7 @@ if(NOT INSTALLER_RUN)
|
||||
|
||||
if(WIN32)
|
||||
add_subdirectory(libobs-d3d11)
|
||||
add_subdirectory(libobs-winrt)
|
||||
endif()
|
||||
|
||||
add_subdirectory(libobs-opengl)
|
||||
|
@ -62,7 +62,7 @@ jobs:
|
||||
variables:
|
||||
prHasCILabel: $[ dependencies.Prebuild.outputs['checkPrLabel.prHasCILabel'] ]
|
||||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- script: git submodule update --init --recursive
|
||||
displayName: 'Checkout Submodules'
|
||||
@ -89,7 +89,7 @@ jobs:
|
||||
variables:
|
||||
prHasCILabel: $[ dependencies.Prebuild.outputs['checkPrLabel.prHasCILabel'] ]
|
||||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- script: git submodule update --init --recursive
|
||||
displayName: 'Checkout Submodules'
|
||||
|
@ -348,6 +348,9 @@ try {
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
for (gs_device_loss &callback : loss_callbacks)
|
||||
callback.device_loss_release(callback.data);
|
||||
|
||||
gs_obj *obj = first_obj;
|
||||
|
||||
while (obj) {
|
||||
@ -404,6 +407,7 @@ try {
|
||||
state.Release();
|
||||
|
||||
context->ClearState();
|
||||
context->Flush();
|
||||
|
||||
context.Release();
|
||||
device.Release();
|
||||
@ -506,6 +510,9 @@ try {
|
||||
for (auto &state : blendStates)
|
||||
state.Rebuild(dev);
|
||||
|
||||
for (gs_device_loss &callback : loss_callbacks)
|
||||
callback.device_loss_rebuild(device.Get(), callback.data);
|
||||
|
||||
} catch (const char *error) {
|
||||
bcrash("Failed to recreate D3D11: %s", error);
|
||||
|
||||
|
@ -2798,3 +2798,22 @@ device_stagesurface_create_nv12(gs_device_t *device, uint32_t width,
|
||||
|
||||
return surf;
|
||||
}
|
||||
|
||||
extern "C" EXPORT void
|
||||
device_register_loss_callbacks(gs_device_t *device,
|
||||
const gs_device_loss *callbacks)
|
||||
{
|
||||
device->loss_callbacks.emplace_back(*callbacks);
|
||||
}
|
||||
|
||||
extern "C" EXPORT void device_unregister_loss_callbacks(gs_device_t *device,
|
||||
void *data)
|
||||
{
|
||||
for (auto iter = device->loss_callbacks.begin();
|
||||
iter != device->loss_callbacks.end(); ++iter) {
|
||||
if (iter->data == data) {
|
||||
device->loss_callbacks.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -946,6 +946,7 @@ struct gs_device {
|
||||
matrix4 curViewMatrix;
|
||||
matrix4 curViewProjMatrix;
|
||||
|
||||
vector<gs_device_loss> loss_callbacks;
|
||||
gs_obj *first_obj = nullptr;
|
||||
|
||||
void InitCompiler();
|
||||
|
34
libobs-winrt/CMakeLists.txt
Normal file
34
libobs-winrt/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
project(libobs-winrt)
|
||||
|
||||
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
|
||||
|
||||
add_definitions(-DLIBOBS_EXPORTS)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(libobs-winrt_SOURCES
|
||||
winrt-capture.cpp)
|
||||
|
||||
set(libobs-winrt_HEADERS
|
||||
winrt-capture.h)
|
||||
|
||||
add_library(libobs-winrt MODULE
|
||||
${libobs-winrt_SOURCES}
|
||||
${libobs-winrt_HEADERS})
|
||||
set_target_properties(libobs-winrt
|
||||
PROPERTIES
|
||||
OUTPUT_NAME libobs-winrt
|
||||
PREFIX "")
|
||||
target_precompile_headers(libobs-winrt
|
||||
PRIVATE
|
||||
[["../libobs/util/windows/ComPtr.hpp"]]
|
||||
<obs-module.h>
|
||||
<d3d11.h>
|
||||
<Windows.Graphics.Capture.Interop.h>
|
||||
<winrt/Windows.Foundation.Metadata.h>
|
||||
<winrt/Windows.Graphics.Capture.h>)
|
||||
target_link_libraries(libobs-winrt
|
||||
libobs
|
||||
windowsapp)
|
||||
|
||||
install_obs_core(libobs-winrt)
|
328
libobs-winrt/winrt-capture.cpp
Normal file
328
libobs-winrt/winrt-capture.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
extern "C" {
|
||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
|
||||
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
|
||||
|
||||
HRESULT __stdcall CreateDirect3D11SurfaceFromDXGISurface(
|
||||
::IDXGISurface *dgxiSurface, ::IInspectable **graphicsSurface);
|
||||
}
|
||||
|
||||
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
|
||||
IDirect3DDxgiInterfaceAccess : ::IUnknown {
|
||||
virtual HRESULT __stdcall GetInterface(GUID const &id,
|
||||
void **object) = 0;
|
||||
};
|
||||
|
||||
extern "C" EXPORT bool winrt_capture_supported()
|
||||
{
|
||||
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
|
||||
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
||||
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract",
|
||||
8);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static winrt::com_ptr<T> GetDXGIInterfaceFromObject(
|
||||
winrt::Windows::Foundation::IInspectable const &object)
|
||||
{
|
||||
auto access = object.as<IDirect3DDxgiInterfaceAccess>();
|
||||
winrt::com_ptr<T> result;
|
||||
winrt::check_hresult(
|
||||
access->GetInterface(winrt::guid_of<T>(), result.put_void()));
|
||||
return result;
|
||||
}
|
||||
|
||||
struct winrt_capture {
|
||||
bool capture_cursor;
|
||||
|
||||
gs_texture_t *texture;
|
||||
bool texture_written;
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
|
||||
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device{
|
||||
nullptr};
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool{
|
||||
nullptr};
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureSession session{
|
||||
nullptr};
|
||||
winrt::Windows::Graphics::SizeInt32 last_size;
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
||||
FrameArrived_revoker frame_arrived;
|
||||
|
||||
bool thread_changed;
|
||||
struct winrt_capture *next;
|
||||
|
||||
void on_frame_arrived(winrt::Windows::Graphics::Capture::
|
||||
Direct3D11CaptureFramePool const &sender,
|
||||
winrt::Windows::Foundation::IInspectable const &)
|
||||
{
|
||||
obs_enter_graphics();
|
||||
|
||||
const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame
|
||||
frame = sender.TryGetNextFrame();
|
||||
const winrt::Windows::Graphics::SizeInt32 frame_content_size =
|
||||
frame.ContentSize();
|
||||
|
||||
winrt::com_ptr<ID3D11Texture2D> frame_surface =
|
||||
GetDXGIInterfaceFromObject<ID3D11Texture2D>(
|
||||
frame.Surface());
|
||||
|
||||
/* need GetDesc because ContentSize is not reliable */
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
frame_surface->GetDesc(&desc);
|
||||
|
||||
if (texture) {
|
||||
if (desc.Width != gs_texture_get_width(texture) ||
|
||||
desc.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);
|
||||
}
|
||||
|
||||
/* if they gave an SRV, we could avoid this copy */
|
||||
context->CopyResource(
|
||||
(ID3D11Texture2D *)gs_texture_get_obj(texture),
|
||||
frame_surface.get());
|
||||
|
||||
texture_written = true;
|
||||
|
||||
if (frame_content_size.Width != last_size.Width ||
|
||||
frame_content_size.Height != last_size.Height) {
|
||||
frame_pool.Recreate(
|
||||
device,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, frame_content_size);
|
||||
|
||||
last_size = frame_content_size;
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
}
|
||||
};
|
||||
|
||||
struct winrt_capture *capture_list;
|
||||
|
||||
static void winrt_capture_device_loss_release(void *data)
|
||||
{
|
||||
winrt_capture *capture = static_cast<winrt_capture *>(data);
|
||||
capture->frame_arrived.revoke();
|
||||
capture->frame_pool.Close();
|
||||
capture->session.Close();
|
||||
|
||||
capture->session = nullptr;
|
||||
capture->frame_pool = nullptr;
|
||||
capture->context = nullptr;
|
||||
capture->device = nullptr;
|
||||
}
|
||||
|
||||
static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
|
||||
{
|
||||
winrt_capture *capture = static_cast<winrt_capture *>(data);
|
||||
|
||||
ID3D11Device *const d3d_device = (ID3D11Device *)device_void;
|
||||
ComPtr<IDXGIDevice> dxgi_device;
|
||||
if (FAILED(d3d_device->QueryInterface(&dxgi_device)))
|
||||
blog(LOG_ERROR, "Failed to get DXGI device");
|
||||
|
||||
winrt::com_ptr<IInspectable> inspectable;
|
||||
if (FAILED(CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(),
|
||||
inspectable.put())))
|
||||
blog(LOG_ERROR, "Failed to get WinRT device");
|
||||
|
||||
const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice
|
||||
device = inspectable.as<winrt::Windows::Graphics::DirectX::
|
||||
Direct3D11::IDirect3DDevice>();
|
||||
const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool
|
||||
frame_pool = winrt::Windows::Graphics::Capture::
|
||||
Direct3D11CaptureFramePool::Create(
|
||||
device,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, capture->last_size);
|
||||
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
||||
frame_pool.CreateCaptureSession(capture->item);
|
||||
|
||||
capture->device = device;
|
||||
d3d_device->GetImmediateContext(&capture->context);
|
||||
capture->frame_pool = frame_pool;
|
||||
capture->session = session;
|
||||
capture->frame_arrived = frame_pool.FrameArrived(
|
||||
winrt::auto_revoke,
|
||||
{capture, &winrt_capture::on_frame_arrived});
|
||||
|
||||
session.StartCapture();
|
||||
}
|
||||
|
||||
thread_local bool initialized_tls;
|
||||
|
||||
extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
|
||||
HWND window)
|
||||
{
|
||||
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
|
||||
ComPtr<IDXGIDevice> dxgi_device;
|
||||
if (FAILED(d3d_device->QueryInterface(&dxgi_device))) {
|
||||
blog(LOG_WARNING, "[winrt_capture_init] Failed to "
|
||||
"get DXGI device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IInspectable> inspectable;
|
||||
HRESULT hr = CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(),
|
||||
inspectable.put());
|
||||
if (FAILED(hr)) {
|
||||
blog(LOG_WARNING, "[winrt_capture_init] Failed to "
|
||||
"get WinRT device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory =
|
||||
activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
try {
|
||||
interop_factory->CreateForWindow(
|
||||
window,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::
|
||||
IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
||||
} catch (winrt::hresult_invalid_argument &) {
|
||||
blog(LOG_WARNING, "[winrt_capture_init] Failed to "
|
||||
"create GraphicsCaptureItem");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice
|
||||
device = inspectable.as<winrt::Windows::Graphics::DirectX::
|
||||
Direct3D11::IDirect3DDevice>();
|
||||
const winrt::Windows::Graphics::SizeInt32 size = item.Size();
|
||||
const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool
|
||||
frame_pool = winrt::Windows::Graphics::Capture::
|
||||
Direct3D11CaptureFramePool::Create(
|
||||
device,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, size);
|
||||
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
||||
frame_pool.CreateCaptureSession(item);
|
||||
|
||||
if (capture_list == nullptr)
|
||||
initialized_tls = true;
|
||||
|
||||
struct winrt_capture *capture = new winrt_capture{};
|
||||
capture->capture_cursor = cursor;
|
||||
capture->item = item;
|
||||
capture->device = device;
|
||||
d3d_device->GetImmediateContext(&capture->context);
|
||||
capture->frame_pool = frame_pool;
|
||||
capture->session = session;
|
||||
capture->last_size = size;
|
||||
capture->frame_arrived = frame_pool.FrameArrived(
|
||||
winrt::auto_revoke,
|
||||
{capture, &winrt_capture::on_frame_arrived});
|
||||
capture->next = capture_list;
|
||||
capture_list = capture;
|
||||
|
||||
session.StartCapture();
|
||||
|
||||
gs_device_loss callbacks;
|
||||
callbacks.device_loss_release = winrt_capture_device_loss_release;
|
||||
callbacks.device_loss_rebuild = winrt_capture_device_loss_rebuild;
|
||||
callbacks.data = capture;
|
||||
gs_register_loss_callbacks(&callbacks);
|
||||
|
||||
return capture;
|
||||
}
|
||||
|
||||
extern "C" EXPORT void winrt_capture_free(struct winrt_capture *capture)
|
||||
{
|
||||
if (capture) {
|
||||
struct winrt_capture *current = capture_list;
|
||||
if (current == capture) {
|
||||
capture_list = capture->next;
|
||||
} else {
|
||||
struct winrt_capture *previous;
|
||||
do {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
} while (current != capture);
|
||||
|
||||
previous->next = current->next;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_unregister_loss_callbacks(capture);
|
||||
gs_texture_destroy(capture->texture);
|
||||
obs_leave_graphics();
|
||||
|
||||
capture->frame_arrived.revoke();
|
||||
capture->frame_pool.Close();
|
||||
capture->session.Close();
|
||||
|
||||
delete capture;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
|
||||
{
|
||||
gs_texture_t *const texture = capture->texture;
|
||||
gs_technique_t *tech = gs_effect_get_technique(effect, "Draw");
|
||||
gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
|
||||
size_t passes;
|
||||
|
||||
gs_effect_set_texture(image, texture);
|
||||
|
||||
passes = gs_technique_begin(tech);
|
||||
for (size_t i = 0; i < passes; i++) {
|
||||
if (gs_technique_begin_pass(tech, i)) {
|
||||
gs_draw_sprite(texture, 0, 0, 0);
|
||||
|
||||
gs_technique_end_pass(tech);
|
||||
}
|
||||
}
|
||||
gs_technique_end(tech);
|
||||
}
|
||||
|
||||
extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||
gs_effect_t *effect)
|
||||
{
|
||||
if (capture && capture->texture_written) {
|
||||
if (!initialized_tls) {
|
||||
struct winrt_capture *current = capture_list;
|
||||
while (current) {
|
||||
current->thread_changed = true;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
initialized_tls = true;
|
||||
}
|
||||
|
||||
if (capture->thread_changed) {
|
||||
/* new graphics thread. treat like device loss. */
|
||||
winrt_capture_device_loss_release(capture);
|
||||
winrt_capture_device_loss_rebuild(gs_get_device_obj(),
|
||||
capture);
|
||||
|
||||
capture->thread_changed = false;
|
||||
}
|
||||
|
||||
draw_texture(capture, effect);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" EXPORT int32_t
|
||||
winrt_capture_width(const struct winrt_capture *capture)
|
||||
{
|
||||
return capture ? capture->last_size.Width : 0;
|
||||
}
|
||||
|
||||
extern "C" EXPORT int32_t
|
||||
winrt_capture_height(const struct winrt_capture *capture)
|
||||
{
|
||||
return capture ? capture->last_size.Height : 0;
|
||||
}
|
23
libobs-winrt/winrt-capture.h
Normal file
23
libobs-winrt/winrt-capture.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
EXPORT bool winrt_capture_supported();
|
||||
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window);
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -214,6 +214,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_nv12);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks);
|
||||
#endif
|
||||
|
||||
return success;
|
||||
|
@ -311,6 +311,10 @@ struct gs_exports {
|
||||
gs_stagesurf_t *(*device_stagesurface_create_nv12)(gs_device_t *device,
|
||||
uint32_t width,
|
||||
uint32_t height);
|
||||
void (*device_register_loss_callbacks)(
|
||||
gs_device_t *device, const struct gs_device_loss *callbacks);
|
||||
void (*device_unregister_loss_callbacks)(gs_device_t *device,
|
||||
void *data);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -2959,4 +2959,28 @@ gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void gs_register_loss_callbacks(const struct gs_device_loss *callbacks)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
|
||||
if (!gs_valid("gs_register_loss_callbacks"))
|
||||
return;
|
||||
|
||||
if (graphics->exports.device_register_loss_callbacks)
|
||||
graphics->exports.device_register_loss_callbacks(
|
||||
graphics->device, callbacks);
|
||||
}
|
||||
|
||||
void gs_unregister_loss_callbacks(void *data)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
|
||||
if (!gs_valid("gs_unregister_loss_callbacks"))
|
||||
return;
|
||||
|
||||
if (graphics->exports.device_unregister_loss_callbacks)
|
||||
graphics->exports.device_unregister_loss_callbacks(
|
||||
graphics->device, data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -169,6 +169,12 @@ enum gs_texture_type {
|
||||
GS_TEXTURE_CUBE,
|
||||
};
|
||||
|
||||
struct gs_device_loss {
|
||||
void (*device_loss_release)(void *data);
|
||||
void (*device_loss_rebuild)(void *device, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct gs_monitor_info {
|
||||
int rotation_degrees;
|
||||
long x;
|
||||
@ -883,6 +889,9 @@ EXPORT bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv,
|
||||
EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
EXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks);
|
||||
EXPORT void gs_unregister_loss_callbacks(void *data);
|
||||
|
||||
#endif
|
||||
|
||||
/* inline functions used by modules */
|
||||
|
@ -958,6 +958,8 @@ static void deactivate_source(obs_source_t *source)
|
||||
|
||||
static void show_source(obs_source_t *source)
|
||||
{
|
||||
obs_source_addref(source);
|
||||
|
||||
if (source->context.data && source->info.show)
|
||||
source->info.show(source->context.data);
|
||||
obs_source_dosignal(source, "source_show", "show");
|
||||
@ -968,6 +970,8 @@ static void hide_source(obs_source_t *source)
|
||||
if (source->context.data && source->info.hide)
|
||||
source->info.hide(source->context.data);
|
||||
obs_source_dosignal(source, "source_hide", "hide");
|
||||
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
static void activate_tree(obs_source_t *parent, obs_source_t *child,
|
||||
|
@ -24,6 +24,11 @@
|
||||
#include "media-io/format-conversion.h"
|
||||
#include "media-io/video-frame.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
|
||||
{
|
||||
struct obs_core_data *data = &obs->data;
|
||||
@ -874,6 +879,14 @@ void *obs_graphics_thread(void *param)
|
||||
last_time = tick_sources(obs->video.video_time, last_time);
|
||||
profile_end(tick_sources_name);
|
||||
|
||||
#ifdef _WIN32
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
profile_start(output_frame_name);
|
||||
output_frame(raw_active, gpu_active);
|
||||
profile_end(output_frame_name);
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Unknwn.h>
|
||||
#endif
|
||||
|
||||
/* Oh no I have my own com pointer class, the world is ending, how dare you
|
||||
* write your own! */
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
MonitorCapture="Display Capture"
|
||||
WindowCapture="Window Capture"
|
||||
WindowCapture.Window="Window"
|
||||
WindowCapture.Method="Capture Method"
|
||||
WindowCapture.Method.Auto="Automatic"
|
||||
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,17 @@
|
||||
#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_AUTO obs_module_text("WindowCapture.Method.Auto")
|
||||
#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,19 +24,42 @@
|
||||
|
||||
#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_AUTO,
|
||||
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 auto_choose_method;
|
||||
bool cursor;
|
||||
bool compatibility;
|
||||
bool use_wildcards; /* TODO */
|
||||
|
||||
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 +70,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,7 +88,13 @@ 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->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");
|
||||
@ -72,11 +108,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 +173,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 +193,56 @@ 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_AUTO);
|
||||
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 show_options = method != METHOD_WGC;
|
||||
|
||||
p = obs_properties_get(props, "cursor");
|
||||
obs_property_set_visible(p, show_options);
|
||||
|
||||
p = obs_properties_get(props, "compatibility");
|
||||
obs_property_set_visible(p, show_options);
|
||||
|
||||
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_AUTO, METHOD_AUTO);
|
||||
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 +261,60 @@ 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));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -175,10 +339,26 @@ 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;
|
||||
}
|
||||
|
||||
if (wc->auto_choose_method) {
|
||||
auto_choose_method(wc);
|
||||
}
|
||||
|
||||
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 +383,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 +452,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,
|
||||
|
@ -424,3 +424,52 @@ HWND find_window(enum window_search_mode mode, enum window_priority priority,
|
||||
|
||||
return best_window;
|
||||
}
|
||||
|
||||
struct top_level_enum_data {
|
||||
enum window_search_mode mode;
|
||||
enum window_priority priority;
|
||||
const char *class;
|
||||
const char *title;
|
||||
const char *exe;
|
||||
bool uwp_window;
|
||||
HWND best_window;
|
||||
int best_rating;
|
||||
};
|
||||
|
||||
BOOL CALLBACK enum_windows_proc(HWND window, LPARAM lParam)
|
||||
{
|
||||
struct top_level_enum_data *data = (struct top_level_enum_data *)lParam;
|
||||
|
||||
if (!check_window_valid(window, data->mode))
|
||||
return TRUE;
|
||||
|
||||
const int rating = window_rating(window, data->priority, data->class,
|
||||
data->title, data->exe,
|
||||
data->uwp_window);
|
||||
if (rating < data->best_rating) {
|
||||
data->best_rating = rating;
|
||||
data->best_window = window;
|
||||
}
|
||||
|
||||
return rating > 0;
|
||||
}
|
||||
|
||||
HWND find_window_top_level(enum window_search_mode mode,
|
||||
enum window_priority priority, const char *class,
|
||||
const char *title, const char *exe)
|
||||
{
|
||||
if (!class)
|
||||
return NULL;
|
||||
|
||||
struct top_level_enum_data data;
|
||||
data.mode = mode;
|
||||
data.priority = priority;
|
||||
data.class = class;
|
||||
data.title = title;
|
||||
data.exe = exe;
|
||||
data.uwp_window = strcmp(class, "Windows.UI.Core.CoreWindow") == 0;
|
||||
data.best_window = NULL;
|
||||
data.best_rating = 0x7FFFFFFF;
|
||||
EnumWindows(enum_windows_proc, (LPARAM)&data);
|
||||
return data.best_window;
|
||||
}
|
||||
|
@ -31,3 +31,7 @@ extern void build_window_strings(const char *str, char **class, char **title,
|
||||
extern HWND find_window(enum window_search_mode mode,
|
||||
enum window_priority priority, const char *class,
|
||||
const char *title, const char *exe);
|
||||
extern HWND find_window_top_level(enum window_search_mode mode,
|
||||
enum window_priority priority,
|
||||
const char *class, const char *title,
|
||||
const char *exe);
|
||||
|
Loading…
x
Reference in New Issue
Block a user