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.master
parent
a5447b5fd8
commit
44b8c24f34
|
@ -13,6 +13,7 @@ set(libobs-d3d11_SOURCES
|
||||||
d3d11-subsystem.cpp
|
d3d11-subsystem.cpp
|
||||||
d3d11-texture2d.cpp
|
d3d11-texture2d.cpp
|
||||||
d3d11-vertexbuffer.cpp
|
d3d11-vertexbuffer.cpp
|
||||||
|
d3d11-duplicator.cpp
|
||||||
d3d11-zstencilbuffer.cpp)
|
d3d11-zstencilbuffer.cpp)
|
||||||
|
|
||||||
set(libobs-d3d11_HEADERS
|
set(libobs-d3d11_HEADERS
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
/******************************************************************************
|
||||||
|
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "d3d11-subsystem.hpp"
|
||||||
|
|
||||||
|
static inline bool get_monitor(gs_device_t *device, int monitor_idx,
|
||||||
|
IDXGIOutput **dxgiOutput)
|
||||||
|
{
|
||||||
|
ComPtr<IDXGIAdapter> dxgiAdapter;
|
||||||
|
ComPtr<IDXGIDevice> 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<IDXGIOutput1> output1;
|
||||||
|
ComPtr<IDXGIOutput> 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<IDXGIOutput> 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<ID3D11Texture2D> tex;
|
||||||
|
ComPtr<IDXGIResource> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
|
#include <dxgi1_2.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
|
|
||||||
|
@ -449,6 +450,15 @@ struct gs_vertex_shader : gs_shader {
|
||||||
const char *shaderString);
|
const char *shaderString);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct gs_duplicator {
|
||||||
|
ComPtr<IDXGIOutputDuplication> 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 {
|
struct gs_pixel_shader : gs_shader {
|
||||||
ComPtr<ID3D11PixelShader> shader;
|
ComPtr<ID3D11PixelShader> shader;
|
||||||
vector<ShaderSampler> samplers;
|
vector<ShaderSampler> samplers;
|
||||||
|
|
|
@ -175,6 +175,11 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
GRAPHICS_IMPORT(device_gdi_texture_available);
|
GRAPHICS_IMPORT(device_gdi_texture_available);
|
||||||
GRAPHICS_IMPORT(device_shared_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(device_texture_create_gdi);
|
||||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc);
|
GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc);
|
||||||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc);
|
GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc);
|
||||||
|
|
|
@ -232,6 +232,16 @@ struct gs_exports {
|
||||||
bool (*device_gdi_texture_available)(void);
|
bool (*device_gdi_texture_available)(void);
|
||||||
bool (*device_shared_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,
|
gs_texture_t *(*device_texture_create_gdi)(gs_device_t *device,
|
||||||
uint32_t width, uint32_t height);
|
uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
|
|
@ -1967,6 +1967,60 @@ bool gs_shared_texture_available(void)
|
||||||
return thread_graphics->exports.device_shared_texture_available();
|
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 */
|
/** creates a windows GDI-lockable texture */
|
||||||
gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height)
|
gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height)
|
||||||
{
|
{
|
||||||
|
|
|
@ -168,6 +168,14 @@ enum gs_texture_type {
|
||||||
GS_TEXTURE_CUBE
|
GS_TEXTURE_CUBE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct gs_monitor_info {
|
||||||
|
int rotation_degrees;
|
||||||
|
long x;
|
||||||
|
long y;
|
||||||
|
long cx;
|
||||||
|
long cy;
|
||||||
|
};
|
||||||
|
|
||||||
struct gs_tvertarray {
|
struct gs_tvertarray {
|
||||||
size_t width;
|
size_t width;
|
||||||
void *array;
|
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_gdi_texture_available(void);
|
||||||
EXPORT bool gs_shared_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 */
|
/** creates a windows GDI-lockable texture */
|
||||||
EXPORT gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height);
|
EXPORT gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue