From 4f7ab552df3bb27afacb000cc1578dc549b57fde Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 5 Mar 2014 10:43:14 -0700 Subject: [PATCH] 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. --- .../obs-plugins/win-capture/opaque.effect | 35 +++ libobs-d3d11/d3d11-subsystem.cpp | 61 +++++ libobs-d3d11/d3d11-texture2d.cpp | 10 + libobs-opengl/gl-windows.c | 5 + libobs/CMakeLists.txt | 3 +- libobs/graphics/graphics-imports.c | 9 + libobs/graphics/graphics-internal.h | 10 + libobs/graphics/graphics.c | 45 ++++ libobs/graphics/graphics.h | 14 + libobs/media-io/video-io.c | 9 +- libobs/util/platform-windows.c | 15 +- obs/obs-app.cpp | 2 +- obs/window-basic-main.cpp | 1 + plugins/CMakeLists.txt | 1 + plugins/win-capture/CMakeLists.txt | 18 ++ plugins/win-capture/dc-capture.c | 239 ++++++++++++++++++ plugins/win-capture/dc-capture.h | 39 +++ plugins/win-capture/monitor-capture.c | 147 +++++++++++ plugins/win-capture/plugin-main.c | 13 + vs/2013/OBS.sln | 10 + vs/2013/libobs/libobs.vcxproj | 8 +- vs/2013/win-capture/win-capture.vcxproj | 176 +++++++++++++ .../win-capture/win-capture.vcxproj.filters | 33 +++ 23 files changed, 892 insertions(+), 11 deletions(-) create mode 100644 build/data/obs-plugins/win-capture/opaque.effect create mode 100644 plugins/win-capture/CMakeLists.txt create mode 100644 plugins/win-capture/dc-capture.c create mode 100644 plugins/win-capture/dc-capture.h create mode 100644 plugins/win-capture/monitor-capture.c create mode 100644 plugins/win-capture/plugin-main.c create mode 100644 vs/2013/win-capture/win-capture.vcxproj create mode 100644 vs/2013/win-capture/win-capture.vcxproj.filters diff --git a/build/data/obs-plugins/win-capture/opaque.effect b/build/data/obs-plugins/win-capture/opaque.effect new file mode 100644 index 000000000..49b1dc4c7 --- /dev/null +++ b/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); + } +} diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 8fee49ab1..19478d288 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/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(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(tex); + if (!TextureGDICompatible(tex2d, "texture_release_dc")) + return; + + tex2d->gdiSurface->ReleaseDC(nullptr); +} diff --git a/libobs-d3d11/d3d11-texture2d.cpp b/libobs-d3d11/d3d11-texture2d.cpp index 6cd37c5f1..502036e6f 100644 --- a/libobs-d3d11/d3d11-texture2d.cpp +++ b/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() diff --git a/libobs-opengl/gl-windows.c b/libobs-opengl/gl-windows.c index 9f86e7d5a..b041bb4f3 100644 --- a/libobs-opengl/gl-windows.c +++ b/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; +} diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 05170aa6c..c1dc404f9 100644 --- a/libobs/CMakeLists.txt +++ b/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 diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 192d96fd2..51d7b0030 100644 --- a/libobs/graphics/graphics-imports.c +++ b/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; } diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 3f3f1605e..5990b78fc 100644 --- a/libobs/graphics/graphics-internal.h +++ b/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 { diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index 97628d779..693afac24 100644 --- a/libobs/graphics/graphics.c +++ b/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 diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 3b561569b..3f77db655 100644 --- a/libobs/graphics/graphics.h +++ b/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) diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index f51ed85d8..9910728b6 100644 --- a/libobs/media-io/video-io.c +++ b/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); diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 3a06f831d..d651f72c8 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -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 diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index 64073c42f..6c4f9d3dc 100644 --- a/obs/obs-app.cpp +++ b/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; diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 42b8c8d62..254be79a8 100644 --- a/obs/window-basic-main.cpp +++ b/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 */ diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e1566613b..101b5953a 100644 --- a/plugins/CMakeLists.txt +++ b/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() diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt new file mode 100644 index 000000000..c1bdec931 --- /dev/null +++ b/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) diff --git a/plugins/win-capture/dc-capture.c b/plugins/win-capture/dc-capture.c new file mode 100644 index 000000000..534d0a09f --- /dev/null +++ b/plugins/win-capture/dc-capture.c @@ -0,0 +1,239 @@ +#include "dc-capture.h" + +#define WIN32_MEAN_AND_LEAN +#include + +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; +} diff --git a/plugins/win-capture/dc-capture.h b/plugins/win-capture/dc-capture.h new file mode 100644 index 000000000..44930bf58 --- /dev/null +++ b/plugins/win-capture/dc-capture.h @@ -0,0 +1,39 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +#include + +#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); diff --git a/plugins/win-capture/monitor-capture.c b/plugins/win-capture/monitor-capture.c new file mode 100644 index 000000000..6ba27ae6c --- /dev/null +++ b/plugins/win-capture/monitor-capture.c @@ -0,0 +1,147 @@ +#include +#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 +}; diff --git a/plugins/win-capture/plugin-main.c b/plugins/win-capture/plugin-main.c new file mode 100644 index 000000000..28f12e0a9 --- /dev/null +++ b/plugins/win-capture/plugin-main.c @@ -0,0 +1,13 @@ +#include + +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; +} diff --git a/vs/2013/OBS.sln b/vs/2013/OBS.sln index c4a83d630..1fa95e3f1 100644 --- a/vs/2013/OBS.sln +++ b/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 diff --git a/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj index ff7d4586c..a47857f93 100644 --- a/vs/2013/libobs/libobs.vcxproj +++ b/vs/2013/libobs/libobs.vcxproj @@ -207,7 +207,7 @@ Windows true - avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -228,7 +228,7 @@ Windows true - avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -253,7 +253,7 @@ true true true - avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -278,7 +278,7 @@ true true true - avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) + winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) diff --git a/vs/2013/win-capture/win-capture.vcxproj b/vs/2013/win-capture/win-capture.vcxproj new file mode 100644 index 000000000..3363ce9f3 --- /dev/null +++ b/vs/2013/win-capture/win-capture.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {AB83E5F0-D76E-45AB-A4C9-4711B1BE6916} + Win32Proj + wincapture + + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions) + ../../../libobs + + + Windows + true + $(OutDir);%(AdditionalLibraryDirectories) + libobs.lib;%(AdditionalDependencies) + + + copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)" + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions) + ../../../libobs + + + Windows + true + $(OutDir);%(AdditionalLibraryDirectories) + libobs.lib;%(AdditionalDependencies) + + + copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)" + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions) + ../../../libobs + + + Windows + true + true + true + $(OutDir);%(AdditionalLibraryDirectories) + libobs.lib;%(AdditionalDependencies) + + + copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)" + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WINCAPTURE_EXPORTS;%(PreprocessorDefinitions) + ../../../libobs + + + Windows + true + true + true + $(OutDir);%(AdditionalLibraryDirectories) + libobs.lib;%(AdditionalDependencies) + + + copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)" + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vs/2013/win-capture/win-capture.vcxproj.filters b/vs/2013/win-capture/win-capture.vcxproj.filters new file mode 100644 index 000000000..6d3099809 --- /dev/null +++ b/vs/2013/win-capture/win-capture.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file