在 Linux ARM64 上构建毫秒级延迟 RTSP/RTMP 播放器:架构与实战
前言
随着边缘计算的兴起,瑞芯微(Rockchip)、NVIDIA Jetson、飞腾、鲲鹏等 ARM64 架构的嵌入式设备,已经广泛出现在安防监控、工业自动化、无人机图传、机器人视觉等场景里。
在这些场景中,有两个指标几乎是“生死线”级别的:
-
低延迟:操控类业务必须做到 100–200ms 量级,否则云台转动、无人机飞行会出现明显“拖影感”。
-
稳定性:7×24 小时持续运行不能频繁重启,更不能内存泄漏、句柄泄露导致系统崩溃。
本文基于大牛直播 SDK(SmartPlayerSDK)在 Linux ARM64 平台的 Demo 源码,结合实际的 C++ 封装与 X11 窗口管理代码,系统拆解如何在 Linux ARM64 环境下,实现一个支持多路并行、毫秒级延迟的 RTSP/RTMP 播放器。

一、整体架构:从 C 风格 SDK 到工程化播放器
大牛直播 SDK 在 Linux 下的核心入口是一个纯 C 风格的结构体 SmartPlayerSDKAPI,里面是一组函数指针:Init/UnInit/Open/Close/SetURL/StartPlay/...
在此之上,这套 Demo 做了三层清晰的分层:
-
SDK API 层(C 接口)
-
负责 RTSP/RTMP 拉流、解码、渲染、事件回调等。
-
-
句柄封装层:
NT_SDK_HandleWrapper-
负责
NT_HANDLE的创建、关闭、参数配置,以及 SDK 事件分发。
-
-
播放器封装层:
NT_PlayerSDKWrapper-
负责与 X11 窗口绑定、播放控制、截图、窗口 resize、静音等业务逻辑。
-
-
应用层:
multi_player_demo.cpp-
初始化 SDK & X11,创建主窗口 + 子窗口,实现多画面布局与事件循环。
-
这套架构的核心思路是:把 C 风格的“裸 SDK”包成 C++ RAII 对象,让它适合 7×24 小时运行的工程环境。
二、句柄生命周期管理:NT_SDK_HandleWrapper 与 RAII
2.1 RAII 思路
SDK 底层通过 Open 返回一个 NT_HANDLE,用户需要显式 Close。如果在复杂业务里手动管理,很容易出现:
-
异常路径忘记 Close
-
多线程场景重复 Close
-
资源在对象析构时仍被占用
于是 C++ 层引入 NT_SDK_HandleWrapper,采用 RAII:
NT_SDK_HandleWrapper::~NT_SDK_HandleWrapper() {
Close(); // 析构自动释放 SDK 句柄
}
生命周期与 C++ 对象深度绑定,彻底避免了资源泄漏。
2.2 Open:统一配置播放参数
Open 方法除了创建句柄,还会顺便把一系列“低延迟 + 稳定性”相关参数一次性配置好:
sdk_api_->SetBuffer(handle, buffer);
sdk_api_->SetRtspAutoSwitchTcpUdp(handle, 1);
sdk_api_->SetRtspTimeout(handle, 15);
sdk_api_->SetReportDownloadSpeed(handle, 1, 5);
sdk_api_->SetURL(handle, url.c_str());
其中:
-
SetBuffer(0)→ 极小缓存,降低延迟 -
TCP/UDP 自动切换→ 弱网情况下提升成功率 -
超时 15s→ 避免卡死 -
下载速度上报→ 帮助上层动态监控链路状态
这是典型的“工程化初始化”,让上层应用不必关心这些参数。
2.3 事件分发机制
SDK 原生的事件回调是 C 风格的函数,你通过 NT_SDK_HandleWrapper 将其转换为 C++ 的多观察者模式:
handler->OnEventHandler(handle, event_id, param1, ...);
这样多个业务模块(UI、日志、统计、AI)可以同时接收:
-
连接成功 / 失败
-
缓冲开始 / 停止
-
下载速率
-
RTSP 401 鉴权错误
这为构建一个完整的监控客户端奠定基础。
三、播放器封装:NT_PlayerSDKWrapper 的职责边界

NT_PlayerSDKWrapper 相当于真正“对业务友好”的那层,封装了:
-
窗口绑定
-
参数设置
-
Start / Stop
-
截图
-
缩放模式
-
事件处理
3.1 与 X11 子窗口绑定
播放器被创建后,会被注入:
-
Display* display_ -
int screen_ -
Window window_
Start 时执行:
player_api_->SetRenderXWindow(handle_->Handle(), window_);
player_api_->SetRenderScaleMode(handle_->Handle(), render_scale_mode);
player_api_->SetRenderTextureScaleFilterMode(handle_->Handle(), 3);
SDK 内部使用 OpenGL 或 XVideo 完成渲染,你无需自己处理像素格式转换。
3.2 音频处理与静音
player_api_->SetMute(handle_->Handle(), is_mute ? 1 : 0);
player_api_->SetIsOutputAudioDevice(handle_->Handle(), 1);
player_api_->SetAudioOutputLayer(handle_->Handle(), 0);
Demo 中做了一个非常实用的处理:
多路播放时,只有第一路开声,其余默认静音。
避免了“所有窗口一起叫”的噪音问题。
3.3 解码策略:只解码关键帧模式
player_api_->SetOnlyDecodeVideoKeyFrame(handle, is_only_dec_key_frame ? 1 : 0);
它适合:
-
极低性能的 ARM 开发板
-
多画面预览(画质要求不高)
如果要追求最低延迟 + 流畅度,不建议开启。
四、如何做到“极致低延迟”:核心策略拆解
低延迟是一套系统工程,不是调一个参数那么简单。这里总结实际有效的策略:
4.1 极小缓冲区:SetBuffer(0)
sdk_api->SetBuffer(handle, 0);
直接减少排队时间,是降低延迟的第一步。
注意:弱网抖动时可能会更容易卡顿,需要配合其他策略。
4.2 RTSP 智能 TCP/UDP 自动切换
sdk_api->SetRtspAutoSwitchTcpUdp(handle, 1);
策略:
-
先尝试 UDP(低延迟)
-
网络不佳时自动切换 TCP(稳定)
这比固定 UDP 或固定 TCP 更适合工程落地。
4.3 快速首帧:Fast Startup
适合:
-
监控轮巡
-
频繁切换通道
-
预览墙系统
让画面能“点开即看”,不需要等 I 帧阻塞。
4.4 低延迟播放模式:SetLowLatencyMode
开启后会减少内部队列长度,调整同步策略,使渲染尽可能贴近解码结果。
常用于:
-
云台控制
-
无人机图传
-
远程巡检
五、X11 渲染与多窗口布局
multi_player_demo.cpp 展示了如何构建一个多路实时播放的界面。
5.1 主窗口 & 子窗口的创建绑定
主流程:
-
调用
XOpenDisplay获取 X server -
创建主窗口
XCreateSimpleWindow -
按播放路数动态计算子窗口布局
-
使用
XCreateWindow创建子窗口 -
将子窗口的
WindowID bind 到播放器
这比 Qt/GTK 更轻量,更适合 ARM64 工控设备。
5.2 响应窗口大小改变(Resize)
收到 ConfigureNotify 事件后:
-
重新计算布局
-
调
XMoveResizeWindow调整子窗口 -
通知 SDK:
player_api->OnWindowSize(handle, width, height);
SDK 会自动重新计算显示区域,你不需要自己做矩阵变换或视口调整。
六、高效事件循环:poll + X11 的组合模型
为了避免主循环一直占满 CPU,Demo 引入了:
poll(ConnectionNumber(display), ...)
流程:
-
若 X11 有事件 → 立即处理
-
若无事件 → 在 poll 的超时周期内休眠
优点:
-
不阻塞 UI
-
低 CPU 占用
-
延迟响应毫秒级
特别适合:
-
ARM64 工控板
-
多路渲染
-
资源紧张场景
七、ARM64 场景化调优建议
7.1 无人机 / 云台
-
建议使用 RTSP over UDP
-
参数组合:
-
SetBuffer(0) -
SetLowLatencyMode(1) -
SetFastStartup(1)
-
-
如需更高稳定性可 fallback 到 TCP
7.2 工控监控 / NVR 预览墙
-
多路布局用
SubWindowsLayout -
每路可以设置 0–200ms buffer,提升稳定性
-
开启下载速率上报用于监控链路质量
7.3 AI 视觉前处理
-
用
SetVideoFrameCallBack取 I420 帧喂给 AI 模型 -
播放器仍然可以同步渲染,做到“可算可视”
-
若模型要求统一分辨率,可用 V2 接口自动缩放
八、完整启动流程(串起来)
简化后的主流程:
// 1. SDK 初始化
player_api.Init();
// 2. X11 初始化
Display* display = XOpenDisplay(nullptr);
// 3. 创建 X11 子窗口
Window sub_win = XCreateWindow(...);
// 4. 播放器实例
auto player = std::make_shared<NT_PlayerSDKWrapper>(&player_api);
player->SetDisplay(display);
player->SetScreen(screen);
player->SetWindow(sub_win);
player->SetURL("rtsp://xxx");
// 5. 启动播放(低延迟模式)
player->Start(0, false, 1, false);
// 6. Event Loop(poll + X11)
while (true) {
if (pending_x_event()) {
XNextEvent(...);
}
}
九、总结:从 Demo 到工程落地
这套 Linux ARM64 多路播放器 Demo 展示了一条完整、可落地的行业级链路:
-
SDK 层:跨 RTSP/RTMP 协议、解码、同步、OpenGL 渲染
-
句柄管理层:RAII + 事件分发 + 低延迟参数注入
-
播放器封装层:播放控制、窗口绑定、截图、Resize
-
X11 应用层:多路布局、事件循环、资源管理
它不是一个“只为了展示功能”的 Demo,而是一套可以直接在实际项目中复用的框架。
如果你准备开发:
-
工控机 / NVR
-
机器人 AI 视频节点
-
无人机图传地面站
-
多路实时监控客户端
这套架构可以直接作为项目骨架,稍加业务化即可生产可用版本。
📎 CSDN官方博客:音视频牛哥-CSDN博客

浙公网安备 33010602011771号