​《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开发实战:从零基础到短视频上线》一书。

FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作的更多相关文章

  1. FFmpeg开发笔记(三):ffmpeg介绍、windows编译以及开发环境搭建

    前言   本篇章是对之前windows环境的补充,之前windows的是无需进行编译的,此篇使用源码进行编译,版本就使用3.4.8.   FFmpeg简介   FFmpeg是领先的多媒体框架,能够解码 ...

  2. FFmpeg开发笔记(四):ffmpeg解码的基本流程详解

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  3. FFmpeg开发笔记(五):ffmpeg解码的基本流程详解(ffmpeg3新解码api)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  4. FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

    前言   ffmpeg播放rtsp网络流和摄像头流.   Demo   使用ffmpeg播放局域网rtsp1080p海康摄像头:延迟0.2s,存在马赛克     使用ffmpeg播放网络rtsp文件流 ...

  5. FFmpeg开发笔记(十):ffmpeg在ubuntu上的交叉编译移植到海思HI35xx平台

    FFmpeg和SDL开发专栏(点击传送门) 上一篇:<FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放>下一篇:敬请期待   前言   将ffmpeg移植到海思H ...

  6. Django开发笔记三

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.基于类的方式重写登录:views.py: from ...

  7. Django笔记三十三之缓存操作

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十三之缓存操作 这一节介绍一下如何在 Django 中使用 redis 做缓存操作. 在 Django 中可以有很多种方式做缓存,比如数 ...

  8. 《手把手教你》系列技巧篇(三十三)-java+ selenium自动化测试-单选和多选按钮操作-上篇(详解教程)

    1.简介 在实际自动化测试过程中,我们同样也避免不了会遇到单选和多选的测试,特别是调查问卷或者是答题系统中会经常碰到.因此宏哥在这里直接分享和介绍一下,希望小伙伴或者童鞋们在以后工作中遇到可以有所帮助 ...

  9. FFmpeg开发笔记(一)搭建Linux系统的开发环境

    对于初学者来说,如何搭建FFmpeg的开发环境是个不小的拦路虎,因为FFmpeg用到了许多第三方开发包,所以要先编译这些第三方源码,之后才能给FFmpeg集成编译好的第三方库.不过考虑到刚开始仅仅调用 ...

  10. Java开发笔记(十三)利用关系运算符比较大小

    前面在<Java开发笔记(九)赋值运算符及其演化>中提到,Java编程中的等号“=”表示赋值操作,并非数学上的等式涵义.Java通过等式符号“==”表示左右两边相等,对应数学的等号“=”: ...

随机推荐

  1. JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚

    JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚 介绍 首先非常感谢 FreeSQL 提供的部分源码,让我借鉴了不少功能点,整体设计并没有参考FreeSQL(因为jav ...

  2. ruby后台传变量到前端的JS里

    后端变量 @namelist 前端解释 var result_json = <%= @namelist.to_json.html_safe %>

  3. ubuntu下安装php pdo扩展和导入数据库

    默认安装的php不存在pdo扩展,因此在使用到的时候会报错,直接使用这个命令 apt-get install php-mysql 就可以成功安装pdo扩展 安装完数据库后需要导入sql语句,先进入数据 ...

  4. 3种方法实现图片瀑布流的效果(纯JS,Jquery,CSS)

    最近在慕课网上听如何实现瀑布流的效果:介绍了3种方法. 1.纯JS代码实现: HTML代码部分: <!DOCTYPE html> <html> <head> < ...

  5. Oracle修改字段长度及属性

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247486117&idx=1 ...

  6. Docker 必知必会2----跟我一步步来执行基本操作

    通过前文(https://www.cnblogs.com/jilodream/p/18177695)的了解,我们已经大致明白了什么是docker,为什么要用docker,以及docker的基本设计思路 ...

  7. 4G EPS 中的各种唯一标识

    目录 文章目录 目录 电信运营商的唯一标识:PLMN.MCC 与 MNC 移动用户的唯一标识:IMSI.MSIN 与 MSISDN/MDN 移动用户的唯一临时标识:TMSI.GUTI 与 GUMMEI ...

  8. openstack虚拟机用keep alive添加的VIP,其它机器无法访问

    neutron port-list |grep ipneutron port-update a7fbxxf6cc2 --allowed_address_pairs type=dict list=tru ...

  9. EDP .Net开发框架--自动化日志

    平台下载地址:https://gitee.com/alwaysinsist/edp 自动化日志不需要额外调用日志相关功能即可无感实现程序集方法调用的日志记录. 创建业务逻辑处理类 public cla ...

  10. 将外部jar打入本地maven仓库

    1.将jar包放入某不含中文的路径下 ,例如:E:\file\zip4j-1.3.2.jar 2.在命令行输入操作命令 mvn install:install-file -DgroupId=zip4j ...