FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作

《FFmpeg开发实战:从零基础到短视频上线》一书的"3.4.3 把原始的H264文件封装为MP4格式"介绍了如何把H.264裸流封装为MP4文件。那么在网络上传输的H.264裸流是怎样被接收端获取视频格式的呢?前文指出H.264流必定以"SPS帧→PPS帧→IDR帧"开头,接下来就来验证是否确实如此。

这里用到了雷霄骅雷神写的H264分析器,在此向雷神致敬,雷神10年前写的小程序至今仍然好用。打开H264分析器,该软件的初始界面如下图所示:

单击文件路径栏右边的打开按钮,在弹出的文件对话框中选择某个H.264裸流文件,再单击界面右下角的开始按钮,分析器便开始分析H264文件的内容格式,分析后的结果界面如下图所示:

从分析结果可见,H.264裸流的开头三帧果然是"SPS帧→PPS帧→IDR帧"。单击列表中的某个帧,界面右侧会显示该帧的详细字段信息。

当然,分析器只能读取H.264裸流文件。倘若让分析器读取MP4文件,就无法正常读出各帧信息。那么流媒体服务器又是怎么把MP4文件转化为H.264裸流的呢?

以ZLMediaKit为例,它在向推流序列插入I帧时做了特殊处理,一旦出现I帧,就自动插入SPS与PPS等配置帧。具体代码在ZLMediaKit框架的ext-codec/H264.cpp,查看该源码的H264Track::inputFrame_l函数,找到以下的代码片段,可见程序在判断关键帧之后调用了insertConfigFrame函数。

复制代码
// 判断是否是I帧, 并且如果是,那判断前面是否插入过config帧, 如果插入过就不插入了
if (frame->keyFrame() && !_latest_is_config_frame) {
    insertConfigFrame(frame); // 插入SPS帧和PPS帧
}
if(!frame->dropAble()){
    _latest_is_config_frame = false;
}
ret = VideoTrack::inputFrame(frame);

找到insertConfigFrame函数的定义代码如下,果然函数内容依次插入了SPS帧和PPS帧:

复制代码
// 插入SPS帧和PPS帧
void H264Track::insertConfigFrame(const Frame::Ptr &frame) {
    if (!_sps.empty()) { // 插入SPS帧
        auto spsFrame = FrameImp::create<H264Frame>();
        spsFrame->_prefix_size = 4;
        spsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
        spsFrame->_buffer.append(_sps);
        spsFrame->_dts = frame->dts();
        spsFrame->setIndex(frame->getIndex());
        VideoTrack::inputFrame(spsFrame);
    }
    if (!_pps.empty()) { // 插入PPS帧
        auto ppsFrame = FrameImp::create<H264Frame>();
        ppsFrame->_prefix_size = 4;
        ppsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
        ppsFrame->_buffer.append(_pps);
        ppsFrame->_dts = frame->dts();
        ppsFrame->setIndex(frame->getIndex());
        VideoTrack::inputFrame(ppsFrame);
    }
}

由此可见,ZLMediaKit在每个关键帧前面都额外插入了SPS帧和PPS帧,确保H.264裸流维持着形如"SPS帧→PPS帧→IDR帧"的队形。如果不添加SPS和PPS,客户端在拉流时会报错如下:

复制代码
[NULL @ 0000022ed7782540] non-existing PPS 0 referenced

只有加上SPS与PPS,客户端才能正常拉流解析数据,才能正常渲染视频画面。

更多详细的FFmpeg开发知识参见《FFmpeg开发实战:从零基础到短视频上线》一书。

相关推荐
REDcker6 天前
WebCodecs VideoDecoder 的 hardwareAcceleration 使用
前端·音视频·实时音视频·直播·webcodecs·videodecoder
gihigo19986 天前
基于TCP协议实现视频采集与通信
网络协议·tcp/ip·音视频
山河君6 天前
四麦克风声源定位实战:基于 GCC-PHAT + 最小二乘法实现 DOA
算法·音视频·语音识别·信号处理·最小二乘法·tdoa
音视频牛哥6 天前
Android平台RTMP/RTSP超低延迟直播播放器开发详解——基于SmartMediaKit深度实践
android·人工智能·计算机视觉·音视频·rtmp播放器·安卓rtmp播放器·rtmp直播播放器
qq_416276426 天前
通用音频表征的对比学习
学习·音视频
美狐美颜sdk6 天前
Android全局美颜sdk实现方案详解
人工智能·音视频·美颜sdk·视频美颜sdk·美狐美颜sdk
EasyDSS6 天前
私有化部署EasyDSS视频点播能力:全链路视频技术赋能,打造企业级视听新体验
音视频·hls·m3u8·点播技术·智能转码
qq_416276426 天前
DeLoRes——一种通用的音频表征学习新方法(DeLoRes(基于 Barlow Twins 的冗余最小化方法)
学习·音视频
Q_4582838686 天前
从定位到视频:808 + 1078 在各行业的落地实践
音视频
山顶望月川6 天前
实测MiniMax-Hailuo-02:当“开工大吉“变成“无字天书“,国产AI视频模型的能与之不能
人工智能·音视频