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

怀念夏天  金牌会员 | 2024-6-29 13:43:21 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 881|帖子 881|积分 2643

​《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函数。
  1. // 判断是否是I帧, 并且如果是,那判断前面是否插入过config帧, 如果插入过就不插入了
  2. if (frame->keyFrame() && !_latest_is_config_frame) {
  3.     insertConfigFrame(frame); // 插入SPS帧和PPS帧
  4. }
  5. if(!frame->dropAble()){
  6.     _latest_is_config_frame = false;
  7. }
  8. ret = VideoTrack::inputFrame(frame);
复制代码
找到insertConfigFrame函数的定义代码如下,果然函数内容依次插入了SPS帧和PPS帧:
  1. // 插入SPS帧和PPS帧
  2. void H264Track::insertConfigFrame(const Frame::Ptr &frame) {
  3.     if (!_sps.empty()) { // 插入SPS帧
  4.         auto spsFrame = FrameImp::create<H264Frame>();
  5.         spsFrame->_prefix_size = 4;
  6.         spsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
  7.         spsFrame->_buffer.append(_sps);
  8.         spsFrame->_dts = frame->dts();
  9.         spsFrame->setIndex(frame->getIndex());
  10.         VideoTrack::inputFrame(spsFrame);
  11.     }
  12.     if (!_pps.empty()) { // 插入PPS帧
  13.         auto ppsFrame = FrameImp::create<H264Frame>();
  14.         ppsFrame->_prefix_size = 4;
  15.         ppsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
  16.         ppsFrame->_buffer.append(_pps);
  17.         ppsFrame->_dts = frame->dts();
  18.         ppsFrame->setIndex(frame->getIndex());
  19.         VideoTrack::inputFrame(ppsFrame);
  20.     }
  21. }
复制代码
由此可见,ZLMediaKit在每个关键帧前面都额外插入了SPS帧和PPS帧,确保H.264裸流维持着形如“SPS帧→PPS帧→IDR帧”的队形。假如不添加SPS和PPS,客户端在拉流时会报错如下:
  1. [NULL @ 0000022ed7782540] non-existing PPS 0 referenced
复制代码
只有加上SPS与PPS,客户端才能正常拉流解析数据,才能正常渲染视频画面。 
更多详细的FFmpeg开发知识参见《FFmpeg开发实战:从零基础到短视频上线》一书。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

怀念夏天

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表