Răsfoiți Sursa

Reimplement monitor capture

 - Implement windows monitor capture (code is so much cleaner than in
   OBS1).  Will implement duplication capture later

 - Add GDI texture support to d3d11 graphics library

 - Fix precision issue with sleep timing, you have to call
   timeBeginPeriod otherwise windows sleep will be totally erratic.
jp9000 11 ani în urmă
părinte
comite
4f7ab552df

+ 35 - 0
build/data/obs-plugins/win-capture/opaque.effect

@@ -0,0 +1,35 @@
+uniform float4x4 ViewProj;
+uniform texture2d image;
+
+sampler_state def_sampler {
+	Filter   = Linear;
+	AddressU = Clamp;
+	AddressV = Clamp;
+};
+
+struct VertInOut {
+	float4 pos : POSITION;
+	float2 uv  : TEXCOORD0;
+};
+
+VertInOut VSDefault(VertInOut vert_in)
+{
+	VertInOut vert_out;
+	vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
+	vert_out.uv  = vert_in.uv;
+	return vert_out;
+}
+
+float4 PSDraw(VertInOut vert_in) : TARGET
+{
+	return float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);
+}
+
+technique Draw
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSDraw(vert_in);
+	}
+}

+ 61 - 0
libobs-d3d11/d3d11-subsystem.cpp

@@ -1662,3 +1662,64 @@ enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer)
 {
 	return indexbuffer->type;
 }
+
+extern "C" EXPORT bool gdi_texture_available(void)
+{
+	return true;
+}
+
+extern "C" EXPORT texture_t device_create_gdi_texture(device_t device,
+		uint32_t width, uint32_t height)
+{
+	gs_texture *texture = nullptr;
+	try {
+		texture = new gs_texture_2d(device, width, height, GS_BGRA,
+				1, nullptr, GS_RENDERTARGET, GS_TEXTURE_2D,
+				true, false);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_gdi_texture (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_gdi_texture (D3D11): %s", error);
+	}
+
+	return texture;
+}
+
+static inline bool TextureGDICompatible(gs_texture_2d *tex2d, const char *func)
+{
+	if (!tex2d->isGDICompatible) {
+		blog(LOG_ERROR, "%s (D3D11): Texture is not GDI compatible",
+				func);
+		return false;
+	}
+
+	return true;
+}
+
+extern "C" EXPORT void *texture_get_dc(texture_t tex)
+{
+	HDC hDC = nullptr;
+
+	if (tex->type != GS_TEXTURE_2D)
+		return nullptr;
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+	if (!TextureGDICompatible(tex2d, "texture_get_dc"))
+		return nullptr;
+
+	tex2d->gdiSurface->GetDC(true, &hDC);
+	return hDC;
+}
+
+extern "C" EXPORT void texture_release_dc(texture_t tex)
+{
+	if (tex->type != GS_TEXTURE_2D)
+		return;
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+	if (!TextureGDICompatible(tex2d, "texture_release_dc"))
+		return;
+
+	tex2d->gdiSurface->ReleaseDC(nullptr);
+}

+ 10 - 0
libobs-d3d11/d3d11-texture2d.cpp

@@ -73,6 +73,9 @@ void gs_texture_2d::InitTexture(const void **data)
 	if (isRenderTarget || isGDICompatible)
 		td.BindFlags |= D3D11_BIND_RENDER_TARGET;
 
+	if (isGDICompatible)
+		td.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
+
 	if (data)
 		InitSRD(srd, data);
 
@@ -80,6 +83,13 @@ void gs_texture_2d::InitTexture(const void **data)
 			texture.Assign());
 	if (FAILED(hr))
 		throw HRError("Failed to create 2D texture", hr);
+
+	if (isGDICompatible) {
+		hr = texture->QueryInterface(__uuidof(IDXGISurface1),
+				(void**)gdiSurface.Assign());
+		if (FAILED(hr))
+			throw HRError("Failed to create GDI surface", hr);
+	}
 }
 
 void gs_texture_2d::InitResourceView()

+ 5 - 0
libobs-opengl/gl-windows.c

@@ -513,3 +513,8 @@ extern void gl_getclientsize(struct gs_swap_chain *swap,
 	*width  = rc.right;
 	*height = rc.bottom;
 }
+
+EXPORT bool gdi_texture_available(void)
+{
+	return false;
+}

+ 2 - 1
libobs/CMakeLists.txt

@@ -25,7 +25,8 @@ if(WIN32)
 		util/threading-windows.c
 		util/platform-windows.c)
 	set(libobs_PLATFORM_DEPS
-		w32-pthreads)
+		w32-pthreads
+		winmm.lib)
 elseif(APPLE)
 	set(libobs_PLATFORM_SOURCES
 		obs-cocoa.c

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

@@ -167,8 +167,17 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
 	GRAPHICS_IMPORT(shader_setdefault);
 
 	/* OSX/Cocoa specific functions */
+#ifdef __APPLE__
 	GRAPHICS_IMPORT_OPTIONAL(texture_create_from_iosurface);
 	GRAPHICS_IMPORT_OPTIONAL(texture_rebind_iosurface);
 
+	/* win32 specific functions */
+#elif _WIN32
+	GRAPHICS_IMPORT(gdi_texture_available);
+	GRAPHICS_IMPORT_OPTIONAL(device_create_gdi_texture);
+	GRAPHICS_IMPORT_OPTIONAL(texture_get_dc);
+	GRAPHICS_IMPORT_OPTIONAL(texture_release_dc);
+#endif
+
 	return success;
 }

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

@@ -202,9 +202,19 @@ struct gs_exports {
 			size_t size);
 	void (*shader_setdefault)(shader_t shader, sparam_t param);
 
+#ifdef __APPLE__
 	/* OSX/Cocoa specific functions */
 	texture_t (*texture_create_from_iosurface)(device_t dev, void *iosurf);
 	bool (*texture_rebind_iosurface)(texture_t texture, void *iosurf);
+
+#elif _WIN32
+	bool (*gdi_texture_available)(void);
+	texture_t (*device_create_gdi_texture)(device_t device,
+			uint32_t width, uint32_t height);
+
+	void *(*texture_get_dc)(texture_t gdi_tex);
+	void (*texture_release_dc)(texture_t gdi_tex);
+#endif
 };
 
 struct graphics_subsystem {

+ 45 - 0
libobs/graphics/graphics.c

@@ -1894,6 +1894,8 @@ enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer)
 	return thread_graphics->exports.indexbuffer_gettype(indexbuffer);
 }
 
+#ifdef __APPLE__
+
 /** Platform specific functions */
 texture_t gs_create_texture_from_iosurface(void *iosurf)
 {
@@ -1914,3 +1916,46 @@ bool texture_rebind_iosurface(texture_t texture, void *iosurf)
 
 	return graphics->exports.texture_rebind_iosurface(texture, iosurf);
 }
+
+#elif _WIN32
+
+bool gs_gdi_texture_available(void)
+{
+	if (!thread_graphics)
+		return false;
+
+	return thread_graphics->exports.gdi_texture_available();
+}
+
+/** creates a windows GDI-lockable texture */
+texture_t gs_create_gdi_texture(uint32_t width, uint32_t height)
+{
+	graphics_t graphics = thread_graphics;
+	if (!graphics) return NULL;
+
+	if (graphics->exports.device_create_gdi_texture)
+		return graphics->exports.device_create_gdi_texture(
+				graphics->device, width, height);
+	return NULL;
+}
+
+void *texture_get_dc(texture_t gdi_tex)
+{
+	if (!thread_graphics || !gdi_tex)
+		return NULL;
+
+	if (thread_graphics->exports.texture_get_dc)
+		return thread_graphics->exports.texture_get_dc(gdi_tex);
+	return NULL;
+}
+
+void texture_release_dc(texture_t gdi_tex)
+{
+	if (!thread_graphics || !gdi_tex)
+		return;
+
+	if (thread_graphics->exports.texture_release_dc)
+		thread_graphics->exports.texture_release_dc(gdi_tex);
+}
+
+#endif

+ 14 - 0
libobs/graphics/graphics.h

@@ -677,11 +677,25 @@ EXPORT void     *indexbuffer_getdata(indexbuffer_t indexbuffer);
 EXPORT size_t   indexbuffer_numindices(indexbuffer_t indexbuffer);
 EXPORT enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer);
 
+#ifdef __APPLE__
+
 /** platform specific function for creating (GL_TEXTURE_RECTANGLE) textures
  * from shared surface resources */
 EXPORT texture_t gs_create_texture_from_iosurface(void *iosurf);
 EXPORT bool     texture_rebind_iosurface(texture_t texture, void *iosurf);
 
+#elif _WIN32
+
+EXPORT bool gs_gdi_texture_available(void);
+
+/** creates a windows GDI-lockable texture */
+EXPORT texture_t gs_create_gdi_texture(uint32_t width, uint32_t height);
+
+EXPORT void *texture_get_dc(texture_t gdi_tex);
+EXPORT void texture_release_dc(texture_t gdi_tex);
+
+#endif
+
 /* inline functions used by modules */
 
 static inline uint32_t gs_get_format_bpp(enum gs_color_format format)

+ 7 - 2
libobs/media-io/video-io.c

@@ -120,6 +120,8 @@ static inline void video_output_cur_frame(struct video_output *video)
 	pthread_mutex_unlock(&video->input_mutex);
 }
 
+static inline void nop() {int test = 0;}
+
 static void *video_thread(void *param)
 {
 	struct video_output *video = param;
@@ -127,12 +129,15 @@ static void *video_thread(void *param)
 
 	while (event_try(video->stop_event) == EAGAIN) {
 		/* wait half a frame, update frame */
-		os_sleepto_ns(cur_time += (video->frame_time/2));
+		cur_time += (video->frame_time/2);
+		os_sleepto_ns(cur_time);
+
 		video->cur_video_time = cur_time;
 		event_signal(video->update_event);
 
 		/* wait another half a frame, swap and output frames */
-		os_sleepto_ns(cur_time += (video->frame_time/2));
+		cur_time += (video->frame_time/2);
+		os_sleepto_ns(cur_time);
 
 		pthread_mutex_lock(&video->data_mutex);
 

+ 12 - 3
libobs/util/platform-windows.c

@@ -15,6 +15,7 @@
  */
 
 #include <windows.h>
+#include <mmsystem.h>
 #include <shellapi.h>
 #include <shlobj.h>
 
@@ -95,7 +96,7 @@ bool os_sleepto_ns(uint64_t time_target)
 
 	milliseconds = (uint32_t)((time_target - t)/1000000);
 	if (milliseconds > 1)
-		os_sleep_ms(milliseconds);
+		Sleep(milliseconds-1);
 
 	for (;;) {
 		t = os_gettime_ns();
@@ -184,30 +185,38 @@ int os_mkdir(const char *path)
 	return MKDIR_SUCCESS;
 }
 
-#ifdef PTW32_STATIC_LIB
 
 BOOL WINAPI DllMain(HINSTANCE hinst_dll, DWORD reason, LPVOID reserved)
 {
 	switch (reason) {
 
 	case DLL_PROCESS_ATTACH:
+		timeBeginPeriod(1);
+#ifdef PTW32_STATIC_LIB
 		pthread_win32_process_attach_np();
+#endif
 		break;
 
 	case DLL_PROCESS_DETACH:
+		timeEndPeriod(1);
+#ifdef PTW32_STATIC_LIB
 		pthread_win32_process_detach_np();
+#endif
 		break;
 
 	case DLL_THREAD_ATTACH:
+#ifdef PTW32_STATIC_LIB
 		pthread_win32_thread_attach_np();
+#endif
 		break;
 
 	case DLL_THREAD_DETACH:
+#ifdef PTW32_STATIC_LIB
 		pthread_win32_thread_detach_np();
+#endif
 		break;
 	}
 
 	return true;
 }
 
-#endif

+ 1 - 1
obs/obs-app.cpp

@@ -194,7 +194,7 @@ void OBSApp::GetFPSCommon(uint32_t &num, uint32_t &den) const
 	const char *val = config_get_string(globalConfig, "Video", "FPSCommon");
 
 	if (strcmp(val, "10") == 0) {
-		num = 30;
+		num = 10;
 		den = 1;
 	} else if (strcmp(val, "20") == 0) {
 		num = 20;

+ 1 - 0
obs/window-basic-main.cpp

@@ -71,6 +71,7 @@ void OBSBasic::OBSInit()
 	obs_load_module("mac-capture");
 #elif _WIN32
 	obs_load_module("win-wasapi");
+	obs_load_module("win-capture");
 #endif
 
 	/* HACK: fixes a qt bug with native widgets with native repaint */

+ 1 - 0
plugins/CMakeLists.txt

@@ -3,6 +3,7 @@ include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
 if(WIN32)
 	add_subdirectory(dshow)
 	add_subdirectory(win-wasapi)
+	add_subdirectory(win-capture)
 elseif(APPLE)
 	add_subdirectory(mac-capture)
 endif()

+ 18 - 0
plugins/win-capture/CMakeLists.txt

@@ -0,0 +1,18 @@
+project(win-capture)
+
+set(win-capture_HEADERS
+	dc-capture.h)
+
+set(win-capture_SOURCES
+	dc-capture.c
+	monitor-capture.c
+	plugin-main.c)
+
+add_library(win-capture MODULE
+	${win-capture_SOURCES}
+	${win-capture_HEADERS})
+target_link_libraries(win-capture
+	libobs)
+
+install_obs_plugin(win-capture)
+install_obs_plugin_data(win-capture ../../build/data/obs-plugins/win-capture)

+ 239 - 0
plugins/win-capture/dc-capture.c

@@ -0,0 +1,239 @@
+#include "dc-capture.h"
+
+#define WIN32_MEAN_AND_LEAN
+#include <windows.h>
+
+static inline void init_textures(struct dc_capture *capture)
+{
+	for (size_t i = 0; i < capture->num_textures; i++) {
+		if (capture->compatibility)
+			capture->textures[i] = gs_create_texture(
+					capture->width, capture->height,
+					GS_BGRA, 1, NULL, GS_DYNAMIC);
+		else
+			capture->textures[i] = gs_create_gdi_texture(
+					capture->width, capture->height);
+
+		if (!capture->textures[i]) {
+			blog(LOG_WARNING, "[dc_capture_init] Failed to "
+			                  "create textures");
+			return;
+		}
+	}
+
+	capture->valid = true;
+}
+
+void dc_capture_init(struct dc_capture *capture, int x, int y,
+		uint32_t width, uint32_t height, bool cursor,
+		bool compatibility)
+{
+	capture->x              = x;
+	capture->y              = y;
+	capture->width          = width;
+	capture->height         = height;
+	capture->capture_cursor = cursor;
+
+	gs_entercontext(obs_graphics());
+
+	if (!gs_gdi_texture_available())
+		compatibility = true;
+
+	capture->compatibility = compatibility;
+	capture->num_textures  = compatibility ? 1 : 2;
+
+	init_textures(capture);
+
+	gs_leavecontext();
+
+	if (!capture->valid)
+		return;
+
+	if (compatibility) {
+		BITMAPINFO bi = {0};
+		BITMAPINFOHEADER *bih = &bi.bmiHeader;
+		bih->biSize     = sizeof(BITMAPINFOHEADER);
+		bih->biBitCount = 32;
+		bih->biWidth    = width;
+		bih->biHeight   = height;
+		bih->biPlanes   = 1;
+
+		capture->hdc = CreateCompatibleDC(NULL);
+		capture->bmp = CreateDIBSection(capture->hdc, &bi,
+				DIB_RGB_COLORS, (void**)&capture->bits,
+				NULL, 0);
+		capture->old_bmp = SelectObject(capture->hdc, capture->bmp);
+	}
+}
+
+void dc_capture_free(struct dc_capture *capture)
+{
+	if (capture->hdc) {
+		SelectObject(capture->hdc, capture->old_bmp);
+		DeleteDC(capture->hdc);
+		DeleteObject(capture->bmp);
+	}
+
+	gs_entercontext(obs_graphics());
+
+	for (size_t i = 0; i < capture->num_textures; i++)
+		texture_destroy(capture->textures[i]);
+
+	gs_leavecontext();
+
+	memset(capture, 0, sizeof(struct dc_capture));
+}
+
+static void draw_cursor(struct dc_capture *capture, HDC hdc)
+{
+	HICON icon;
+	ICONINFO ii;
+	CURSORINFO *ci = &capture->ci;
+
+	if (!(capture->ci.flags & CURSOR_SHOWING))
+		return;
+
+	icon = CopyIcon(capture->ci.hCursor);
+	if (!icon)
+		return;
+
+	if (GetIconInfo(icon, &ii)) {
+		POINT pos;
+		pos.x = ci->ptScreenPos.x - (int)ii.xHotspot - capture->x;
+		pos.y = ci->ptScreenPos.y - (int)ii.yHotspot - capture->y;
+
+		DrawIcon(hdc, pos.x, pos.y, icon);
+
+		DeleteObject(ii.hbmColor);
+		DeleteObject(ii.hbmMask);
+	}
+
+	DestroyIcon(icon);
+}
+
+static inline HDC dc_capture_get_dc(struct dc_capture *capture)
+{
+	if (!capture->valid)
+		return NULL;
+
+	if (capture->compatibility)
+		return capture->hdc;
+	else
+		return texture_get_dc(capture->textures[capture->cur_tex]);
+}
+
+static inline void dc_capture_release_dc(struct dc_capture *capture)
+{
+	if (capture->compatibility) {
+		texture_setimage(capture->textures[capture->cur_tex],
+				capture->bits, capture->width*4, false);
+	} else {
+		texture_release_dc(capture->textures[capture->cur_tex]);
+	}
+}
+
+void dc_capture_capture(struct dc_capture *capture, HWND window)
+{
+	HDC hdc_target;
+	HDC hdc;
+
+	if (capture->capture_cursor) {
+		memset(&capture->ci, 0, sizeof(CURSORINFO));
+		capture->ci.cbSize = sizeof(CURSORINFO);
+		capture->cursor_captured = GetCursorInfo(&capture->ci);
+	}
+
+	if (++capture->cur_tex == capture->num_textures)
+		capture->cur_tex = 0;
+
+	hdc = dc_capture_get_dc(capture);
+	if (!hdc) {
+		blog(LOG_WARNING, "[capture_screen] Failed to get "
+		                  "texture DC");
+		return;
+	}
+
+	hdc_target = GetDC(window);
+
+	BitBlt(hdc, 0, 0, capture->width, capture->height,
+			hdc_target, capture->x, capture->y, SRCCOPY);
+
+	ReleaseDC(NULL, hdc_target);
+
+	if (capture->cursor_captured)
+		draw_cursor(capture, hdc);
+
+	dc_capture_release_dc(capture);
+
+	capture->textures_written[capture->cur_tex] = true;
+}
+
+static void draw_texture(struct dc_capture *capture, int id, effect_t effect)
+{
+	texture_t   texture = capture->textures[id];
+	technique_t tech    = effect_gettechnique(effect, "Draw");
+	eparam_t    image   = effect_getparambyname(effect, "image");
+	size_t      passes;
+
+	effect_settexture(effect, image, texture);
+
+	passes = technique_begin(tech);
+	for (size_t i = 0; i < passes; i++) {
+		if (technique_beginpass(tech, i)) {
+			if (capture->compatibility)
+				gs_draw_sprite(texture, GS_FLIP_V, 0, 0);
+			else
+				gs_draw_sprite(texture, 0, 0, 0);
+
+			technique_endpass(tech);
+		}
+	}
+	technique_end(tech);
+}
+
+void dc_capture_render(struct dc_capture *capture, effect_t effect)
+{
+	int last_tex = (capture->cur_tex > 0) ?
+		capture->cur_tex-1 : capture->num_textures-1;
+
+	if (!capture->valid)
+		return;
+
+	if (capture->textures_written[last_tex])
+		draw_texture(capture, last_tex, effect);
+}
+
+effect_t create_opaque_effect(void)
+{
+	effect_t opaque_effect;
+	char *effect_file;
+	char *error_string = NULL;
+
+	effect_file = obs_find_plugin_file("win-capture/opaque.effect");
+	if (!effect_file) {
+		blog(LOG_ERROR, "[create_opaque_effect] Could not find "
+		                "opaque effect file");
+		return false;
+	}
+
+	gs_entercontext(obs_graphics());
+
+	opaque_effect = gs_create_effect_from_file(effect_file, &error_string);
+
+	if (!opaque_effect) {
+		if (error_string)
+			blog(LOG_ERROR, "[create_opaque_effect] Failed to "
+			                "create opaque effect:\n%s",
+			                error_string);
+		else
+			blog(LOG_ERROR, "[create_opaque_effect] Failed to "
+			                "create opaque effect");
+	}
+
+	bfree(effect_file);
+	bfree(error_string);
+
+	gs_leavecontext();
+
+	return opaque_effect;
+}

+ 39 - 0
plugins/win-capture/dc-capture.h

@@ -0,0 +1,39 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <obs.h>
+
+#define NUM_TEXTURES 2
+
+struct dc_capture {
+	int        cur_tex;
+	texture_t  textures[NUM_TEXTURES];
+	bool       textures_written[NUM_TEXTURES];
+	int        x, y;
+	uint32_t   width;
+	uint32_t   height;
+	int        num_textures;
+
+	bool       compatibility;
+	HDC        hdc;
+	HBITMAP    bmp, old_bmp;
+	BYTE       *bits;
+
+	bool       capture_cursor;
+	bool       cursor_captured;
+	CURSORINFO ci;
+
+	bool       valid;
+};
+
+extern void dc_capture_init(struct dc_capture *capture, int x, int y,
+		uint32_t width, uint32_t height, bool cursor,
+		bool compatibility);
+extern void dc_capture_free(struct dc_capture *capture);
+
+extern void dc_capture_capture(struct dc_capture *capture, HWND window);
+extern void dc_capture_render(struct dc_capture *capture, effect_t effect);
+
+extern effect_t create_opaque_effect(void);

+ 147 - 0
plugins/win-capture/monitor-capture.c

@@ -0,0 +1,147 @@
+#include <util/dstr.h>
+#include "dc-capture.h"
+
+struct monitor_capture {
+	obs_source_t      source;
+
+	int               monitor;
+	bool              capture_cursor;
+	bool              compatibility;
+
+	struct dc_capture data;
+
+	effect_t          opaque_effect;
+};
+
+struct monitor_info {
+	int               cur_id;
+	int               desired_id;
+	int               id;
+	RECT              rect;
+};
+
+/* ------------------------------------------------------------------------- */
+
+static inline void do_log(int level, const char *msg, ...)
+{
+	va_list args;
+	struct dstr str = {0};
+
+	va_start(args, msg);
+
+	dstr_copy(&str, "[GDI monitor capture]: ");
+	dstr_vcatf(&str, msg, args);
+	blog(level, "%s", str.array);
+	dstr_free(&str);
+
+	va_end(args);
+}
+
+static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect,
+		LPARAM param)
+{
+	struct monitor_info *monitor = (struct monitor_info *)param;
+
+	if (monitor->cur_id == 0 || monitor->desired_id == monitor->cur_id) {
+		monitor->rect = *rect;
+		monitor->id   = monitor->cur_id;
+	}
+
+	return (monitor->desired_id < monitor->cur_id++);
+}
+
+static void update_monitor(struct monitor_capture *capture,
+		obs_data_t settings)
+{
+	struct monitor_info monitor = {0};
+	uint32_t width, height;
+
+	monitor.desired_id = (int)obs_data_getint(settings, "monitor");
+	EnumDisplayMonitors(NULL, NULL, enum_monitor, (LPARAM)&monitor);
+
+	capture->monitor = monitor.id;
+
+	width  = monitor.rect.right  - monitor.rect.left;
+	height = monitor.rect.bottom - monitor.rect.top;
+
+	dc_capture_init(&capture->data, monitor.rect.left, monitor.rect.top,
+			width, height, capture->capture_cursor,
+			capture->compatibility);
+}
+
+static inline void update_settings(struct monitor_capture *capture,
+		obs_data_t settings)
+{
+	capture->capture_cursor = obs_data_getbool(settings, "capture_cursor");
+	capture->compatibility  = obs_data_getbool(settings, "compatibility");
+
+	dc_capture_free(&capture->data);
+	update_monitor(capture, settings);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static const char *monitor_capture_getname(const char *locale)
+{
+	/* TODO: translate */
+	return "Monitor Capture";
+}
+
+static void monitor_capture_destroy(void *data)
+{
+	struct monitor_capture *capture = data;
+
+	gs_entercontext(obs_graphics());
+
+	dc_capture_free(&capture->data);
+	effect_destroy(capture->opaque_effect);
+
+	gs_leavecontext();
+
+	bfree(capture);
+}
+
+static void *monitor_capture_create(obs_data_t settings, obs_source_t source)
+{
+	struct monitor_capture *capture;
+	effect_t opaque_effect = create_opaque_effect();
+
+	if (!opaque_effect)
+		return NULL;
+
+	capture = bzalloc(sizeof(struct monitor_capture));
+	capture->opaque_effect = opaque_effect;
+
+	obs_data_set_default_int(settings, "monitor", 0);
+	obs_data_set_default_bool(settings, "capture_cursor", true);
+	obs_data_set_default_bool(settings, "compatibility", false);
+	update_settings(capture, settings);
+
+	return capture;
+}
+
+static void monitor_capture_tick(void *data, float seconds)
+{
+	struct monitor_capture *capture = data;
+
+	gs_entercontext(obs_graphics());
+	dc_capture_capture(&capture->data, NULL);
+	gs_leavecontext();
+}
+
+static void monitor_capture_render(void *data, effect_t effect)
+{
+	struct monitor_capture *capture = data;
+	dc_capture_render(&capture->data, capture->opaque_effect);
+}
+
+struct obs_source_info monitor_capture_info = {
+	.id           = "monitor_capture",
+	.type         = OBS_SOURCE_TYPE_INPUT,
+	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
+	.getname      = monitor_capture_getname,
+	.create       = monitor_capture_create,
+	.destroy      = monitor_capture_destroy,
+	.video_render = monitor_capture_render,
+	.video_tick   = monitor_capture_tick
+};

+ 13 - 0
plugins/win-capture/plugin-main.c

@@ -0,0 +1,13 @@
+#include <obs-module.h>
+
+OBS_DECLARE_MODULE()
+
+extern struct obs_source_info monitor_capture_info;
+
+bool obs_module_load(uint32_t libobs_ver)
+{
+	obs_register_source(&monitor_capture_info);
+
+	UNUSED_PARAMETER(libobs_ver);
+	return true;
+}

+ 10 - 0
vs/2013/OBS.sln

@@ -41,6 +41,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obs-studio", "obs-studio\ob
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-wasapi", "win-wasapi\win-wasapi.vcxproj", "{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-capture", "win-capture\win-capture.vcxproj", "{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -129,6 +131,14 @@ Global
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Win32.Build.0 = Release|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.ActiveCfg = Release|x64
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.Build.0 = Release|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.ActiveCfg = Debug|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.Build.0 = Debug|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.ActiveCfg = Debug|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.Build.0 = Debug|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.ActiveCfg = Release|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.Build.0 = Release|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.ActiveCfg = Release|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 4 - 4
vs/2013/libobs/libobs.vcxproj

@@ -207,7 +207,7 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
@@ -228,7 +228,7 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
@@ -253,7 +253,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
@@ -278,7 +278,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>

+ 176 - 0
vs/2013/win-capture/win-capture.vcxproj

@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>wincapture</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\win-capture\dc-capture.c" />
+    <ClCompile Include="..\..\..\plugins\win-capture\monitor-capture.c" />
+    <ClCompile Include="..\..\..\plugins\win-capture\plugin-main.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\plugins\win-capture\dc-capture.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 33 - 0
vs/2013/win-capture/win-capture.vcxproj.filters

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\win-capture\monitor-capture.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\win-capture\plugin-main.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\win-capture\dc-capture.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\plugins\win-capture\dc-capture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>