1.SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
2.RTMP 视频数据封装
3.分析流媒体服务器源码:Rtmp发布流程的源码SRS解析
4.流媒体客户端RTMP拉流保存h264(flv保存为h264)
5.SRS(simple-rtmp-server)流媒体服务器源码分析--启动
6.nginx-rtmp-module学习
SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
本章内容梳理了SRS在接收到RTMP信息后如何进行转发的过程。在此过程中,大全首先进行代码梳理,源码作者也在源码熟悉阶段,大全可能尚未完全梳理完接受到RTMP后信息如何处理、源码缓存以及转发给直播用户等内容。大全主力红绿指标源码
SRS源码中的源码Play流程如下:
1. 进入play流程:本章内容直接从SrsRtmpConn::stream_service_cycle()方法开始梳理。
2. 在接受流程中,大全客户类型为SrsRtmpConnFMLEPublish “fmle publish”,源码而在转发流程中,大全客户类型为SrsRtmpConnPlay。源码
3. 在/ossrs/srs/wiki...,大全导出后,源码在research/librtmp下有作者编写的大全demo,其中srs_rtmp_dump.c用于从rtmp服务器拉流并保存为flv文件。源码
以下是简化版的demo源码,我注释了自己的理解,若有错误请指正。在vs下此代码能编译运行,但在linux下能正常播放。
主要讲述了flv头信息的结构,srs_librtmp源码中srs_flv_write_tag通过data封装成Tag并写入flv文件,srs_rtmp_read_packet读取的数据是flv文件中的tag data。
Tag data分为Audio、Video、Script三种,这里仅讲解Video Tag Data。
VideoTagHeader的第一个字节包含了视频帧类型及视频CodecID的基本信息。VideoTagHeader之后跟着的是VIDEODATA数据,即video payload,对于H.格式的视频,VideoTagHeader会额外包含4个字节的信息。
AVCPacketType和CompositionTime。AVCPacketType表示VIDEODATA的内容类型:若AVCPacketType为0,则为AVCDecoderConfigurationRecord(H.序列头);若为1,则为一个或多个NALU(完整帧是源码 innertext必需的)。
AVCDecoderConfigurationRecord包含H.解码相关的sps和pps信息,解码器在送数据流之前必须送出sps和pps信息,否则解码器不能正常解码。在解码器停止后再次开始之前,如seek、快进快退状态切换等,都需要重新送出sps和pps的信息。AVCDecoderConfigurationRecord在FLV文件中通常只出现一次,即第一个video tag,但有些视频流的sps和pps可能会发生变化,所以可能会出现多次。
Composition Time用于告知渲染器视频帧进入解码器后多长时间在设备上显示。在flv格式中,timestamp用于告知帧何时提供给解码器,单位为毫秒。Composition Time告诉渲染器视频帧显示的时间,因此compositionTime = (PTS - DTS) / .0。
总结如下:使用srs_librtmp拉流,拉取的数据为一个又一个的Tag Data,可通过type与宏值比较判断Tag Data是否为Video Tag Data。连接rtmp服务器拉流时收到的第一个Video Tag Data通常包含PPS和SPS信息。对于每个h编码的Video Tag Data,会多出4个字节的AVCPacketType和CompositionTime,其中CompositionTime用于B帧,这里暂时忽略它,我们仅支持P帧和I帧。Frame Type在h编码中只能是1或2,Frame Type == 1表示关键帧或包含PPS和SPS信息的Video Tag Data。CodecID在h编码中只能是7(AVC)。当AVCPacketType == 0时,Video Tag Data包含SPS和PPS信息;当AVCPacketType == 1时,为帧数据。
获取PPS和SPS信息非常关键,如果不告知解码器,根本无法播放视频。tabhost 源码我写了一段代码,虽然技术有限,但希望能帮助到您。
AVCPacketType为1表示Video Tag Body的内容是NALU。Frame Type为1表示NALU内容是关键帧,Frame Type为2表示NALU内容是非关键帧。NALU的开头的4个字节表示NALU的长度(nalu_length),nalu_length之后是一个字节的nalu header。
nalu header中nal_ref_idc表示优先级,范围在~(2进制),值越大表示越重要。值指示NAL单元的内容不用于重建影响图像的帧间图像预测。对于nal_unit_type为6、9、、、的NAL单元,H.规范要求NRI的值应该为0。对于nal_unit_type等于7、8(指示顺序参数集或图像参数集)的NAL单元,H.编码器应设置NRI为(二进制格式)。nal_unit_type表示nalu类型,SPS开头是0x(nal_ref_idc为3,nal_unit_type为7),PPS开头是0x(nal_ref_idc为3,nal_unit_type为8),关键帧开头是0x(nal_ref_idc为3,nal_unit_type为5),非关键帧开头是0x(nal_ref_idc为2,nal_unit_type为1)。nal_unit_type为5表示idr帧,idr帧具有随机访问能力,所以每个idr帧前需要加上sps和pps。startcode起始码。
H.原始码流由一个一个的jsf源码NALU组成,其结构包括起始码(0x或0x,取决于编码器实现)和数据。具体何时使用3个字节的起始码,何时使用4个字节的起始码,这个我没有完全弄明白,资料中提到具体哪种开头取决于编码器实现。0x是NAL起始前缀码,解码器检测每个起始码,作为NAL的起始标识,当检测到下一个起始码时,当前NAL结束。同时H.规定,当检测到0x时,也可以表示当前NAL的结束。对于NAL中数据出现0x或0x时,H.引入了防止竞争机制,如果编码器检测到NAL数据存在0x或0x时(非起始码,而是真正的音视频数据),编码器会在最后个字节前插入一个新的字节0x,这样当遇到0x或0x时就一定是起始码了。解码器检测到0x时,把抛弃,恢复原始数据。因此,组装H的步骤如下:读取tag data并判断是否是video tag data,判断frameType和AVCPacketType,区分video tag data是AVCDecoderConfigurationRecord还是NALU,如果是AVCDecoderConfigurationRecord则解析PPS和SPS保存在内存中并加上startcode(我这里加的是0x),如果是NALU,则判断nal_unit_type(有些NALU的流比较奇怪,依然包含PPS、SPS信息,甚至还有SEI信息)。switch case根据不同的nal_unit_type来解析,并加上startcode。shadowssocks源码如果nal_unit_type == 0x,则是idr帧,需要加上PPS和SPS信息(即一个idr通常包含3个startcode,SPS一个PPS一个idr帧数据一个)。
以下是完整代码:
rtmpTo.h
rtmpTo.cpp
main.cpp
原文链接:blog.csdn.net/qq_...
SRS(simple-rtmp-server)流媒体服务器源码分析--启动
小卒最近探索了SRS源码,并撰写博客以整理思路,方便日后查阅。SRS源码具备以下优势:
1、轻量化设计,代码结构清晰,SRS3.0版本代码量约为8万行,功能却足以支撑直播业务。
2、采用State Threads架构,实现高性能、高并发。
3、支持rtmp和hls,满足PC和移动直播的需求。
4、支持集群部署,适应不同规模的部署需求。
代码分析分为两个阶段:一、梳理代码框架,理解流程;二、深入细节,熟悉SRS工作原理。
SRS源码框架包括系统启动、RTMP消息处理、RTMP信息发布、HLS切片等功能模块。系统启动时,初始化类,监听端口,对每个访问请求创建线程,专门处理连接操作。
系统监听包含不同类型的请求,如RTMP连接、HTTP API等,通过创建线程处理。
RTMP连接处理中,SRS采用协程而非线程,实现高效并发。创建协程后,进入协程循环处理。
HTTP API连接监听机制与RTMP类似,仅参数不同。
HTTP API回调接口在run_master函数中注册,允许访问服务器参数。
SRS对拉流处理独特,通过ffmpeg工具实现,SRS代码负责简单的系统调用。
系统启动代码结构清晰,从初始化、监听到线程处理,再到回调注册、拉流处理、自服务,各环节紧密衔接。
总结SRS源码分析,不仅展现了代码的高效性和扩展性,还提供了灵活的部署方案,适用于多种直播场景。
nginx-rtmp-module学习
nginx-rtmp-module是nginx中的一个用于音视频点播与直播的模块。在业务需求中,我们需实现两点功能:实时接收音频数据和rtmp客户端从头开始播放接收的音频数据。尽管模块已实现第一点,但第二点仅支持实时播放。为满足需求,需深入学习模块实现过程,基于nginx-rtmp-module进行功能扩展。
nginx为开源软件,其子模块rtmp-module同样开源,源代码可从arut/nginx-rtmp-module下载。理解rtmp客户端和服务端通信机制是关键,nginx-rtmp模块高效实现了这套机制。按照流程图,分为六个步骤完成一次完整的rtmp音视频传输。
rtmp并非仅用于直播,其本质是一种传输多媒体数据的协议。代码结构清晰,学习流程以图示为主,重点在于握手协议。位于ngx_rtmp_handshake.c文件中的握手代码,采用SSL加密,涉及摘要认证过程。客户端发起请求,经过认证后,将字节数据传至服务端。服务端响应,完成认证流程。摘要认证机制保障了客户端与服务端的握手。
总结而言,nginx-rtmp-module提供了一套实现rtmp传输的解决方案,通过深入学习其代码结构和实现细节,可进行功能拓展,满足业务需求。理解其背后的通信机制与协议原理,是掌握该模块的关键。
RTMP推流方案总结
RTMP协议简介,其全称为Real Time Messaging Protocol,是由Adobe Systems公司为Flash播放器与服务器之间音频、视频和数据传输开发的私有协议。RTMP协议像一个容器,用于装载AMF格式的数据或FLV中的视/音频数据,一个连接可通过不同的通道传输多路网络流,通道中的包遵循固定大小的传输规则。更多协议细节请参考《rtmp specification 1.0》。
RTMP服务器的选择有多种开源方案,如Nginx的rtmp插件,用于实时流推送,具体实现可参考另一篇博客。SRS(Simple RTMP Server)是一款国人开发的优秀开源流媒体服务器软件,使用C++开发,适用于直播、录播、视频客服等场景,提供丰富的接入方案和流变换功能,GitHub源码链接为:github.com/ossrs/srs。
crtmpserver是一款由C++语言编写的开源RTMP流媒体服务器,功能相对简单,与Flash Player的兼容性较差,但代码结构良好,适用于学习RTMP协议和服务器端编程。GitHub源码链接为:github.com/shiretu/crtm...。
livego是基于Go语言的RTMP直播服务器,Go语言为服务器性能而生,开发效率高于C/C++。GitHub源码链接为:github.com/gwuhaolin/liv...
基于Go的livego服务器解决了语言级别上的并发问题。node-rtsp-rtmp-server是使用Node.js实现的RTMP服务器,GitHub源码链接为:github.com/iizukanao/nod...
测试时,推荐使用大牛直播提供的推流工具,也可以使用FFmpeg进行推流。
RTMP推流器的选择同样多样,librtmp软件包含一个基本的客户端:rtmpdump,以及提供RTMP协议支持的库。FFmpeg也能实现RTMP推流,内部集成了librtmp,官方给出了muxing.c源代码示例。srs-librtmp是srs提供的一个RTMP库,可以推送H数据,但在Windows环境下存在兼容性问题。
音视频开发相关教程与资料可免费订阅QQ群:,领取学习资源。
FFplay源码分析-nobuffer
在使用 FFplay 播放 RTMP 流时,不开启 nobuffer 选项会导致画面延迟高达7秒左右,而开启此选项后,局域网延迟可降低到毫秒左右。因此,本文将深入探讨nobuffer的实现细节,以及播放端缓存7秒数据的作用。
fflags 的定义在 libavformat/options_table.h 文件中,这是一个通用选项,所有解复用器均包含此选项。在调用 avformat_open_input() 函数时,会将该命令行参数传入,其位置与所有格式参数相同,如在之前的文章《FFplay源码分析》中所述。记得在调试参数中添加-fflags nobuffer。
在 avformat_open_input() 函数内部,fflags 这个 AVOption 会被传递给 AVClass,该类存储了多个 AVOption,而fflags 的索引为5。在 av_opt_set_dict() 函数中,fflags 的值会被应用并清除其他选项。在 avformat_open_input() 执行完毕后,AVFormatContext::flags 的第7位应被置为1,即二进制的 。通过下图可以清晰地看到这个过程。
在 avformat_find_stream_info() 函数内部,如果没有设置nobuffer标记,探测的数据包将被丢入队列。avformat_find_stream_info() 首先读取一段数据包以分析输入流的编码器等信息,为了重用这些数据包,它们会被放入队列中。然而,整个探测过程长达5秒,这意味着 FFplay 大概会读取5秒的数据来分析输入流。若开启nobuffer,则不会重复使用这些探测数据,FFplay 探测完输入流后,会读取新的数据包进行播放。无需缓存,从而降低了延迟。
通过在 ffpaly.c 文件中的 avformat_find_stream_info() 函数前后输出时间,可以发现两者相差5秒,直观展示了nobuffer对于降低延迟的作用。在实时场景下,缓存功能变得多余,它原本是为了分析本地文件,避免重复读取,但在实时场景中反而影响了性能。因此,在实时场景中,关闭缓存更为合适。
补充说明:若在本地虚拟机环境下,不启用缓存也能实现流畅播放。然而,如果 SRS 部署在局域网的另一台机器上,不开启缓存可能导致视频卡顿,原因可能是解码前未能及时读取视频帧,FFplay 不断丢弃视频帧,尤其是当视频比音频慢时,这种情况下缓存功能反而成为瓶颈。