OBS源码解读1:源码编译

1,870 阅读4分钟

本系列假设读者已经掌握以下知识:

  • C 和 C++
  • 使用 Visual Studio 开发 C/C++ 项目
  • 使用 OBS

本系列使用的 OBS 版本和开发环境:

本文将介绍如何从源码编译出OBS可执行文件,然后概述其项目结构,最后分享一些阅读源码的小技巧。

Build

作为《OBS源码解读》系列的开篇,我们必须先学会如何从源码编译 OBS 项目,因为在后续的学习中,我们会通过打断点、改代码的方式来逐步解读 OBS 的源码。

作为一个现代的 C++ 项目,OBS 的编译其实并不难。我们可以直接按照官方文档的步骤生成项目。

注意事项:

  • 除非需要生成旧版本的工程,否则不要参考官网以外的其它教程,因为官方文档可能会随着版本的升级而修改
  • 如果你以前已经生成过工程,想升级OBS,那么你依然应该严格按照最新的官方文档来生成,注意依赖库的版本变化,尤其是文档里面提供的依赖库下载链接应该重新下载,不要用以前下载好的
  • 截至v27.0.1版本,由于官方提供的 cef 包只有 Release 版,没有 Debug 版,使用 CMake 进行 configure 的时候可能会提示找不到 CEFWRAPPER_LIBRARY_DEBUG,此时虽然可以生成项目,但是后续选择 Debug 模式会运行失败。有3个解决方案:
    1. 如果不需要 Debug 模式,就忽略这个提示,直接用 Release 模式进行构建即可;
    2. 自己构建一个 Debug 版的 cef,注意 CEFWRAPPER_LIBRARY_DEBUG 要改到自己构建出来的目录;
    3. 如果不需要 Browser 插件,就取消选择 BUILD_BROWSER,这样也就不需要 cef 插件了;本系列不打算讲 Browser 插件,因此不折腾 cef 了,就采用这种方式

编译和调试运行:

选择 Debugx64 模式,调试运行。如果运行错误,重新看一下官方文档和上面的注意事项。

项目结构概览

项目结构.png

接下来简单介绍一下项目结构。

OBS 最核心的代码在“core/libobs”项目,我们称之为 OBS 的“后端”。对应的,还有一个“前端”项目,即 “frontend/obs”。关于“后端”和“前端”的架构以及其它模块的功能,将在下一章《核心概念》里面介绍,现在只需要有一个大概的印象,不必纠结其名字和意义。

  • core
    • libobs OBS的“后端”
  • deps 第三方依赖,本系列将在后期介绍其中的 jansson 库和 pthreads 库,但不做深度解读
  • frontend
    • obs OBS的“前端”
  • plugins 内置插件,本系列只挑选其中比较重要的几个插件介绍
  • scripts 脚本支持,本系列不介绍

阅读源码的小技巧

在开始解读源码之前,我先给大家讲几个阅读源码的小技巧。

以下小技巧是我自己在独自摸索的过程中总结出来的,列出来是为了让大家少走弯路,用最短的时间读懂源码、进行二次开发。读者可以先大概看一下。你可能不理解其中的意思,没有关系,现在不需要理解,也不需要记住。只要有个印象即可,等有需要的时候再回来看一看。

  1. 用 Debug 模式才方便在不改动项目属性的情况下调试。本文不打算介绍怎么用 Release 模式调试,熟悉 VS 的读者应该可以自己搞定,不熟悉 VS 的读者,我建议就不要折腾了(实在想折腾的,欢迎私信留言)。
  2. 某个结构体的 info 成员通常指向某个插件内定义的结构体,例如 output->info 指向的是某个插件定义的 output 类型的结构体。如果要找到 info 的成员函数的源码,要先确定 info 具体属于哪种类型,然后去 plugins 里面找。
  3. 反过来,如果想知道某个插件定义的 source/output/encoder 等类型结构体的某个成员函数的引用,可以用“Shift + F12”查找;如果找不到,可以尝试搜索 info.成员函数名
  4. 接下来的每一章的流程图中展示了一些重要的函数调用关系,方便在VS中用“F12”一步步地查看。
  5. obs 是一个全局对象,很多线程或函数中会直接使用,或者作为参数传递。它的初始化过程详见“启动流程”。
  6. 当你看不懂某个流程的时候,回过头来看看核心概念,源码中各种变量、类型或函数的命名与核心概念息息相关。
  7. 多线程读写需要加互斥锁,使用的方法是 pthread_mutex_lockpthread_mutex_unlock;反过来,如果你看到某个变量的访问前后加了这两个方法,说明这个变量需要多线程读写。如果你要修改某处的代码,一定要注意你读写的变量是不是线程安全的。