浏览代码

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.
jp9000 10 年之前
父节点
当前提交
44b8c24f34

+ 1 - 0
libobs-d3d11/CMakeLists.txt

@@ -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

+ 210 - 0
libobs-d3d11/d3d11-duplicator.cpp

@@ -0,0 +1,210 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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;
+}
+
+}

+ 10 - 0
libobs-d3d11/d3d11-subsystem.hpp

@@ -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;

+ 5 - 0
libobs/graphics/graphics-imports.c

@@ -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);

+ 10 - 0
libobs/graphics/graphics-internal.h

@@ -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);
 
 

+ 54 - 0
libobs/graphics/graphics.c

@@ -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)
 {
 {

+ 25 - 0
libobs/graphics/graphics.h

@@ -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);