Ubuntu 下编 Live555 只有三行命令:解包、选配置、make。 真正要理解的是「为什么必须按这个顺序编」。
8.1 用的是编好的示例程序,这一节讲怎么把它们编出来。 8.2.2 是同一件事在 Windows 下的做法,坑完全不同。
一、三行命令
官方给的步骤就三步:
① 解包:tar xzf live555-latest.tar.gz
得到一个叫 live 的目录。
② 选配置:./genMakefiles linux
它按 config.linux 生成每个子目录的 Makefile。
③ 编译:make -j8
在根目录跑一次,所有子目录依次编完。
第 ② 步的参数不要照抄教程。
先 ls config.* 看一眼你这版源码里到底有哪些可选值。
不同年份的发布包,可用的名字是不一样的——教程里写的那个名字,在新版可能已经改名或被删掉。
名字给错的表现是「找不到 config.xxx 文件」,很直接。
二、为什么必须按这个顺序
源码包解开是四个库加一堆示例程序。 库之间是有依赖的,顺序错了就找不到符号。
① UsageEnvironment:最底层的环境抽象。
整个包里只有它谁也不依赖,所以永远第一个编。
② groupsock:组播与套接字封装。
它要 UsageEnvironment 里的日志与任务调度。
③ BasicUsageEnvironment:前两个的具体实现。
它把抽象的接口填成能跑的东西。
④ liveMedia:真正的核心,协议全在这。
它依赖上面三个,所以最后一个编。
这就是一个标准的依赖有向图。
拓扑排序给出的顺序,就是 Makefile 里写死的顺序。
所以你只要在根目录 make,它自然按对。
这也是官方文档从来只写一句「make」的原因。
反过来,如果你想只编某一个库,
就必须自己先把它的依赖编好。
直接进 liveMedia 目录 make 会报一堆「undefined reference」。
三、config 文件里那些变量
config.ubuntu 这种文件里全是 $(VAR) 的写法。
它看着像 shell,其实是 make 的变量。
会被一层一层替换,最后拼成一条完整的编译命令。
最该认识的是这一串开关:
-O2:开二级优化,发布版的标准配置。
-DSOCKLEN_T=socklen_t:告诉代码「长度类型叫这个」。
不同系统的套接字长度类型名字不一样,这行就是抹平差异。
-D_FILE_OFFSET_BITS=64:让 32 位系统上的文件偏移也按 64 位算。
没有它,录超过 2 GB 的文件就会在某个位置翻回去重写。
CPLUSPLUS_FLAGS 比 COMPILE_OPTS 多两个:
-Wall 开全部警告,-DBSD=1 表示「按 BSD 风格的套接字来」。
这两个都是 Live555 自己代码需要的,不是通用的。
四、最常见的四个坑
① 缺 openssl 头文件:报 openssl/ssl.h: No such file。
装 libssl-dev 就好。
Live555 的 RTSP 认证会用到它,不装编不过。
② 用高版本编译器编老代码:一堆 was not declared 之类的报错。
原因不是代码写错了,是新编译器默认标准变了。
先试在编译选项里显式指定 -std=c++11,
不行再考虑换老一点的编译器。
③ genMakefiles 参数给错:报找不到 config.xxx。
ls config.* 看一眼就解决。
④ 在子目录里 make:报一堆 undefined reference。
因为依赖的库还没编出来。
记住:永远在根目录 make,或者在子目录 make 之前先把依赖编好。
五、动手 Demo:Ubuntu 下编译——四组库,一个顺序
真跑一次拓扑排序,看该按什么次序编; 再真展开一遍 config 里的变量,看它最后变成什么。
DEPS = {
"UsageEnvironment": [],
"groupsock": ["UsageEnvironment"],
"BasicUsageEnvironment": ["groupsock", "UsageEnvironment"],
"liveMedia": ["groupsock", "UsageEnvironment",
"BasicUsageEnvironment"],
"testProgs": ["liveMedia", "groupsock",
"UsageEnvironment", "BasicUsageEnvironment"],
}
def toposort(g):
indeg = dict((k, len(g[k])) for k in g)
ready = sorted([k for k in g if indeg[k] == 0])
order = []
while ready:
n = ready.pop(0)
order.append(n)
for k in sorted(g):
if n in g[k]:
indeg[k] -= 1
if indeg[k] == 0:
ready.append(k)
ready.sort()
return order
for i, n in enumerate(toposort(DEPS), 1):
print("%d. %-24s 依赖 %d 个" % (i, n, len(DEPS[n])))
实跑输出:
--------------------------------------------------------------------
Ubuntu 下编译——四组库,一个顺序
--------------------------------------------------------------------
**① 这一步到底在编什么**
**Live555 的源码包解开是四个库加一堆示例程序。**
**库之间是有依赖的,编错了顺序就找不到符号。**
目录 编出什么 依赖谁
------------------------------------------------------------------------
UsageEnvironment 静态库 谁也不依赖,最先编
groupsock 静态库 UsageEnvironment
BasicUsageEnvironment 静态库 groupsock、UsageEnvironment
liveMedia 静态库 前三个全要
testProgs 可执行程序 四个库全要
**② 真跑一次拓扑排序,看该按什么次序编**
**依赖关系就是个有向图,拓扑排序给出的顺序就是编译顺序。**
**1. UsageEnvironment 无依赖**
**2. groupsock 先编 UsageEnvironment**
**3. BasicUsageEnvironment 先编 groupsock 等 2 个**
**4. liveMedia 先编 groupsock 等 3 个**
**5. mediaServer 先编 liveMedia 等 4 个**
**6. proxyServer 先编 liveMedia 等 2 个**
**7. testProgs 先编 liveMedia 等 4 个**
**这个顺序和 `genMakefiles` 生成的 Makefile 里的 `all` 目标一致。**
**所以官方从来只说一句「在根目录 make」,**
**因为次序已经写死在 Makefile 里了。**
**③ config.ubuntu 里的变量是怎么展开的**
**`$(COMPILE_OPTS)` 这种写法看着像 shell,其实是 make 的变量。**
**它会被一层一层替换掉,最后变成一条长长的编译命令。**
**COMPILE_OPTS 展开后共 74 字符:**
-I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
**C_FLAGS 展开后跟它完全一样:是。**
**CPLUSPLUS_FLAGS 只多两个开关:-Wall、-DBSD=1。**
**为什么会一样?因为 `$(CPPFLAGS)`、`$(CFLAGS)` 默认都是空的。**
**想加调试信息,不用改文件——编译时传 `make CFLAGS=-g3` 更干净。**
**注意 `-D_FILE_OFFSET_BITS=64` 这个宏:**
**它让 32 位系统上的文件偏移也按 64 位算。**
**录一个几 GB 的流,没这个宏就会写到一个 2 GB 的地方翻回去。**
**④ 并行编译能省多少**
**四个库加起来几百个源文件,单线程编要十几分钟。**
**这里按「总量固定、核数增加」的模型算个趋势(不是实测)。**
核数 理想耗时 实际估计 利用率
---------------------------------------------
1 900 秒 900 秒 100%
2 450 秒 514 秒 87%
4 225 秒 277 秒 81%
8 112 秒 144 秒 78%
16 56 秒 73 秒 77%
**核数一多,利用率就掉——因为链接那一步只能一个人干。**
**所以 `make -j8` 之后通常还要等,等的是最后的链接。**
**【📋 实操路径】真编译的完整命令**
tar xzf live555-latest.tar.gz && cd live
./genMakefiles linux
make -j8
ls -l liveMedia/libliveMedia.a
**`genMakefiles` 的参数要对:Linux 用 `linux`,**
**老教程里写的 `ubuntu` 在新版已经没有了,会报找不到文件。**
排序结果第一个一定是 UsageEnvironment,因为只有它不依赖别人。
最后一个不一定是 testProgs,取决于同一层里谁的名字靠前。
顺序不是唯一的,但「依赖在前」这条约束一定成立。
Makefile 里的 all 目标就是手写的这个顺序,所以官方只说 make。
30 秒实验
- 把
testProgs改成不依赖liveMedia,再看排序——它会跑到最前面,然后编译立刻失败。 - 故意造一个循环依赖(A 依赖 B、B 依赖 A),看会怎样——排序结果会少几个,因为拓扑排序处理不了环。
- 把
-O2换成-O0,猜编译时间与运行速度各怎么变——编得快、跑得慢,调试时才这么用。
常见误区
- 「编译就是把所有 .cpp 编一遍」——库之间有序,顺序错了链不上。
- 「
genMakefiles随便给个参数」——名字不对直接报找不到文件。 - 「子目录里也能直接 make」——依赖没编就先报一堆未定义符号。
自测三问(点开对答案)
1. 为什么 UsageEnvironment 要第一个编? —— **它不依赖任何别的库**,是整张依赖图的根。 2. `-D_FILE_OFFSET_BITS=64` 是干什么的? —— **让 32 位系统上的文件偏移按 64 位算**,否则大文件会写回开头。 3. 在 liveMedia 目录里直接 make 会怎样? —— **报一堆 undefined reference**,因为依赖的三个库还没编出来。- ☐ (本节完,下一节 8.2.2 Live555 在 Windows 10 下的源码编译)