这段时间在捣腾基于 RTMP 协议的流媒体直播框架,其间参考了众多博主的文章,剩下一些细节问题自行琢磨也算摸索出个门道,现将自己认为比较恼人的 AAC 音频帧的推送和解析、H264 码流的推送和解析以及网上没说清楚的地方分享给各位。

RTMP 协议栈的实现,Bill 直接使用的 libRTMP,关于 libRTMP 的编译、基本使用方法,以及简单的流媒体直播框架,请参见博文[C++实现RTMP协议发送H.264编码及AAC编码的音视频],言简意赅,故不再赘述。

言归正传,我们首先来看看 AAC 以及 H264 的推送。

不论向 RTMP 服务器推送音频还是视频,都需要按照 FLV 的格式进行封包。因此,在我们向服务器推送第一个AAC 或 H264 数据包之前,需要首先推送一个音频 Tag [AAC Sequence Header] 以下简称“音频同步包”,或者视频 Tag [AVC Sequence Header] 以下简称“视频同步包”。

AAC 音频帧的推送

我们首先来看看音频 Tag,根据 FLV 标准 Audio Tags 一节的描述:

我们可以将其简化并得到 AAC 音频同步包的格式如下:

音频同步包大小固定为 4 个字节。前两个字节被称为 [AACDecoderSpecificInfo],用于描述这个音频包应当如何被解析。后两个字节称为 [AudioSpecificConfig],更加详细的指定了音频格式。

[AACDecoderSpecificInfo] 俩字节可以直接使用 FAAC 库的 faacEncGetDecoderSpecificInfo 函数来获取,也可以根据自己的音频源进行计算。一般情况下,双声道,44kHz 采样率的 AAC 音频,其值为 0xAF00,示例代码:

根据 FLV 标准 不难得知,[AACDecoderSpecificInfo] 第 1 个字节高 4 位 |1010| 代表音频数据编码类型为 AAC,接下来 2 位 |11| 表示采样率为 44kHz,接下来 1 位 |1| 表示采样点位数 16bit,最低 1 位 |1| 表示双声道。其第二个字节表示数据包类型,0 则为 AAC 音频同步包,1 则为普通 AAC 数据包。

音频同步包的后两个字节 [AudioSpecificConfig] 的结构,援引其他博主图如下:

我们只需参照上述结构计算出对应的值即可。至此,4 个字节的音频同步包组装完毕,便可推送至 RTMP 服务器,示例代码如下:

网上有博主说音频采样率小于等于 44100 时 SamplingFrequencyIndex 应当选择 3(48kHz),Bill 测试发现采样率等于 44100 时设置标记为 3 或 4 均能正常推送并在客户端播放,不过我们还是应当按照标准规定的行事,故此处的 SamplingFrequencyIndex 选 4。

完成音频同步包的推送后,我们便可向服务器推送普通的 AAC 数据包,推送数据包时,[AACDecoderSpecificInfo] 则变为 0xAF01,向服务器说明这个包是普通 AAC 数据包。后面的数据为 AAC 原始数据去掉前 7 个字节(若存在 CRC 校验,则去掉前 9 个字节),我们同样以一张简化的表格加以阐释:

推送普通 AAC 数据包的示例代码:

至此,我们便完成了 AAC 音频的推送流程。此时可尝试使用 VLC 或其他支持 RTMP 协议的播放器连接到服务器测试正在直播的 AAC 音频流。

H264 码流的推送

前面提到过,向 RTMP 服务器发送 H264 码流,需要按照 FLV 格式进行封包,并且首先需要发送视频同步包[AVC Sequence Header]。我们依旧先阅读 FLV 标准 Video Tags 一节:

由于视频同步包前半部分比较简单易懂,仔细阅读上述标准便可明白如何操作,故 Bill 不另作图阐释。由上图可知,我们的视频同步包 FrameType == 1,CodecID == 7,VideoData == AVCVIDEOPACKET,继续展开 AVCVIDEOPACKET,我们可以得到 AVCPacketType == 0x00,CompositionTime == 0x000000,Data == AVCDecoderConfigurationRecord。

因此构造视频同步包的关键点便是构造 AVCDecoderConfigurationRecord。同样,我们援引其他博主的图片来阐释这个结构的细节:

其中需要额外计算的是 H264 码流的 Sps 以及 Pps,这两个关键数据可以在开始编码 H264 的时候提取出来并加以保存,在需要时直接使用即可。具体做法请读者自行 Google 或参见 参考博文[2],在此不再赘述。

当我们得到本次 H264 码流的 Sps 以及 Pps 的相关信息后,我们便可以完成视频同步包的组装,示例代码如下:

至此,视频同步包便构造完毕并推送给 RTMP 服务器。接下来只需要将普通 H264 码流稍加封装便可实现 H264 直播,下面我们来看一下普通视频包的组装过程。

回顾 FLV 标准 的 Video Tags 一节,我们可以得到 H264 普通数据包的封包信息,FrameType == (H264 I 帧? 1 : 2),CodecID == 7,VideoData == AVCVIDEOPACKET,继续展开,我们可以得到  AVCPacketType == 0x01,CompositionTime 此处仍然设置为 0x000000,具体原因 TODO(billhoo),Data == H264 NALU Size + NALU Raw Data。

构造视频数据包的示例代码如下:

至此 H264 码流的整个推送流程便已完成,我们可以使用 VLC 或其他支持 RTMP 协议的播放器进行测试。

关于 AAC 音频帧及 H264 码流的时间戳

通过前文的步骤我们已经能够将 AAC 音频帧以及 H264 码流正常推送到 RTMP 直播服务器,并能够使用相关播放器进行播放。但播放的效果如何还取决于时间戳的设定。

在网络良好的情况下,自己最开始使用的音频流时间戳为 AAC 编码器刚输出一帧的时间,视频流时间戳为 H264 编码器刚编码出来一帧的时间,VLC 播放端就频繁报异常,要么是重新缓冲,要么直接没声音或花屏。在排除了推送步骤实现有误的问题后,Bill 发现问题出在时间戳上。

之后有网友说直播流的时间戳不论音频还是视频,在整体时间线上应当呈现递增趋势。由于 Bill 最开始的时间戳计算方法是按照音视频分开计算,而音频时戳和视频时戳并不是在一条时间线上,这就有可能出现音频时戳在某一个时间点比对应的视频时戳小, 在某一个时间点又跳变到比对应的视频时戳大,导致播放端无法对齐。

目前采用的时间戳为底层发送 RTMP 包的时间,不区分音频流还是视频流,统一使用即将发送 RTMP 包的系统时间作为该包的时间戳。目前局域网测试播放效果良好,音视频同步且流畅。

参考博文

[1][C++实现RTMP协议发送 H.264 编码及 AAC 编码的音视频]

[2][使用 libRtmp 进行 H264 与 AAC 直播]

[3][RTMP直播到FMS中的AAC音频直播]

本文转自Bill_Hoo 51CTO博客,原文链接:http://blog.51cto.com/billhoo/1557646,如需转载请自行联系原作者

【基于libRTMP的流媒体直播之 AAC、H264 推送】相关推荐

  1. (转)基于libRTMP的流媒体直播之 AAC、H264 推送

    参考: 1,基于libRTMP的流媒体直播之 AAC.H264 推送 http://billhoo.blog.51cto.com/2337751/1557646 转载于:https://www.cnb ...

  2. 基于Tomcat7、Java、WebSocket的服务器推送聊天室

    2019独角兽企业重金招聘Python工程师标准>>> 基于Tomcat7.Java.WebSocket的服务器推送聊天室 转载于:https://my.oschina.net/u/ ...

  3. Android之基于xmpp openfire smack开发之Android消息推送技术原理分析和实践[4]

    http://blog.csdn.net/shimiso/article/details/8156439 前面几篇给大家系统讲解的有关xmpp openfire smack asmack相关的技术和使 ...

  4. 基于 WEB 的实时事件通知方式 服务器推送

    下面这些可以实现基于web的实时事件通知的方法.在他们的实验性研究中在一个利用COMET推送方式实现(Dojo的Cometd库,dwr的反向Ajax)的AJAX应用和一个纯拉取方式的应用之间,对数据一 ...

  5. Tomcat学习总结(4)——基于Tomcat7、Java、WebSocket的服务器推送聊天室

    前言         HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要求比较高的应用.以前的服务器消息推送大部分 ...

  6. 技术分享| 基于RTM 实现的呼叫邀请如何添加推送功能?

    RTM 实时消息服务,解决了在线实时信令的传递,如何配合推送服务,去做离线通知功能一直困扰着开发者,本文从 RTM 的功能以及响应机制入手,教大家如何通过RTM配合第三方推送服务来完成离线消息通知. ...

  7. 斗鱼html5直播流,用树莓派做 RTMP 流直播服务器,可推送至斗鱼直播

    楼主你好! 我推流到一个Windows的Ngix-rtmp云服务器上树莓派会发至命错误,请教一下是什么原因造成的呢?端口已开放,但在本局域网里推流一切正常. gst-launch-1.0 -v v4l ...

  8. 树莓派 video4linux,用树莓派做 RTMP 流直播服务器,可推送至斗鱼直播

    楼主你好! 我推流到一个Windows的Ngix-rtmp云服务器上树莓派会发至命错误,请教一下是什么原因造成的呢?端口已开放,但在本局域网里推流一切正常. gst-launch-1.0 -v v4l ...

  9. 最简单的基于librtmp的示例 发布(FLV通过RTMP发布)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! ==== ...

最新文章

  1. grafana官方使用文档_使用 Loki 采集微服务日志
  2. NLP:基于snownlp库对文本实现提取文本关键词和文本摘要
  3. ubuntu如何安装linux驱动程序,Ubuntu下如何安装驱动程序和应用软件?
  4. 阿里云kafka使用记录(python版本)
  5. 移动前端适配—逻辑像素和物理像素
  6. github和gitlab仓库一起使用
  7. FreeRTOS调度器挂起与解除
  8. python编程快速上手第四章_《Python编程快速上手——让繁琐的工作自动化》读书笔记 第四章 列表...
  9. 算法笔记_面试题_4.树的遍历(前序/中序/后续遍历)
  10. 阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_1 请求参数绑定入门
  11. Atitit 通用接口的设计与实现attilax 总结
  12. 从放弃迅雷和IDM到自己开发下载工具 1
  13. 【Flutter系列】第一期——初识Dart语言
  14. 2019 Java 全栈工程师进阶路线图,一定要收藏!
  15. 深度学习入门笔记(8)——什么是optim?
  16. 田忌赛马c语言程序设计,关于c语言的田忌赛马问题。
  17. PowerManagerService(一)
  18. 【信号去噪】基于NLM时间序列心电信号去噪附matlab代码
  19. 微软专利展示了具有灵活触控笔的双屏设备
  20. 【OpenCV】 全景拼接——多张图像拼接

热门文章

  1. 解决老版本eclipse无法完全替换主题的问题
  2. 【C# 复习总结】类、继承和接口
  3. 数据结构5: 链表(单链表)的基本操作及C语言实现
  4. Vue.js之UI组件elementUI——MintUI
  5. Launcher3删除开机引导页面
  6. MySQL性能优化之参数配置
  7. java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
  8. 配置GoldenGate源端Manager参数
  9. C# ListT用法
  10. C#下的插件体系探讨