🎬 第八章 Live555搭建直播平台
📄 一句话概括

Windows 这边没有 configure,改的是一份 win32config。 最常见的坑不在命令上,在 /MD 与 /MT 这个开关上。

📝 本节和前面几节的关系

8.2.1 讲 Linux 下的编译,这一节讲 Windows 下的同一件事。 两边产物一样,但工具链、配置方式、坑全不一样。

一、改配置而不是跑脚本

Linux 靠 genMakefiles 从 config.xxx 生成 Makefile; Windows 靠 genWindowsMakefiles 从 win32config 生成 .mak。 思路一模一样,只是文件名不同。

所以第一件事永远是打开 win32config 改几行: ① MSVCDIR:指向本机的 VC 目录。 这个值里有空格(Program Files (x86)), 所以整段必须带引号,否则 -I 后面只认到 C:\Program。 ② MSVC_VER:写编译器版本号。 1900 是 VS2015,191x 是 VS2017,192x 是 VS2019,193x 是 VS2022。 写错的后果不是编译失败,而是某些条件编译分支走错。 ③ NODEBUG:1 表示发布版,注释掉就是调试版。 这一行决定用哪个运行时库,是下一节那个坑的源头。

二、/MD 与 /MT:编得过却跑崩的根源

这是 Windows 编译 Live555 最经典的问题, 而且它不报错,只在运行时崩。

① /MT:把 C 运行时静态链进 EXE。 每个模块各带一份,于是进程里就有好几个堆。 在一个模块里 new、在另一个模块里 delete,必崩。 ② /MD:用系统的运行时 DLL。 全进程共用一份堆,跨模块释放是安全的。 代价是目标机器上要有对应的运行库。

所以规则只有一句: 你编 Live555 用的那个开关,你的工程也必须用同一个。 不一致的典型表现是「Debug 版好好的,Release 版一播就闪退」——因为两个配置的运行时库默认就不一样。

还有个连带问题: NODEBUG=1 时编出来的是发布版,用的是发布版运行时。 你的 VS 工程如果还停在 Debug 配置,两边立刻对不上。 这就是「明明照教程做了却崩」的常见原因。

三、生成什么、怎么编

genWindowsMakefiles 会一次生成一组 .mak 文件: 四个库各一个,示例程序一个,服务端与代理各一个。

编译要在「x64 Native Tools 命令提示符」里做,不能在普通 cmd: 因为 cl.exe、nmake.exe 和头文件路径都在那个专用窗口里才配好。 在普通 cmd 里连 nmake 都找不到。

顺序还是那个依赖顺序: 先 UsageEnvironment,再 groupsock,再 BasicUsageEnvironment, 然后 liveMedia,最后才是 testProgs。 逐个 nmake /f xxx.mak 执行。

编完在 testProgs 目录里会有 openRTSP.exe、 testOnDemandRTSPServer.exe 这些程序。 跑起来能连上,就说明这一套都是对的。

四、在自己的 VS 工程里用它

要让自己的项目用上 Live555,需要做四件事:

① 加头文件路径:指向 live 根目录与四个库目录。 Live555 的头文件是平铺的,路径别指错层。 ② 加库文件:四个 .lib 全加上。 链接顺序按依赖来:被依赖的放后面。 ③ 加系统库:ws2_32.lib 是必须的,它提供 socket 相关函数。 用到认证还要加 crypt32.lib。 ④ 统一运行时库:就是上一节说的那个开关。

最容易漏的是第 ③ 步。 漏了的报错长这样:unresolved external symbol __imp_socket。 看到 __imp_ 前缀,基本就是缺一个系统库。

五、动手 Demo:Windows 下编译——win32config 里的几个坑

真解析一遍 win32config,看它到底读了哪几个变量; 再核对运行时库那几种组合。

import re

CFG = """\
NODEBUG=1
MSVCDIR=\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\"
MSVC_VER=1900
INCLUDES=-I$(MSVCDIR)\\include
COMPILE_OPTS=$(INCLUDES) -I. -nologo -GR -EHsc -O2 -MD
"""

pat = re.compile(r"\$[({]([A-Za-z0-9_]+)[)}]")

def expand(text):
    env = {}
    for line in text.strip().splitlines():
        k, _, v = line.partition("=")
        env[k.strip()] = v.strip()
    out = {}
    for k in env:
        cur = env[k]
        for _ in range(12):
            nxt = pat.sub(lambda m: env.get(m.group(1), ""), cur)
            nxt = " ".join(nxt.split())
            if nxt == cur:
                break
            cur = nxt
        out[k] = cur
    return out

for k, v in expand(CFG).items():
    print("%-12s = %s" % (k, v))

实跑输出:

--------------------------------------------------------------------
Windows 下编译——win32config 里的几个坑
--------------------------------------------------------------------
**① Windows 这边不用 configure,改一份 win32config**

  **Linux 靠 `genMakefiles` 从 config.xxx 生成 Makefile;**
  **Windows 靠 `genWindowsMakefiles` 从 win32config 生成 .mak。**
  **所以第一件事永远是:把 win32config 里那几行路径改对。**

  **真解析一遍这份文件,看它到底读了哪几个变量:**

  **MSVCDIR          = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC"**
  **MSVC_VER         = 1900**
  **PLATFORM         = x64**
  **LIBRARY_LINK     = lib -nologo -out:**
  **COMPILE_OPTS     =**
    -I"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC"\include -I.
    -nologo -GR -EHsc -O2 -MD -DNO_STRSTREAM_H

  **`MSVCDIR` 是斜杠方向与空格两个雷:**
  **路径里有空格(Program Files (x86)),整个值必须加引号,**
  **否则 `-I` 后面只认到 `C:\Program`,然后报找不到头文件。**

**② /MD 与 /MT 选错,编得过但跑起来崩**

  **这是 Windows 编译 Live555 最常见的坑,而且报错根本看不出来。**

  运行时库     谁提供 CRT       和别的库混链会怎样
  --------------------------------------------------------------------------
  /MT          静态链进 EXE     两个模块各有一份堆,跨模块 free 必崩
  /MD          用系统 DLL       共用一份堆,安全;但目标机要有对应运行库

  **所以规则只有一句:**
  **你编 Live555 用的那个开关,你的工程也必须用同一个。**
  **不一致的表现是「Debug 版好好的,Release 版一播就闪退」。**

  **还有个连带问题:**
  **`NODEBUG=1` 时用的是 Release 运行时,**
  **你的 VS 工程如果还停在 Debug 配置,两边就对不上了。**
  **这也是「明明照教程做了却崩」的常见原因。**

**③ 会生成哪些 .mak**

  **`genWindowsMakefiles` 一次把这几个文件摆出来:**

  **liveMedia.mak                核心库**
  **groupsock.mak                组播与套接字**
  **UsageEnvironment.mak         环境抽象**
  **BasicUsageEnvironment.mak    基础实现**
  **testProgs.mak                示例程序**
  **mediaServer.mak              点播服务器**
  **proxyServer.mak              代理服务器**
  **hlsProxy.mak                 HLS 代理**

  **用 nmake 编的时候,顺序还是刚才那个依赖顺序:**
  **先 UsageEnvironment,最后才是 testProgs。**

  **【📋 实操路径】真编译的完整步骤**

    1. 改 win32config:MSVCDIR 指向本机 VC 目录,NODEBUG 按需设
    2. 开「x64 Native Tools 命令提示符」进 live 目录
    3. genWindowsMakefiles.cmd
    4. nmake /f liveMedia.mak
    5. nmake /f testProgs.mak
    6. 跑 testProgs\openRTSP.exe 验证

  **第 2 步最容易被跳过:**
  **在普通 cmd 里连 nmake 都找不到,**
  **报的是「nmake 不是内部或外部命令」,看着像没装 VS。**
  **就算 PATH 里有 nmake,它启动 cl.exe 时照样失败——**
  **因为编译器和头文件路径都在那个专用命令窗口里才配好。**

$(MSVCDIR) 展开后那一大段路径,就是带空格的坑。 -MD 会一直留在 COMPILE_OPTS 里,它就是运行时库开关。 MSVC_VER=1900 是 VS2015,教材用的就是它。 换个版本号只是改一个数字,但改了要成套改。

30 秒实验

  1. 把 -MD 改成 -MT,想想哪里会崩——跨模块 new/delete,堆不是同一个。
  2. 把 MSVCDIR 的引号去掉,看展开结果——-I 后面只到 C:\Program,路径断了。
  3. 把 NODEBUG 那行删掉,猜会编出哪一版——默认走调试分支,运行时库也跟着换。

常见误区

  1. 「Windows 也要跑 configure」——改 win32config,没有 configure。
  2. 「/MT 与 /MD 只是体积差别」——是堆的差别,会崩。
  3. 「在普通 cmd 里就能 nmake」——连 nmake 都找不到。
自测三问(点开对答案) 1. MSVC_VER 1900 对应哪个 VS 版本? —— **VS2015**。191x 是 2017,192x 是 2019,193x 是 2022。 2. /MT 与 /MD 的本质区别是什么? —— **运行时库静态链入还是共享 DLL**,也就是进程里几份堆。 3. 看到 `unresolved external symbol __imp_socket` 缺什么? —— **缺 `ws2_32.lib`**,`__imp_` 前缀基本就指系统库。

相关笔记