科技 > 软件教程 > 媒体工具

FFmpeg开发笔记(三十七)分析SRS对HLS协议里TS包的插帧操作

57人参与 2024-08-04 媒体工具

《ffmpeg开发实战:从零基础到短视频上线》一书的“2.1.2  音视频文件的封装格式”介绍了视频流的ps格式和ts格式。由于ts包的长度固定,从ts流的任一片段开始都能独立解码,因此可以把ts当成音视频文件的封装格式。

鉴于ts包的独立解码特性,hls协议引入了ts格式作为传输单元。hls协议的实现原理是对一个大的媒体分片,并将分片后的文件路径记录于m3u8文件,客户端依据该m3u8文件即可获取对应的分片列表,再依次播放分片内容。每个ts分片都以sps与pps等配置帧开头,其中指定了视频的规格信息及其编码参数,因此每个ts片段都能正常解析播放。关于sps与pps的详细说明参见之前的文章《解析h.264码流中的sps帧和pps帧》。
上述的分片文件便是一个个以ts格式封装的视频资源,那么当直播源来自一个mp4文件的时候,流媒体服务器又是怎么把mp4文件转化为一个个ts分片的呢?
以srs为例,它在组装ts包时做了特殊处理,在每个ts包的开头位置,就自动插入sps与pps等配置帧。具体代码在srs框架的trunk/src/main/srs_main_ingest_hls.cpp,查看该源码的srsingesthlsoutput::on_ts_video函数,找到以下的代码片段,可见程序在写入h.264流时,先写入sps帧和pps帧,再写入i帧、p帧和b帧。

if ((ret = write_h264_sps_pps(dts, pts)) != error_success) {
    return ret;
}

if ((ret = write_h264_ipb_frame(ibps, frame_type, dts, pts)) != error_success) {
    // drop the ts message.
    if (ret == error_h264_drop_before_sps_pps) {
        return error_success;
    }
    return ret;
}

找到write_h264_sps_pps函数的定义代码如下,发现函数内部在封装序列头时依次输入了sps帧和pps帧:

// h264 raw to h264 packet.
std::string sh;
if ((err = avc->mux_sequence_header(h264_sps, h264_pps, sh)) != srs_success) {
    // todo: fixme: use error
    ret = srs_error_code(err);
    srs_freep(err);
    return ret;
}

进一步跟踪mux_sequence_header的定义来源,详细的定义代码在srs框架的trunk/src/protocol/srs_protocol_raw_avc.cpp,查看该源码的srsrawh264stream::mux_sequence_header函数,找到以下的代码片段,可见程序依据iso_iec_14496-15的文档规范,先后写入了sequenceparameterset的nal单元(即sps帧),以及pictureparameterset的nal单元(即pps帧)。

// sps
if (true) {
    // 5.3.4.2.1 syntax, iso_iec_14496-15-avc-format-2012.pdf, page 16
    // numofsequenceparametersets, always 1
    stream.write_1bytes(uint8_t(0xe0 | 0x01));
    // sequenceparametersetlength
    stream.write_2bytes((int16_t)sps.length());
    // sequenceparametersetnalunit
    stream.write_string(sps);
}

// pps
if (true) {
    // 5.3.4.2.1 syntax, iso_iec_14496-15-avc-format-2012.pdf, page 16
    // numofpictureparametersets, always 1
    stream.write_1bytes(0x01);
    // pictureparametersetlength
    stream.write_2bytes((int16_t)pps.length());
    // pictureparametersetnalunit
    stream.write_string(pps);
}

由此可见,srs在每个ts包头都写入了sps帧和pps帧,确保ts包是拥有sps和pps的完整h.264分片。只有加上sps与pps,客户端才能正常拉流解析数据,才能正常渲染视频画面。 
更多详细的ffmpeg开发知识参见《ffmpeg开发实战:从零基础到短视频上线》。

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址

08-04

FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo

08-04

FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg

08-04

FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧

08-04

FFmpeg开发笔记(四十)Nginx集成rtmp模块实现RTMP推拉流

08-04

FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频

08-04

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论