通过上图你可以看到,在 Media-server-node 项目中,用红框框出来的四个目录比较重要。其中**src 目录**中存放的是 C++ 语言实现的 Native 代码,用于生成 JavaScript 脚本可以调用的 C++ 接口,从而使 JavaScript 编写的业务层代码可以调用 Medooze 核心层的 C++代码;**lib 目录**里存放的是在 Node.js 端执行的,控制 Medooze 业务逻辑的 JavaScript 代码;**external 目录**中存放的是 Medooze 使用的第三方库,如 MP4v2 和 SRTP;**media-server 目录**是一个比较重要的目录,它里面存放的是 **Medooze 的核心代码,即 C++ 实现的流媒体服务器代码**。
这里需要注意的是,Media-server-node 中的 media-server 目录是一个独立的项目,它是通过外部链接引用到Media-server-node项目中的。它既可以随 Media-server-node 一起编译,也可以自己单独编译。对于这一点我们后面还会做详细介绍。
### 1. 构建Media-server-node项目
构建 Media-server-node 项目非常简单,只需要执行下面的命令即可构建成功:
```
npm install medooze-media-server --save
```
实际上,在构建 Media-server-node 项目时,重点是对上面三个 C/C++ 目录(即 external、media-server、src)中的 Native 代码的构建。项目构建时会调用 node-gyp 命令将三个目录中的 C++ 代码编译成 Node.js 的 Native 插件,以供 JavaScript 脚本使用。
>
另外,Media-server-node 目录中的 binding.gyp 文件就是供node-gyp使用来构建C++ Navtie 代码的规则文件。在执行上面的构建命令时,底层会调用 node-gyp命令,node-gyp 以binding.gyp为输入,然后根据 binding.gyp 中的规则对C++ Native代码进行构建的。
下面我们就来看看,node-gyp 是如构建 C++ Native代码的吧。
### 2. node-gyp & GYP
首先我们来了解一下node-gyp,**node-gyp 是一个由 Node.js 执行的脚本程序,是专门用于生成在 Node.js 中运行的 C/C++ Native 插件的工具**。
实际上,你可以简单地将 node-gyp 认为是 gyp 和 Make/Ninja 工具的集合,当构建某个项目时,在底层node-gyp先使用 gyp 工具根据 binding.gyp 中的规则生成Makefile或 build.ninja文件,然后再调用 Make/Ninja 命令进行构建,编译出 Native 插件。
那上面说的GYP又是什么呢?GYP(Generate Your Projects)是 Chromium 团队使用 Python 语言开发的构建工具,它以 .gyp 为输入文件,可以产生不同平台的 IDE 工程文件,如VS 工程;或产生各种编译文件,如 Makefile 。
通过上面的描述你可以知道,存在着两个层面的规则文件,.gyp 是由 GYP工具使用的项目规则文件,Makefile/build.ninja是由 Make/Ninja 使用的编译规则文件,我们称它为编译文件。另外,Make/Ninja 命令相对更底层一些,它们执行时会直接调用编译器(如 GCC/G++)对源码进行编译。而 gyp命令更偏项目管理一些,它是为了产生各种工程或Makefile/build.ninja文件而存在的。
有很多同学对 node-gyp、GYP、Make、Ninja 这些工具是什么关系分不清,通过上面的讲解你应该就清楚它们之间的区别了。下面这张图将它们之间的关系描述得更加清晰:
通过上图我们可以看到 gyp 命令是将 binding.gyp文件生成Makefile文件,然后交给 make,最终将 Native 插件编译出来的。
了解了 node-gyp 和 GYP 之间的关系之后,我们再来了解一下 GYP 规则的语法。首先我们要知道 GYP 规则文件是以 JSON 格式存储的。另外,在 GYP 的规则文件中,它按有效范围将规则分为了两大类,即全局规则和局部规则。下面我们就以 binding.gyp 为例,看看它是如何使用这些规则的。
所谓全局规则就是指这些规则在整个文件内有效的规则,我们看一下代码吧:
代码中的 **variables** 用于在GYP规则文件中定义全局变量,这些全局变量被定义好后,就可以在规则文件中的任何地方被引用了。
GYP中除了**variables**外,还定义了其他几个全局规则,具体的内容可以查看文末的参考一节,这里就不一一列出了。
在规则文件中最重要的规则要数 target了,它属于局部规则,在 binding.gyp 文件中的 target 描述如下所示:
```
"targets":
[
{
"target_name": "medooze-media-server",
"type": "static_library",
"cflags": //设置编译器编译参数
[
...
"-O3",
"-g",
...
],
"ldflags" : [" -lpthread -lresolv"], //设置编译器连接参数
"include_dirs" : //项目需要的头文件目录
[
'/usr/include/nodejs/',
"<!(node -e \"require('nan')\")"
],
"link_settings":
{
'libraries': ["-lpthread -lpthread -lresolv"] //链接时使用的第三方库
},
"sources": //所有需要编译的源码文件
[
"src/media-server_wrap.cxx",
...
],
"dependencies":[ //指定依赖的其他的 target
...
.
],
"conditions" : [ //编译条件
["target_arch=='ia32'", {
...
}],
...
['OS=="linux"',{
...
}],
],
}
]
```
下面我就向你详细讲解一下 target 中每个规则的作用和含义。
**target_name** ,是target 的名字,在一个 .gyp 文件中,名字必须是唯一的,这里是 medooze-media-server。当使用 GYP 生成工程文件时,如 VS 工程或XCode工程,target_name 就是各工程文件的名字。
**type**,指明了生成的 target 的类型,你可以设置为以下几种类型:executable 表示要生成可执行程序,在 Windows 中就是 exe 文件;static_library 表示要生成静态库,生成的文件为 `*.a` 或者是`*.lib` 后辍的文件;shared_library 表示要生成共享库,也就是文件后辍名为.so 或者 .dll 的文件;none,表示为无类型,该类型留作特殊用途使用。
……
由于篇幅的原因,其他规则就不在这里一一列举了,如果你对它们感兴趣的话可以查看后面的参考一节。
通过上面的描述可以知道,在调用 npm 构建 Media-server-node 项目时,在它的内部会调用 node-gyp 命令,该命令以 binding.gyp 为输入文件,按照该文件中的规则生成 Makefile 或 build.ninja 文件,最后使用 Make/Ninja 命令来编译 C/C++ 的 Native 代码。这就是 node-gyp 执行编译的基本过程。
## 单独构建media-server项目
media-server 是 Medooze 流媒体服务器部分的实现,它用 C++ 实现。由于采用了 C++17 语法,所以需要使用较高版本 GCC 编译器。接下来我们就来看看该如何单独构建 Medooze 的 media-server 项目。
>
我的构建环境如下,操作系统 Ubuntu18.04 ,编译器版本GCC 7.3.0 。
### 1. 安装依赖库
由于 Medooze 不仅支持 SFU,而且还支持 MCU 功能,所以它依赖音视频的编解码库和 FFmpeg 库。除此之外,为了与浏览器对接,它还依赖 libssl 库。因此,在构建 media-server 之前我们需要先将这些依赖库安装好。
安装依赖库的命令如下:
```
sudo apt-get install libxmlrpc-c++8-dev
sudo apt-get install libgsm1-dev
sudo apt-get install libspeex-dev
sudo apt-get install libopus-dev
sudo apt-get install libavresample-dev
sudo apt-get install libx264-dev
sudo apt-get install libvpx-dev
sudo apt-get install libswscale-dev
sudo apt-get install libavformat-dev
sudo apt-get install libmp4v2-dev
sudo apt-get install libgcrypt11-dev
sudo apt-get install libssl1.0-dev
```
安装好上面的依赖库后,我们就可以从 GitHub上获取 media-server 项目的代码进行编译了。media-server 项目的源代码地址为:[https://github.com/medooze/media-server.git](https://github.com/medooze/media-server.git) 。
>
需要注意是的,获取代码时,你需要切换到 optimizations 分支。我最开始使用的是 master 分支,但在这个分支上会遇到问题,切换到optimizations
分支后就没有任何问题了。