最近在一个 Windows 桌面端项目中需要实现 RTSP 视频流拉取功能。项目本身已经有一个 VideoModule 动态库模块负责从共享内存中读取视频帧并显示,我们希望在此基础上增加从网络摄像头拉取 RTSP 流的能力,而 Live555 是一个成熟且常用的选择。然而将 Live555 编译为 Windows 静态库并融入现有 CMake/VS 工程并非一帆风顺,其间遇到了不少配置与兼容性问题。本文从零开始,逐步记录整个集成过程,包括:
-
如何将 Live555 源码组织到解决方案中
-
如何创建一个静态库项目并配置编译环境
-
如何解决编译错误(
std::atomic_flag、ifaddrs.h、OpenSSL 依赖等) -
如何配置生成后事件实现自动化
-
如何在调用模块中链接该静态库
1. 项目背景与设计思路
当前我们的解决方案中已有一个 VideoModule 动态库模块,它负责获取视频帧并将视频帧发给主程序显示。现在需要增加 RTSP 拉流能力,但先不做解码,只完成流媒体协议交互并拿到编码后的视频帧。所以我们决定:
-
将 Live555 四个核心库(
UsageEnvironment、groupsock、liveMedia、BasicUsageEnvironment)编译成 单个静态库MyLive555lib.lib。 -
在
VideoModule项目中链接该静态库,直接在动态库内调用 Live555 的 RTSP 客户端 API。 -
所有输出
.lib文件集中管理,通过生成后事件自动复制到公共库目录。
整体依赖关系如下:
VideoModule.dll ─── 依赖 ───> MyLive555lib.lib ws2_32.lib
未来可以在 VideoModule 内部创建 RTSP 客户端,在帧回调中将数据写入共享内存,供主程序或其他模块消费。
2. 源码准备与目录结构
从 Live555 官网(Index of /)或 GitHub 下载源码,解压后找到这四个目录(其他如 WindowsAudioInputDevice、testProgs 等不需要):
live/
├── UsageEnvironment/
│ ├── include/ (头文件 .hh)
│ └── *.cpp
├── groupsock/
│ ├── include/
│ ├── *.cpp
│ └── *.c (可能存在)
├── liveMedia/
│ ├── include/
│ └── *.cpp
└── BasicUsageEnvironment/
├── include/
└── *.cpp
我选择将这些源文件直接复制到解决方案下新创建的的 MyLive555lib 项目目录中,并按原有模块分文件夹放置,便于后续在 VS 中按筛选器整理。即先进行3,将静态库项目创建后,再将文件复制进来。
实际最终磁盘结构如下:
SolutionDir/
├── VideoModule/ (已有的动态库项目)
├── MyLive555lib/ (即将创建的静态库项目)
│ ├── UsageEnvironment/
│ │ ├── include/ (头文件)
│ │ └── *.cpp
│ ├── groupsock/
│ │ ├── include/
│ │ ├── *.cpp, *.c
│ ├── liveMedia/
│ │ ├── include/
│ │ └── *.cpp
│ └── BasicUsageEnvironment/
│ ├── include/
│ └── *.cpp
├── lib/ (公共库输出目录)
└── YourSolution.sln
说明:也可以在添加文件时使用"添加为链接"方式保持源码位置不变,我这里为了归档方便直接复制了进来。
3. 创建 MyLive555lib 静态库项目
-
在 VS2022 中打开解决方案,右键解决方案 → 添加 → 新建项目。
-
选择 静态库 模板,项目名称设为
MyLive555lib,位置选在SolutionDir\MyLive555lib\。 -
创建完成后,删除项目自动生成的
.cpp、pch.h、pch.cpp、framework.h等文件(因为我们不需要预编译头)。 -
打开项目属性(注意选择所有配置和所有平台,或者至少对应你需要的配置,如 Release x64):
-
C/C++ → 预编译头 → 预编译头 改为 不使用预编译头。
-
高级 → 字符集 → 选择 使用多字节字符集(Live555 假定多字节字符集)。
-
C/C++ → 代码生成 → 运行库 → 选择 多线程 DLL (/MD) (与调用它的
VideoModule动态库保持一致)。
-
4. 组织筛选器(强迫症友好)
在解决方案资源管理器中,右键 MyLive555lib 项目,添加下列筛选器(虚拟文件夹),让结构清晰:
MyLive555lib
├── liveMedia
│ ├── 头文件
│ └── 源文件
├── groupsock
│ ├── 头文件
│ └── 源文件
├── UsageEnvironment
│ ├── 头文件
│ └── 源文件
└── BasicUsageEnvironment
├── 头文件
└── 源文件
然后通过右键每个 源文件/头文件 筛选器 → 添加 → 现有项 ,将对应模块的 .cpp 和 .hh 文件分别添加进去。注意 groupsock 下如果有 .c 文件也同样加入源文件,VS 会以 C 方式编译。
5. 项目属性关键配置
打开 MyLive555lib 项目属性,我们需要设置以下选项。
5.1 附加包含目录
让编译器能直接通过文件名找到头文件。因为源文件就位于各模块子目录中,头文件在 include 子目录下,所以包含目录应为:
$(ProjectDir)UsageEnvironment\include
$(ProjectDir)groupsock\include
$(ProjectDir)liveMedia\include
$(ProjectDir)BasicUsageEnvironment\include
在项目属性的 C/C++ → 常规 → 附加包含目录 中填入,用分号分隔。
5.2 预处理器定义
这是最核心的一步,用于解决 Windows 平台兼容性和减少外部依赖。在 C/C++ → 预处理器 → 预处理器定义 中填入以下宏(分号分隔,注意不要漏掉分号):
NOMINMAX
_CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
WIN32_LEAN_AND_MEAN
NO_OPENSSL
NO_STD_LIB
NO_GETIFADDRS
各宏含义:
| 宏 | 用途 |
|---|---|
NOMINMAX |
避免 Windows.h 定义的 min/max 宏冲突 |
_CRT_SECURE_NO_WARNINGS |
禁止不安全 CRT 函数警告 |
_WINSOCK_DEPRECATED_NO_WARNINGS |
禁止 Winsock 废弃警告 |
WIN32_LEAN_AND_MEAN |
精简 Windows.h 包含内容,减少编译时间 |
NO_OPENSSL |
不编译 TLS/SSL 相关代码,避免需要 OpenSSL 头文件 |
NO_STD_LIB |
使用 Live555 自带的备选原子操作实现 ,避免依赖 VS2022 已废弃的 std::atomic_flag::test |
NO_GETIFADDRS |
禁用 getifaddrs() 调用 ,Windows 下没有 ifaddrs.h,此项让 Live555 使用备用方法获取本机 IP |
5.3 其他设置
-
C/C++ → 高级 → 禁用特定警告 → 填入
4996(可选,忽略不安全函数警告)。 -
链接器 → 常规 → 输出文件 保持默认,最终会生成
MyLive555lib.lib。
6. 编译与错误修复
第一次直接生成,可能出现若干错误。以下是我实际遇到的及其修复方法。
6.1 ifaddrs.h 找不到
错误信息:
GroupsockHelper.cpp(46,10): error C1083: 无法打开包括文件: “ifaddrs.h”: No such file or directory
原因 :Live555 在未定义 NO_GETIFADDRS 时默认包含该头文件。
解决 :已在预处理器定义中添加 NO_GETIFADDRS,直接跳过该包含。
6.2 std::atomic_flag::test 不是成员
错误信息:
BasicTaskScheduler.cpp(191,40): error C2039: "test": 不是 "std::atomic_flag" 的成员
原因 :VS2022 的 <atomic> 中移除了 std::atomic_flag::test 方法。Live555 在 BasicUsageEnvironment0.hh 中有条件编译分支:若定义了 NO_STD_LIB,则使用 Boolean volatile fTriggersAwaitingHandling[...] 代替 std::atomic_flag。
解决 :添加预处理器定义 NO_STD_LIB 即可,无需修改任何源码。
6.3 OpenSSL 头文件找不到
大量错误:
TLSState.hh(34,10): error C1083: 无法打开包括文件: “openssl/ssl.h”: No such file or directory
原因 :Live555 默认启用 TLS 支持,需要 OpenSSL。
解决 :宏 NO_OPENSSL 可禁用所有 SSL 相关代码,已在预处理器定义中添加。
6.4 strtol 或 abort 未定义
错误信息:
RTSPCommon.cpp(40,22): error C3861: “strtol”: 找不到标识符
UsageEnvironment.cpp(49,3): error C3861: “abort”: 找不到标识符
原因 :某些源文件没有显式包含 <stdlib.h>,而 VS2022 的某些头文件依赖链发生了变化。
解决 :在报错的 RTSPCommon.cpp 和 UsageEnvironment.cpp 文件最顶部添加:
#include <stdlib.h>
这是本次集成中唯二直接修改源码的地方。
7. 设置生成后事件(复制 lib)
为了方便其他项目链接,我们让 MyLive555lib 编译成功后自动将输出的 .lib 文件复制到公共库目录。
在项目属性 → 生成事件 → 生成后事件 的命令行中输入:
if not exist "$(SolutionDir)lib" mkdir "$(SolutionDir)lib"
xcopy /y "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)lib\"
这里假设公共目录是 $(SolutionDir)lib,可根据需要调整。也可以按 Debug/Release 区分,将路径改为 $(SolutionDir)lib\$(Configuration)\。
8. 重新生成 MyLive555lib
做完以上所有设置后,右键 MyLive555lib → 重新生成 。此时应该能够顺利编译,只有少量类型转换警告,可忽略。生成成功后,检查 lib 目录,应能看到 MyLive555lib.lib。
9. 在 VideoModule项目中集成
现在我们拥有了静态库,需要让 VideoModule项目能够使用 Live555 的功能。
9.1 配置 VideoModule项目属性
打开 VideoModule项目属性,进行以下设置(注意选择与 MyLive555lib 相同的配置和平台):
-
附加包含目录 :与静态库项目相同,添加四个 include 路径(从
VideoModule项目目录出发的相对路径可能稍有不同,示例为..\MyLive555lib\...\include,按实际调整)。 -
预处理器定义 :添加与
MyLive555lib完全一致的宏:NO_OPENSSL;NO_STD_LIB;NO_GETIFADDRS同时保留原有的
NOMINMAX等。 -
字符集 :同样设为 多字节字符集。
-
附加库目录 :在 链接器 → 常规 → 附加库目录 中添加
$(SolutionDir)lib\(或带配置子目录)。 -
附加依赖项 :在 链接器 → 输入 → 附加依赖项 中添加:
MyLive555lib.lib ws2_32.lib -
运行库 :保持 /MD,与静态库一致。
9.2 注意预处理器宏的一致性
NO_STD_LIB 会改变 BasicTaskScheduler0 类中 fTriggersAwaitingHandling 数组的类型和大小。如果静态库和调用它的模块之间该宏定义不一致,会导致结构体大小不同,运行时可能发生难以排查的崩溃。所以务必保持两个项目的宏完全相同。
10. 在 VideoModule中编写 RTSP 拉流代码框架
至此编译环境已就绪,可以在 VideoModule的某个源文件中包含 Live555 头文件:
#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>
拉流的基本步骤遵循 Live555 的经典用法:创建 TaskScheduler 和 UsageEnvironment,然后创建 RTSPClient,发送 DESCRIBE 命令;在回调中拿到 MediaSession,对每个 MediaSubsession 执行 SETUP 和 PLAY;同时创建我们自定义的 MediaSink(通常称为 DummySink),在其 afterGettingFrame 回调中获取每一帧的原始编码数据 (fTo,长度为 frameSize),然后将其写入共享内存中,供主程序读取显示。
由于本文主要聚焦于编译集成,具体 RTSP 交互逻辑可参考 Live555 自带的 testRTSPClient 示例,或继续在本模块中实现。
11. 整体编译运行
-
确保
MyLive555lib已编译成功且.lib已复制到公共目录。 -
编译
VideoModule,应无链接错误。 -
主程序加载
VideoModule.dll,在适当位置调用其提供的 RTSP 启动函数,传入摄像头 RTSP 地址,内部开始拉流并将帧不断写入共享内存。 -
主程序从共享内存取出帧并显示。
12. 常见问题与注意事项
-
链接错误 LNK2019(无法解析的外部符号) :检查是否忘记了
ws2_32.lib依赖,或库路径是否正确。 -
运行时崩溃 :大概率是
NO_STD_LIB等宏在静态库和使用者之间不一致,重新确认两边预处理定义完全一致。 -
ssize_t未定义 :Live555 的NetCommon.h会处理,但在极端情况下可在包含 Live555 头文件前自行typedef SSIZE_T ssize_t;。 -
WinSock 重定义 :包含顺序应先
winsock2.h再windows.h,Live555 自身已处理。
结语
通过上述步骤,我们成功将 Live555 编译为静态库并集成到现有的 Windows 动态库模块中,为项目增加了 RTSP 拉流能力。整个过程虽然遇到了不少编译问题,但借助 Live555 自身预留的编译选项(NO_OPENSSL、NO_STD_LIB、NO_GETIFADDRS 等),我们几乎无需修改源码就完成了适配。希望这篇记录能帮助到在 Windows 下集成 Live555 的开发者。