From 44b8c24f34ea2b0446b7df858ae562378f53ca32 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 29 Dec 2014 00:29:55 -0800 Subject: [PATCH] libobs: Add output duplicator support This adds support for the windows 8+ output duplicator feature which allows the efficient capturing of a specific monitor connected to the currently used device. --- libobs-d3d11/CMakeLists.txt | 1 + libobs-d3d11/d3d11-duplicator.cpp | 210 ++++++++++++++++++++++++++++ libobs-d3d11/d3d11-subsystem.hpp | 10 ++ libobs/graphics/graphics-imports.c | 5 + libobs/graphics/graphics-internal.h | 10 ++ libobs/graphics/graphics.c | 54 +++++++ libobs/graphics/graphics.h | 25 ++++ 7 files changed, 315 insertions(+) create mode 100644 libobs-d3d11/d3d11-duplicator.cpp diff --git a/libobs-d3d11/CMakeLists.txt b/libobs-d3d11/CMakeLists.txt index 0ca632acf..95d85c0c5 100644 --- a/libobs-d3d11/CMakeLists.txt +++ b/libobs-d3d11/CMakeLists.txt @@ -13,6 +13,7 @@ set(libobs-d3d11_SOURCES d3d11-subsystem.cpp d3d11-texture2d.cpp d3d11-vertexbuffer.cpp + d3d11-duplicator.cpp d3d11-zstencilbuffer.cpp) set(libobs-d3d11_HEADERS diff --git a/libobs-d3d11/d3d11-duplicator.cpp b/libobs-d3d11/d3d11-duplicator.cpp new file mode 100644 index 000000000..493646062 --- /dev/null +++ b/libobs-d3d11/d3d11-duplicator.cpp @@ -0,0 +1,210 @@ +/****************************************************************************** + Copyright (C) 2013 by Hugh Bailey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "d3d11-subsystem.hpp" + +static inline bool get_monitor(gs_device_t *device, int monitor_idx, + IDXGIOutput **dxgiOutput) +{ + ComPtr dxgiAdapter; + ComPtr dxgiDevice; + HRESULT hr; + + hr = device->device->QueryInterface(__uuidof(IDXGIDevice), + (void**)dxgiDevice.Assign()); + if (FAILED(hr)) + throw HRError("Failed to query IDXGIDevice", hr); + + hr = dxgiDevice->GetAdapter(dxgiAdapter.Assign()); + if (FAILED(hr)) + throw HRError("Failed to get adapter", hr); + + hr = dxgiAdapter->EnumOutputs(monitor_idx, dxgiOutput); + if (FAILED(hr)) { + if (hr == DXGI_ERROR_NOT_FOUND) + return false; + + throw HRError("Failed to get output", hr); + } + + return true; +} + +gs_duplicator::gs_duplicator(gs_device_t *device_, int monitor_idx) + : device(device_), texture(nullptr) +{ + ComPtr output1; + ComPtr output; + HRESULT hr; + + if (!get_monitor(device, monitor_idx, output.Assign())) + throw "Invalid monitor index"; + + hr = output->QueryInterface(__uuidof(IDXGIOutput1), + (void**)output1.Assign()); + if (FAILED(hr)) + throw HRError("Failed to query IDXGIOutput1", hr); + + hr = output1->DuplicateOutput(device->device, duplicator.Assign()); + if (FAILED(hr)) + throw HRError("Failed to duplicate output", hr); +} + +gs_duplicator::~gs_duplicator() +{ + delete texture; +} + +extern "C" { + +EXPORT bool device_get_duplicator_monitor_info(gs_device_t *device, + int monitor_idx, struct gs_monitor_info *info) +{ + DXGI_OUTPUT_DESC desc; + HRESULT hr; + + try { + ComPtr output; + + if (!get_monitor(device, monitor_idx, output.Assign())) + return false; + + hr = output->GetDesc(&desc); + if (FAILED(hr)) + throw HRError("GetDesc failed", hr); + + } catch (HRError error) { + blog(LOG_ERROR, "device_get_duplicator_monitor_info: " + "%s (%08lX)", error.str, error.hr); + return false; + } + + switch (desc.Rotation) { + case DXGI_MODE_ROTATION_UNSPECIFIED: + case DXGI_MODE_ROTATION_IDENTITY: + info->rotation_degrees = 0; + break; + + case DXGI_MODE_ROTATION_ROTATE90: + info->rotation_degrees = 90; + break; + + case DXGI_MODE_ROTATION_ROTATE180: + info->rotation_degrees = 180; + break; + + case DXGI_MODE_ROTATION_ROTATE270: + info->rotation_degrees = 270; + break; + } + + info->x = desc.DesktopCoordinates.left; + info->y = desc.DesktopCoordinates.top; + info->cx = desc.DesktopCoordinates.right - info->x; + info->cy = desc.DesktopCoordinates.bottom - info->y; + + return true; +} + +EXPORT gs_duplicator_t *device_duplicator_create(gs_device_t *device, + int monitor_idx) +{ + gs_duplicator *duplicator = nullptr; + + try { + duplicator = new gs_duplicator(device, monitor_idx); + + } catch (const char *error) { + blog(LOG_ERROR, "device_duplicator_create: %s", + error); + return nullptr; + + } catch (HRError error) { + blog(LOG_ERROR, "device_duplicator_create: %s (%08lX)", + error.str, error.hr); + return nullptr; + } + + return duplicator; +} + +EXPORT void gs_duplicator_destroy(gs_duplicator_t *duplicator) +{ + delete duplicator; +} + +static inline void copy_texture(gs_duplicator_t *d, ID3D11Texture2D *tex) +{ + D3D11_TEXTURE2D_DESC desc; + tex->GetDesc(&desc); + + if (!d->texture || + d->texture->width != desc.Width || + d->texture->height != desc.Height) { + + delete d->texture; + d->texture = (gs_texture_2d*)gs_texture_create( + desc.Width, desc.Height, + ConvertDXGITextureFormat(desc.Format), 1, + nullptr, 0); + } + + if (!!d->texture) + d->device->context->CopyResource(d->texture->texture, + tex); +} + +EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *d) +{ + DXGI_OUTDUPL_FRAME_INFO info; + ComPtr tex; + ComPtr res; + HRESULT hr; + + hr = d->duplicator->AcquireNextFrame(0, &info, res.Assign()); + if (hr == DXGI_ERROR_ACCESS_LOST) { + return false; + + } else if (hr == DXGI_ERROR_WAIT_TIMEOUT) { + return true; + + } else if (FAILED(hr)) { + blog(LOG_ERROR, "gs_duplicator_update_frame: Failed to update " + "frame (%08lX)", hr); + return true; + } + + hr = res->QueryInterface(__uuidof(ID3D11Texture2D), + (void**)tex.Assign()); + if (FAILED(hr)) { + blog(LOG_ERROR, "gs_duplicator_update_frame: Failed to query " + "ID3D11Texture2D (%08lX)", hr); + d->duplicator->ReleaseFrame(); + return true; + } + + copy_texture(d, tex); + d->duplicator->ReleaseFrame(); + return true; +} + +EXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator) +{ + return duplicator->texture; +} + +} diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 2e4d2445b..8257c8a49 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -449,6 +450,15 @@ struct gs_vertex_shader : gs_shader { const char *shaderString); }; +struct gs_duplicator { + ComPtr duplicator; + gs_texture_2d *texture; + gs_device_t *device; + + gs_duplicator(gs_device_t *device, int monitor_idx); + ~gs_duplicator(); +}; + struct gs_pixel_shader : gs_shader { ComPtr shader; vector samplers; diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index fc8c8fd02..e9420e849 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -175,6 +175,11 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, #elif _WIN32 GRAPHICS_IMPORT(device_gdi_texture_available); GRAPHICS_IMPORT(device_shared_texture_available); + GRAPHICS_IMPORT_OPTIONAL(device_get_duplicator_monitor_info); + GRAPHICS_IMPORT_OPTIONAL(device_duplicator_create); + GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_destroy); + GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_update_frame); + GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_texture); GRAPHICS_IMPORT_OPTIONAL(device_texture_create_gdi); GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc); GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc); diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 305292a7e..f8742cc91 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -232,6 +232,16 @@ struct gs_exports { bool (*device_gdi_texture_available)(void); bool (*device_shared_texture_available)(void); + bool (*device_get_duplicator_monitor_info)(gs_device_t *device, + int monitor_idx, struct gs_monitor_info *monitor_info); + + gs_duplicator_t *(*device_duplicator_create)(gs_device_t *device, + int monitor_idx); + void (*gs_duplicator_destroy)(gs_duplicator_t *duplicator); + + bool (*gs_duplicator_update_frame)(gs_duplicator_t *duplicator); + gs_texture_t *(*gs_duplicator_get_texture)(gs_duplicator_t *duplicator); + gs_texture_t *(*device_texture_create_gdi)(gs_device_t *device, uint32_t width, uint32_t height); diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index 4a17affab..fcaa5fdfc 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -1967,6 +1967,60 @@ bool gs_shared_texture_available(void) return thread_graphics->exports.device_shared_texture_available(); } +bool gs_get_duplicator_monitor_info(int monitor_idx, + struct gs_monitor_info *monitor_info) +{ + if (!thread_graphics) + return false; + if (!thread_graphics->exports.device_get_duplicator_monitor_info) + return false; + + return thread_graphics->exports.device_get_duplicator_monitor_info( + thread_graphics->device, monitor_idx, + monitor_info); +} + +gs_duplicator_t *gs_duplicator_create(int monitor_idx) +{ + if (!thread_graphics) + return NULL; + if (!thread_graphics->exports.device_duplicator_create) + return NULL; + + return thread_graphics->exports.device_duplicator_create( + thread_graphics->device, monitor_idx); +} + +void gs_duplicator_destroy(gs_duplicator_t *duplicator) +{ + if (!thread_graphics) + return; + if (!thread_graphics->exports.gs_duplicator_destroy) + return; + + thread_graphics->exports.gs_duplicator_destroy(duplicator); +} + +bool gs_duplicator_update_frame(gs_duplicator_t *duplicator) +{ + if (!thread_graphics) + return true; + if (!thread_graphics->exports.gs_duplicator_get_texture) + return true; + + return thread_graphics->exports.gs_duplicator_update_frame(duplicator); +} + +gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator) +{ + if (!thread_graphics) + return NULL; + if (!thread_graphics->exports.gs_duplicator_get_texture) + return NULL; + + return thread_graphics->exports.gs_duplicator_get_texture(duplicator); +} + /** creates a windows GDI-lockable texture */ gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height) { diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 7934fe218..3cd961506 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -168,6 +168,14 @@ enum gs_texture_type { GS_TEXTURE_CUBE }; +struct gs_monitor_info { + int rotation_degrees; + long x; + long y; + long cx; + long cy; +}; + struct gs_tvertarray { size_t width; void *array; @@ -712,6 +720,23 @@ EXPORT bool gs_texture_rebind_iosurface(gs_texture_t *texture, EXPORT bool gs_gdi_texture_available(void); EXPORT bool gs_shared_texture_available(void); +struct gs_duplicator; +typedef struct gs_duplicator gs_duplicator_t; + +/** + * Gets information about the monitor at the specific index, returns false + * when there is no monitor at the specified index + */ +EXPORT bool gs_get_duplicator_monitor_info(int monitor_idx, + struct gs_monitor_info *monitor_info); + +/** creates a windows 8+ output duplicator (monitor capture) */ +EXPORT gs_duplicator_t *gs_duplicator_create(int monitor_idx); +EXPORT void gs_duplicator_destroy(gs_duplicator_t *duplicator); + +EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *duplicator); +EXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator); + /** creates a windows GDI-lockable texture */ EXPORT gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height); -- GitLab