RambosPlayer 加了个拉流录制,只改了 150 行代码

33

项目github:https://github.com/johnjiamzhong-project/RambosPlayer

起因

最近在用 RambosPlayer 拉 AlertGateway 推出的RTMP流,想录下来。翻了一遍功能列表——有推流录制,但那是配合文件播放用的,拉流模式下没这个功能。

那就加一个吧。

先看看手头有什么

打开代码一看,其实基础设施都有了:

DemuxThread 的读包循环里,本来就有分叉逻辑——每读一个包,除了推给播放队列,还会同步拷贝给所有注册的录制器

LocalRecorder 已经实现了 -c copy 直通写 FLV,PTS 归零、seek 抑制、关键帧对齐全都有

PlayerController 暴露了 addLocalRecorder() 接口,一行就能注册

所以本质上,拉流录制就是在拉流播放的同时,往 DemuxThread 的分叉列表里塞一个 LocalRecorder

暂停/恢复怎么搞

网络流是实时的,"暂停"不可能真的暂停网络。那就只能停止写入,丢弃数据

关键问题是恢复的时候 PTS 怎么续接。暂停期间网络流的 PTS 跑了 30 秒,恢复后第一帧的源 PTS 已经跳了很远。如果直接写,输出文件会出现 30 秒的空洞。

解法很朴素:

pause()  → 设 paused_ 标志,write 直接返回

resume() → videoAccumPts_ = videoLastOut_   // 记住写到哪了
           videoSegBase_ = AV_NOPTS_VALUE   // 下一帧重新采集基准

恢复后第一帧算出来:

outputPTS = newSrcPTS - newSegBase + videoAccumPts_
          ≈ 0 + videoLastOut_
          = 暂停前最后一帧

时间轴无缝接上。

保存策略的纠结

最初设计是点"录制"就弹保存对话框。后来发现不对——看直播的时候想录就录,哪有功夫选路径?

改成临时文件方案

点"录制" → 直接写 %TEMP%/rambos_record_XXXXXX.flv,零打断

点"停止" → 先写完文件,再弹框让你选保存位置和格式

选 FLV → 移动临时文件,瞬间完成

选 MP4 → 用 avformat 做一次容器转换(-c copy,不重编码),100MB 的文件大概 1 秒

取消 → 删临时文件,当无事发生

34

MP4 容器转换

为什么不直接录 MP4?因为 MP4 的 moov atom 在文件尾部,录到一半断电,文件就废了。FLV 没这个问题,随时断都能播放。

所以策略是录制用 FLV 保平安,保存时可选转 MP4 求兼容

转换代码很短,核心就是一个循环:

打开源 FLV → 创建 MP4 输出 → 逐包读写(只换容器不换数据)→ 写 trailer

速度极快,本质就是复制数据。

UI 怎么加的

在拉流控制栏末尾塞了三个按钮,全部初始隐藏:

录制 → 只在拉流连接成功后显示

暂停 / 恢复 → 录制中显示,点击切换文案

停止录制 → 录制中显示,点击弹保存框

状态机很简单:

未连接 → 全部隐藏
已连接 → [录制]
录制中 → [暂停] [停止录制]
暂停中 → [恢复] [停止录制]
停止   → [录制]
断开   → 全部隐藏

断开拉流时如果正在录制,自动触发停止并弹框保存,不会丢数据。

状态栏的蓝色条子

录制期间底部会显示一个蓝色状态栏(style.qss 里 QStatusBar 的背景色是 #007acc),提示"录制中..."。

录完之后光清文字没用,底色还在。得 statusBar()->hide() 整个隐藏掉。开始录制时再 statusBar()->show()

就这一个小细节,多改了两行。

代码审查发现的问题

写完跑了一遍,发现 resume() 里只重置了 segBase,没更新 accumPts_。这会导致恢复后 PTS 回跳到暂停前的位置,输出文件时间轴倒退。

修复就一行:resume() 开头加 videoAccumPts_ = videoLastOut_

最终改动清单

文件 改了什么
localrecorder.h +3 方法声明, +1 成员变量
localrecorder.cpp +20 行(pause/resume/init 重置)
mainwindow.ui +3 按钮控件
mainwindow.h +4 成员变量, +4 方法声明
mainwindow.cpp +120 行(录制控制 + remux)

150 行新增代码,零修改已有逻辑,零新增线程。

感受

这个功能之所以好加,是因为项目早期的架构做对了一件事——DemuxThread 的分叉机制

所有录制、推流、转发,都是在读包循环里挂一个写入器。新功能不需要改 DemuxThread 一行代码,只需要注册一个新的消费者。

当初设计这个架构的时候没想到要加拉流录制,但它天然支持。

好的架构不是预知未来,是让未来的事情不需要推翻重来。


相关项目:RambosPlayer
相关项目:AlertGateway
相关项目:MqttMonitor

技术栈:C++17 / Qt 5.14.2 / FFmpeg 4.x

posted @ 2026-07-06 13:19  rambos1996  阅读(3)  评论(0)    收藏  举报