OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作
Posted chen_song_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作相关的知识,希望对你有一定的参考价值。
OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作
OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作
- OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作
- 前言
- 一、 OBS中三种采集说明
- 二、游戏窗口采集 graphics-hook 的源码分析
- 三、 OpenGL与D3D11资源共享 进行互操作
OpenGL与D3D11的GPU资源进行互操作哈 !!!
前言
云渲染客户端 采集的工作 所以分析OBS中源码
提示:以下是本篇文章正文内容,下面案例可供参考
一、 OBS中三种采集说明
OBS中采集有 桌面、窗口和游戏采集
1、桌面
dxgi
2、窗口
METHOD_AUTO,
METHOD_BITBLT, // 位图
METHOD_WGC, // 从GPU中抓取数据
3、游戏
其中游戏采集是独立一套注入的工具 ,因为游戏本身对渲染的帧数要求非常高, 使用,使用注入的方法,主要使用GPU资源共享的模式, 达到渲染流畅的
这个OpenGL提供接口与D3D11进行资源进行互操作 WGL_NV_DX_interop2.txt
二、游戏窗口采集 graphics-hook 的源码分析
1、 环境初始化工作
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID unused1)
if (reason == DLL_PROCESS_ATTACH)
wchar_t name[MAX_PATH];
dll_inst = hinst;
if (!init_dll())
DbgOut("[OBS] Duplicate hook library");
return false;
HANDLE cur_thread;
bool success = DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(), &cur_thread,
SYNCHRONIZE, false, 0);
if (!success)
DbgOut("[OBS] Failed to get current thread handle");
// 1. 信号是obs中进程发送要控制图形进程的指令
if (!init_signals())
return false;
// 2. 获取系统的动态库的路径
if (!init_system_path())
return false;
// 3. 与obs控制进程同享一块内存地址 一些内存
if (!init_hook_info())
return false;
if (!init_mutexes())
return false;
/* this prevents the library from being automatically unloaded
* by the next FreeLibrary call */
GetModuleFileNameW(hinst, name, MAX_PATH);
LoadLibraryW(name);
capture_thread = CreateThread(
NULL, 0, (LPTHREAD_START_ROUTINE)main_capture_thread,
(LPVOID)cur_thread, 0, 0);
if (!capture_thread)
CloseHandle(cur_thread);
return false;
else if (reason == DLL_PROCESS_DETACH)
if (!dup_hook_mutex)
return true;
if (capture_thread)
stop_loop = true;
WaitForSingleObject(capture_thread, 300);
CloseHandle(capture_thread);
free_hook();
(void)unused1;
return true;
static inline bool init_signals(void)
DWORD pid = GetCurrentProcessId();
//1 、从新启动
signal_restart = init_event(EVENT_CAPTURE_RESTART, pid);
if (!signal_restart)
return false;
// 2、停止的信令
signal_stop = init_event(EVENT_CAPTURE_STOP, pid);
if (!signal_stop)
return false;
// 3、准备的信令
signal_ready = init_event(EVENT_HOOK_READY, pid);
if (!signal_ready)
return false;
// 4、退出的信令
signal_exit = init_event(EVENT_HOOK_EXIT, pid);
if (!signal_exit)
return false;
//5、初始化的信令
signal_init = init_event(EVENT_HOOK_INIT, pid);
if (!signal_init)
return false;
return true;
static inline bool init_hook(HANDLE thread_handle)
// 等待一下
wait_for_dll_main_finish(thread_handle);
_snwprintf(keepalive_name, sizeof(keepalive_name) / sizeof(wchar_t),
L"%s%lu", WINDOW_HOOK_KEEPALIVE, GetCurrentProcessId());
// obs要获取图形进程数据发送信令指令通过管道发送指令的哈
init_pipe();
init_dummy_window_thread();
log_current_process();
// 发送 从新启动信令
SetEvent(signal_restart);
return true;
2、 主要attempt_hook中进行hook操作抓取数据
看OpenGL中hook
bool hook_gl(void)
void *wgl_dc_proc;
void *wgl_slb_proc;
void *wgl_sb_proc;
gl = get_system_module("opengl32.dll");
if (!gl)
return false;
/* "life is feudal: your own" somehow uses both opengl and directx at
* the same time, so blacklist it from capturing opengl */
const char *process_name = get_process_name();
if (_strcmpi(process_name, "yo_cm_client.exe") == 0 ||
_strcmpi(process_name, "cm_client.exe") == 0)
hlog("Ignoring opengl for game: %s", process_name);
return true;
if (!gl_register_window())
return true;
// win的窗口的api的两个缓冲区的地址hook
wgl_dc_proc = base_get_proc("wglDeleteContext");
wgl_slb_proc = base_get_proc("wglSwapLayerBuffers");
wgl_sb_proc = base_get_proc("wglSwapBuffers");
DetourTransactionBegin();
RealSwapBuffers = SwapBuffers;
DetourAttach((PVOID *)&RealSwapBuffers, hook_swap_buffers);
if (wgl_dc_proc)
RealWglDeleteContext = (PFN_WglDeleteContext)wgl_dc_proc;
DetourAttach((PVOID *)&RealWglDeleteContext,
hook_wgl_delete_context);
if (wgl_slb_proc)
RealWglSwapLayerBuffers = (PFN_WglSwapLayerBuffers)wgl_slb_proc;
DetourAttach((PVOID *)&RealWglSwapLayerBuffers,
hook_wgl_swap_layer_buffers);
if (wgl_sb_proc)
RealWglSwapBuffers = (PFN_WglSwapBuffers)wgl_sb_proc;
DetourAttach((PVOID *)&RealWglSwapBuffers,
hook_wgl_swap_buffers);
const LONG error = DetourTransactionCommit();
const bool success = error == NO_ERROR;
if (success)
hlog("Hooked SwapBuffers");
if (RealWglDeleteContext)
hlog("Hooked wglDeleteContext");
if (RealWglSwapLayerBuffers)
hlog("Hooked wglSwapLayerBuffers");
if (RealWglSwapBuffers)
hlog("Hooked wglSwapBuffers");
hlog("Hooked GL");
else
RealSwapBuffers = NULL;
RealWglDeleteContext = NULL;
RealWglSwapLayerBuffers = NULL;
RealWglSwapBuffers = NULL;
hlog("Failed to attach Detours hook: %ld", error);
return success;
3、 gl_shtex_init 初始化d3d11与OpenGL的GPU资源访问
static bool gl_shtex_init(HWND window)
// 1、 这个没有看懂 哭了
if (!gl_shtex_init_window())
return false;
// 2、创建设备与GPU交换 、交换链
if (!gl_shtex_init_d3d11())
return false;
// 3、创建ID3D11Texture2D 并设置同享模式 用于其他进程可以发送 跨进程访问 -》 这里主要是obs显示访问这个GPU的内存
if (!gl_shtex_init_d3d11_tex())
return false;
// 4、把d3d11的GPU的资源可以给OpenGL访问
if (!gl_shtex_init_gl_tex())
return false;
// 5、分配 fbo的GPU的内存
if (!gl_init_fbo())
return false;
// 6、 把同享地址设置到共享变量中去 data.shtex_info、 data.handle
if (!capture_init_shtex(&data.shtex_info, window, data.cx, data.cy,
data.format, true, (uintptr_t)data.handle))
return false;
hlog("gl shared texture capture successful");
return true;
4、 进行拷贝GPU数据gl_shtex_capture
static void gl_shtex_capture(void)
GLint last_fbo;
GLint last_tex;
jimglDXLockObjectsNV(data.gl_device, 1, &data.gl_dxobj);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fbo);
if (gl_error("gl_shtex_capture", "failed to get last fbo"))
return;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
if (gl_error("gl_shtex_capture", "failed to get last texture"))
return;
gl_copy_backbuffer(data.texture);
glBindTexture(GL_TEXTURE_2D, last_tex);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, last_fbo);
jimglDXUnlockObjectsNV(data.gl_device, 1, &data.gl_dxobj);
IDXGISwapChain_Present(data.dxgi_swap, 0, 0);
//创建渲染目标视图
/*ID3D11Texture2D *pBackBuffer = NULL;
IDXGISwapChain_GetBuffer(data.dxgi_swap, 0, __uuidof(ID3D11Texture2D),
(LPVOID *)&pBackBuffer);*/
三、 OpenGL与D3D11资源共享 进行互操作
神奇扩展WGL_NV_DX_interop的 出现,使得OpenGL可以正式与D3D进行互操作
//Example: Render to Direct3D 11 backbuffer with openGL.
// create D3D11 device, context and swap chain.
ID3D11Device *device;
ID3D11DeviceContext *devCtx;
IDXGISwapChain *swapChain;
DXGI_SWAP_CHAIN_DESC scd;
<set appropriate swap chain parameters in scd>
hr = D3D11CreateDeviceAndSwapChain(NULL, // pAdapter
D3D_DRIVER_TYPE_HARDWARE, // DriverType
NULL, // Software
0, // Flags (Do not set D3D11_CREATE_DEVICE_SINGLETHREADED)
NULL, // pFeatureLevels
0, // FeatureLevels
D3D11_SDK_VERSION, // SDKVersion
&scd, // pSwapChainDesc
&swapChain, // ppSwapChain
&device, // ppDevice
NULL, // pFeatureLevel
&devCtx); // ppImmediateContext
// Fetch the swapchain backbuffer
ID3D11Texture2D *dxColorbuffer;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
// Create depth stencil texture
ID3D11Texture2D *dxDepthBuffer;
D3D11_TEXTURE2D_DESC depthDesc;
depthDesc.Usage = D3D11_USAGE_DEFAULT;
// <set other depthDesc parameters appropriately>
// Create Views
ID3D11RenderTargetView *colorBufferView;
D3D11_RENDER_TARGET_VIEW_DESC rtd;
<set rtd parameters appropriately>
device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
ID3D11DepthStencilView *depthBufferView;
D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
<set dsd parameters appropriately>
device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
// Attach back buffer and depth texture to redertarget for the device.
devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
// Register D3D11 device with GL
HANDLE gl_handleD3D;
// 把D3D设备注册给OpenGL
gl_handleD3D = wglDXOpenDeviceNV(device);
// register the Direct3D color and depth/stencil buffers as
// renderbuffers in opengl
GLuint gl_names[2];
HANDLE gl_handles[2];
glGenRenderbuffers(2, gl_names);
把D3Drender target注册成OpenGL纹理对象
gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
gl_names[0],
GL_RENDERBUFFER,
WGL_ACCESS_READ_WRITE_NV);
gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
gl_names[1],
GL_RENDERBUFFER,
WGL_ACCESS_READ_WRITE_NV);
// attach the Direct3D buffers to an FBO
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, gl_names[0]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, gl_names[1]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, gl_names[1]);
// D3D和OpenGL渲到同一个render target
while (!done)
// 现在纹理就可以当成普通的OpenGL纹理来用了
// 和平常一样进行D3D渲染
<direct3d renders to the render targets>
// lock the render targets for GL access
// 锁定render target,交给OpenGL
wglDXLockObjectsNVX(handleD3D, 2, gl_handles);
// 和平常一样进行OpenGL渲染
<opengl renders to the render targets>
// unlock the render targets
wglDXUnlockObjectsNVX(handleD3D, 2, gl_handles);
<direct3d renders to the render targets and presents
the results on the screen>
vs2015编译OBS-Studio
编译之前的准备:
系统win10
QT5.7.0
VS2015
CMake 3.13.4
obs vs2015环境依赖包:dependencies2015
obs-studio 24.0
===========================================
1.下载源码:
https://github.com/obsproject/obs-studio/archive/24.0.0.zip
解压到比如D盘xx下code目录内
然后再xx下新建build目录
2.下载依赖包:
https://obsproject.com/downloads/dependencies2015.zip
解压之后同样发到xx目录下
3.打开cmake:
分别设置source code 和build 的路径就是上面xx目录下的code 和build目录
然后点击 ADD ENTRY 添加两个依赖:
(1)QTDIR 然后设置qt的目录指定到例如:D:QtQt5.7.05.7msvc2015
(2)DepsPath 设置 vs2015依赖项的路径比如:D:projectvsobsOBSStudio24.0dependencies2015win32 (这里是win32)
如图:
(3)设置好之后点击上面的 Configure 然后选择v140 vs2015
之后再点击一下 Generate 就搞定了。
4.打开build 目录内的 obs-studio.sln
然后 编译 ALL_BUILD
------------------------------------------------------------------------------------------------
可能会出现的编译错误见上一篇 :
vs2015 编译obs studio 遇到的几个错误
----------------------------------------------------------------------------------------------------------------------
备注:编译成功之后 设置obs 为启动项
然后右键点击 obs 设置:调试---------工作目录:.. undirDebugin32bit
就可以断点调试
以上是关于OBS Studio 窗口采集game-capture注入之OpenGL与D3D11的GPU资源进行互操作的主要内容,如果未能解决你的问题,请参考以下文章