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

     

  • 相关阅读:
    cocos 构建发布没有对话框
    Vue-事件
    “蔚来杯“2022牛客暑期多校训练营(加赛),签到题MHEJ
    Spring MVC 的核心组件有哪些?
    36 机器学习(四):异常值检测|线性回归|逻辑回归|聚类算法|集成学习
    PMP刷题小结2
    Go切片排序
    《天天数学》连载51:二月二十日
    RunnerGo 支持UI自动化的测试平台
    并发原理 — CPU原子性指令(一)
  • 原文地址:https://www.cnblogs.com/aqi00/p/18240179