STTFFmpeg 封装本地媒体、摄像头、HTTP/RTSP 流的播放。ARM 真机的视频路径通常走硬件播放器,音频及模拟器走 LVGL FFmpeg;STTFFmpegSoft 强制使用软件路径。两套后端行为并不完全一致。
// 必须在创建第一个视频控件之前调用
STTFFmpeg::setVideoChannels(2);
STTFFmpeg::setLiveAlignInterval(60);
STTFFmpeg::rtsp_transport = "tcp";
STTFFmpeg* cam1 = new STTFFmpeg(getRuntime(), this, "cam1");
cam1->channel = 0;
STTFFmpeg* cam2 = new STTFFmpeg(getRuntime(), this, "cam2");
cam2->channel = 1;
构造函数会自动调用一次 init();也可由应用显式调用。IsInited 表示底层初始化是否成功。setVideoChannels(n) 只支持 1..4,并在 ARM 上按路数调整队列内存,必须在首次 init/构造之前调用才生效。
当前构造函数把每个实例的公开字段 channel 初始设为 0。多路播放时必须为实例分配唯一的 0..3 通道,否则它们会共享播放状态、列表和底层播放器,产生互相停止或覆盖。
setLiveAlignInterval(seconds) 设置 RTSP 定期清缓冲间隔;负数禁用。清缓冲可降低累计延迟,但可能短暂跳帧、爆音或等待下一个关键帧。
本地文件:
video = new STTFFmpeg(getRuntime(), this, "video");
video->channel = 0;
video->setMainArg(20, 20, 440, 248, "/customer/usr/demo.mp4");
video->setVolume(60)->setAutoRestart(false)->play();
RTSP 页面无阻塞初始化:
video->setMainArgAsync(20, 20, 440, 248,
"rtsp://user:pass@192.168.1.10/live", 300);
setMainArg(x,y,w,h,src) 自动识别源并设置播放列表。ARM 视频窗口的宽高会向下取 8 的倍数,例如 445 变为 440。setMainArg(src) 使用之前保存的位置和尺寸。
setMainArgAsync 先固定等待约 200 ms,再在完成回调中配置;delayms=0 立即播放,>0 再延时播放,<0 只配置不播放。对象被销毁后会通过内部映射跳过回调,但仍应在页面关闭前主动停止。
带分辨率、帧率和 const char* 的 setMainArg 以及 setVideoProp 是兼容旧代码/摄像头的接口,新文件播放优先使用 string 重载。
video->play();
video->pause();
video->resume();
video->gotoSec(30);
video->stop(200); // 先暂停,200 ms 后停止
int command = video->getCmd();
double total = video->getDuration();
double current = video->getPlayedSecond();
int frames = video->getFrameNum();
setCmd(0/1/2/3):播放、停止、暂停、继续;继续后内部命令状态记为 0。play(ms):ms<=0 直接播放,正数异步延时。stop(ms):0 直接停止;正数先暂停,再异步停止。gotoSec(sec):负数改为 0;直播流通常不支持任意 seek。getCmd() 是最近命令,不等价于底层真实网络/解码状态。getDuration() / getPlayedSecond() 失败返回 0,直播时 duration 可能未知。静态帧信息:
int w = 0, h = 0, fps = 0;
int count = STTFFmpeg::getFrameNum("/customer/usr/demo.mp4", w, h, fps);
解析文件可能耗时,RTSP 不应依赖完整帧数。返回 0 既可能表示失败,也可能表示未知,需结合源类型判断。
video->setPos(20, 20)
->setSize(440, 248)
->setImgSize(1280, 720)
->setImgZoom(1.0f);
video->stop();
video->setRotate(90);
video->setRotateContent(90);
video->play();
setPos/setSize 设置显示窗口并保存参数。setImgSize 设置内部图像区域。setImgZoom(z) 使用 z*256;z<=0 会退回 1 倍,当前实现没有限制超大值。setRotate 用于跟随屏幕方向,接受 0..3 或 0/90/180/270;ARM 会交换 90/270 的内部方向。setRotateContent 仅旋转内容,当前只在 ARM 生效。setHidden(bool) 同时隐藏/显示内部 image 和容器,并在 ARM 调整 framebuffer 透明色。旋转说明虽写“调用 play 生效”,当前 ARM 视频还会尝试立即更新窗口;为保持各平台一致,仍建议 stop、设置、play。
video->setVolume(70);
video->ignoreAudio(false);
STTFFmpeg::ignoreAudioHard(true, 0);
STTFFmpeg::intercutWav("/customer/usr/alarm.wav", 0);
setVolume(value) 设计范围为 0..100,但当前实现本身没有统一钳制;调用者必须限制范围。音量保存在全局值中,新播放会再次应用,因此多个实例可能互相影响。
ignoreAudio 是实例通道静音/停止送硬件;ignoreAudioHard 和 intercutWav 是指定通道的静态操作。模拟器行为不同:忽略音频通过清空回调实现,插播走 WavePlayer。
自定义音频设备回调:
video->setAudioInitCb(myAudioInit)
->setAudioPlayCb(myAudioPlay)
->setAudioVolumeCb(myAudioVolume)
->setAudioDeInitCb(myAudioDeInit);
这些是全局底层回调,不是实例私有配置。函数和其依赖资源必须在整个播放期有效;多控件不要分别设置互相冲突的回调。
vector<string> files = {
"/customer/usr/a.mp4",
"/customer/usr/b.mp4"
};
video->stop();
video->setPlayList(files, true)
->setAutoRestart(true)
->play();
video->addPlayList("/customer/usr/c.mp4", false);
video->playNext();
video->playPrev();
video->removePlayList("/customer/usr/b.mp4", true);
setPlayList(vector,resetSrc) 替换列表,非空时当前索引设为 0;resetSrc=true 同时设置第一项为当前源。setPlayList(path) 建立单项列表。addPlayList(...,allowSame) 默认去重。removePlayList(path,once) 不删除正在播放项;空 path 表示清除非播放项或在停止时全清。playNext/playPrev 循环到首尾。setRandPlay/isRandPlay 控制随机下一项。setAutoRestart 是业务推荐的自动续播开关;enLoop/isLoop 是底层循环标志。getPlayList() 返回内部 vector 指针,不要 delete,也不要长期缓存。播放线程可能访问列表,修改列表前先 stop();头文件也明确要求替换列表时关闭视频。
只有一个条目时开启随机播放会进入“排除当前索引”的随机循环风险,不要对单项列表启用随机。
STTFFmpeg::rtsp_transport = "tcp"; // 也可 "udp"
bool online = STTFFmpeg::checkRtspSource(
"rtsp://user:pass@192.168.1.10:554/live");
if (online) video->setMainArgAsync(20, 20, 440, 248, url, 0);
checkRtspSource 是同步探测。ARM 当前通过 TCP 连接并发送 DESCRIBE,连接和接收各约 1 秒超时,只把 RTSP 200 视为成功;认证支持取决于服务器响应。不要在 UI 高频定时器直接调用,应放到工作线程。
getRtspStatus() 返回位掩码,而不是单一枚举:
| 位 | 含义 |
|---|---|
0x0001/0x0002 |
音频/视频完成 |
0x0004 |
暂停 |
0x0008/0x0010 |
音频/视频解码错误 |
0x0020 |
不同步 |
0x0040/0x0080 |
读取超时/网络中断 |
0x0100 |
无效源 |
0x0200/0x0400 |
静音/音频暂停 |
0x0800/0x1000 |
循环/切换文件 |
0x2000/0x4000 |
加载中/源连接中断 |
返回 <0 表示播放器未打开,0 表示没有置位状态或正常播放。必须用按位与判断,例如 (status & 0x0040) != 0。
statusLabel 是 RTSP 状态标签,可改样式或隐藏,但由视频控件按需创建和管理,不要 delete。
flushBuffer() 在 ARM 清 packet、帧队列和解码器以降低直播延迟;会造成画面短暂中断,模拟器为空操作。
bool saved = video->saveJpg("/customer/usr/capture.jpg");
video->setImage("/customer/usr/poster.png");
bool av = video->isAv();
saveJpg 只在已经打开且正在产生画面时可靠;路径后缀可用 PNG/JPG。ARM 调硬件播放器保存,模拟器走控件截图,结果和颜色通道可能有差异。不要在同一路视频上高频并发截图。
isAv() 根据文件/协议/摄像头路径重新判断并写入公开字段 isAV。setImage 直接给内部 LVGL image 设置源,路径资源必须有效。
video->setVarOfCmd("Video.Command");
Ivariable commandVar = video->getVarOfCmd();
循环播放流程中,播放完成可把变量设为 1,下一项开始时设为 0。setVarOfCmd 未检查变量是否存在,使用前先通过变量管理器验证名称。
onDataChanged 在当前实现中为空;仅调用继承的 bindData 不会自动把变量值解释成播放命令,需使用命令变量/变量消息或业务回调。
STTFFmpeg::delayLoadRtsp(
{cam1, cam2},
{0, 500},
{url1, url2});
三个 vector 按最短长度配对,空指针及非法通道被跳过。内部先延时约 200 ms,再依次配置;ms=0 立即播放、正数延时、负数不播放。调用前先设置每个控件的位置、尺寸和唯一通道。
void Frm01::beforeClose() {
video->stop();
STTFFmpeg::prepareCloseAll(50);
// 随后再销毁页面/视频对象
}
prepareCloseAll 用于避免多个 ARM 硬件通道在析构时逐个长时间等待。注意当前实现与头文件“异步预关闭”说明不一致:它先等待约 200 ms,然后并行启动 close,但会 join 等到所有 close 完成;参数 closeWaitMs 当前未使用。因此该调用本身仍可能阻塞,只是多路关闭并行化。模拟器为空操作。
析构时若已预关闭,会 detach 播放线程而不 join。页面切换前应确保业务不再访问对象,也不要立即复用仍在退出的外部资源。
STTFFmpegSoft* soft = new STTFFmpegSoft(getRuntime(), this, "soft");
soft->setMainArg(20, 20, 440, 248, "/customer/usr/demo.mp4");
soft->setVolume(60)->setAutoRestart(true)->play();
它重载 getFrameNum、setSrc、setCmd、play、stop、pause、resume、gotoSec、getDuration、getPlayedSecond、setAutoRestart、setVolume、setRotate 和两种 setMainArg。
实际差异:
lv_ffmpeg_player_* 软件路径。setVolume(value) 传给底层的是 value-60,与主类 ARM 语义不同。setRotate 当前直接返回,不做任何旋转。stop() 是无参数重载。setMainArg 同样把宽高向下取 8 的倍数。软解码占用 CPU 和内存更高,适合硬解码不支持的格式或调试,不适合未经压测的多路高清播放。
channel;所有通道索引必须在 0..3。filePath、iPlaying、resX/resY、isAV 等公开字段主要供框架使用,业务优先调用方法。enHardDecode 注释已说明过期;H.264/H.265 由底层自动决定。enFlipHor 需在停止后设置,再播放生效。STT_USE_FFMPEG 在头文件定义为 0 是编译期标志,不代表构造一定成功;运行时还必须检查平台库、编解码器和 IsInited。