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资源进行互操作


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进行互操作

OpenGL地址


 //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资源进行互操作的主要内容,如果未能解决你的问题,请参考以下文章

OBS-studio 显示器采集黑屏 Windows10笔记本 解决方案

OBS窗口捕获

obs怎么捕捉手机屏幕,在手机直播王者荣耀的

OBS源码分析流程梳理

OBS Studio使用教程/工具高速下载

OBS数据采集及渲染过程