JRTPLIB 不是单个库,它依赖 jthread,所以必须编两次、且顺序不能反。 四步走完,把两个 .lib 和头文件拷进自己的工程就算通。
3.1 到 3.3 讲的都是「协议长什么样」,从这一节起开始动真代码。 这一节在 Windows 上编库,3.4.2 在 Ubuntu 上编同一个库,3.4.3 用编好的库收发第一个包。
一、为什么是两个包
JRTPLIB 的源码包里,线程、互斥量、定时器这些全部交给 jthread 做。 所以下 jrtplib 之前必须先下 jthread。
两者的分工很清楚:
① jthread:跨平台的线程封装(Windows 上包 CreateThread,Linux 上包 pthread),外加互斥量与条件变量。
② jrtplib:RTP/RTCP 的会话管理,内部开一个后台线程收包,这个线程就是 jthread 建的。
所以「先编 jthread,再编 jrtplib」不是建议,是硬依赖。
顺序反了会在链接期报一大片未解析外部符号,而报错信息里写的是 jthread::JMutex::Lock 之类的名字,第一次见很容易以为是 C++ 标准库的问题。
二、四步:下载、生成、编译、拷贝
① 下载:jrtplib 取 3.11.2,jthread 取 1.3.3,两个版本要配对,混版本偶发编译不过。
② 生成工程:用 CMake,别手工建工程。
命令是 cmake -G "Visual Studio 14 2015 Win64" ..——14 就是 v140 工具集,对应 VS 2015。
③ 编译:先 jthread 的 INSTALL 目标,再 jrtplib 的 INSTALL 目标。
④ 拷贝:把 .lib 放进链接目录、头文件放进 include 目录。
两个容易踩的细节:
① 位数要一致:Win64 生成 64 位库,自己的工程也必须是 x64。32 位工程链 64 位库,报的是 LNK1112: 模块计算机类型冲突。
② 运行时库要一致:Debug 用 MDd、Release 用 MD。
混了会报 LNK2038: 检测到 RuntimeLibrary 的不匹配项。
三、库到底帮你做了什么
很多人编完库、跑通示例,却说不清它省了自己多少事。 实测把它的行为拆开,其实只有三件:
① 记住配置:SSRC、PT、时钟频率、MTU 这些每包都要用的值,库替你存着,不用每次传。
② 自动递增序号:每发一包序号加一,绕回 65535 自动归零。
这一行自己写也对,但加上多线程就不会了。
③ 收包时按序号排队:PollData 内部会处理乱序与重复。
这是最值钱的一条——自己写要维护一个窗口缓冲区,边界条件极多。
换句话说:RTP 头的拼装是最简单的部分,难的是「多线程 + 乱序窗口」。 JRTPLIB 的价值全在后半段。
四、沙箱里怎么验证语义
这一节的核心动作是「在 Windows 上编库」,而沙箱里没有 VS 2015。 所以做法是: 真实步骤按【📋 实操路径】写清,代码语义用 Python 等价实现验证。
等价实现的五个动作,和 JRTPLIB 的五个接口一一对应: 建 socket → bind → 设 PT → 记对端 → 发/收。 跑通之后你会发现:五步里有四步都是「配置」,只有最后一步是「做事」。 这就是封装型库的典型形态。
要在真机上复现,建议按这个顺序试:
先只编 jthread 并跑它自带示例(验证编译器没问题)→ 再编 jrtplib 跑 example1(验证链接对)→ 最后才改自己的代码。
一次只前进一步,出错时才知道是哪一层的问题。
五、动手 Demo:VS 2015 的四步与沙箱里的等价验证
上表是【📋 实操路径】——需要一台装了 VS 2015 的 Windows 10。 下面这段是沙箱能跑的等价实现:用五个动作复刻 JRTPLIB 的五步接口。
import socket, struct
SSRC, PT, CLK = 0x1A2B3C4D, 96, 90000
# ① RTPSession sess; ② Create(params):建 socket 并 bind
snd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
snd.bind(("127.0.0.1", 0))
rcv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
rcv.bind(("127.0.0.1", 0))
rcv.settimeout(2.0)
# ③ SetDefaultPayloadType / SetDefaultMark / ④ AddDestination
dest = rcv.getsockname()
# ⑤ SendPacket:序号自动递增,时间戳按帧走
for i in range(5):
hdr = struct.pack("!BBHII", 0x80, PT, 100 + i, CLK // 25 * i, SSRC)
snd.sendto(hdr + ("帧%d" % i).encode("utf-8"), dest)
# ⑤ PollData / GetNextPacket
got = []
for _ in range(5):
try:
got.append(rcv.recvfrom(2048)[0])
except socket.timeout:
break
print("收到 %d 个包" % len(got))
print("序号 %s" % [struct.unpack("!BBHII", d[:12])[2] for d in got])
实跑输出:
--------------------------------------------------------------------
编译 JRTPLIB——VS 2015 的四步与沙箱里的替代验证
--------------------------------------------------------------------
📋 **这一节的主体是「在 Windows 上把 JRTPLIB 编出来」,而沙箱里没有 VS 2015。**
**所以真实步骤按实操路径写,代码部分用一个纯 Python 的等价实现验证语义。**
**① 实操路径(在有 VS 2015 的 Windows 10 上)**
步 做什么 关键点
------------------------------------------------------------------------------------------------
1 下载 jrtplib-3.11.2 与 jthread-1.3.3 两个包都要,jrtplib 依赖 jthread
2 用 CMake 生成 VS 2015 工程 `cmake -G "Visual Studio 14 2015 Win64" ..`
3 先编 jthread,再编 jrtplib 顺序错了会报一堆未解析外部符号
4 把 jrtplib.lib 与头文件拷进自己的工程 Debug 配 Debug、Release 配 Release,别混
**第 3 步是最容易卡住的:**
**jrtplib 内部大量用到 jthread 的线程与互斥量封装,先编 jthread 才能拿到 .lib。**
**而 VS 2015 的默认工具集是 v140。**
**CMake 生成时要保证 `-G` 参数对得上,否则打开工程会提示「需要升级」。**
**② 沙箱里的替代:用 Python 复刻 JRTPLIB 的五步接口**
**JRTPLIB 的用法永远是这五步,语言换成什么都一样:**
**建会话 → 设载荷类型 → 加目的地址 → 发 → 收。**
JRTPLIB 的调用 这里等价做的事 结果
--------------------------------------------------------------------------
RTPSession sess; 建两个 UDP socket 成功
Create(sessparams) bind 到 127.0.0.1:0 端口 57043
SetDefaultPayloadType PT = 96 写进头
SetDefaultMark M = 0 写进头
AddDestination 记下对端地址 127.0.0.1:57043
SendPacket 打包 12 字节头 + 载荷 发 5 个
PollData / GetNextPacket recvfrom 并解析 收 5 个
**实测收到 5 个包,序号 100、101、102、103、104。**
**所以库帮你做的其实就三件事:**
**① 记住 SSRC/PT/时钟这几项配置;② 每次发自动递增序号;③ 收的时候把乱序的包按序号排好。**
**这三件事自己写也就几十行,用库是为了少踩边界条件的坑。**
实测收到 5 个包,序号 100 到 104——这正是 SendPacket 内部「序号自动加一」的结果。
PT 与 SSRC 一次设好、每包复用,对应库里的 SetDefault* 系列。
这段代码里没有一行是 RTP 协议特有的「难题」,真正的难题(乱序窗口、多线程、超时重传)都在库里。
30 秒实验
- 把
PT从 96 改成 97,看接收端解出来的 PT 变不变——96 以上都是动态类型,具体含义要靠 SDP 说明。 - 把 5 个包的
SSRC改成两个不同的值,模拟「两路流复用一对端口」,看接收端能不能分开。 - 在真机上先只编 jthread 跑示例,确认能过再编 jrtplib——分步验证比一次编完省时间。
常见误区
- 「jrtplib 可以单独编」——它硬依赖 jthread,且必须后编。
- 「x64 工程链 32 位库也行」——会报
LNK1112模块计算机类型冲突。 - 「Debug 库给 Release 用没事」——会报
LNK2038运行时库不匹配,症状是偶发崩溃。
自测三问(点开对答案)
1. 为什么必须先编 jthread? —— **jrtplib 的收包线程、互斥量都来自 jthread。**先编才能拿到 `.lib`。 2. CMake 里 `Visual Studio 14 2015` 的 14 是什么意思? —— **v140 工具集版本号。**对应 VS 2015,写错会提示「需要升级工程」。 3. JRTPLIB 帮你做的三件事里,哪一件最值钱? —— **收包时按序号排队。**多线程加乱序窗口的边界条件最难写。- ☐ (本节完,下一节 3.4.2 Ubuntu18编译JRTPLIB)