1.SRS(simple-rtmp-server)流媒体服务器源码分析--HLS切片
2.FFplay源码分析-nobuffer
3.RTMP 视频数据封装
4.分析流媒体服务器源码:Rtmp发布流程的播放播放SRS解析
5.RTMP推流方案总结
6.SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
SRS(simple-rtmp-server)流媒体服务器源码分析--HLS切片
SRS流媒体服务器支持rtmp协议,同时也支持HLS协议,源码满足了PC和移动端直播需求。器源HLS协议的播放播放关键文件包括.m3u8和.ts文件,其中.m3u8文件包含播放信息,源码ts文件则包含音视频数据。器源在线客服 源码SRS接收H/AAC编码的播放播放音视频数据,进行切片处理成.m3u8和.ts文件,源码通常存储在内存中。器源这些文件通过nginx进行分发。播放播放
HLS切片处理遵循TS协议,源码将音视频数据按照TS协议规则分割成TS包,器源其中PAT表(PID为0x0)和PMT表(PID为0x)作为首包,播放播放视频数据PID为0x,源码音频数据PID为0x。器源这样,每一段音视频数据就被封装在一个TS包中。
在SRS源码中,HLS处理流程涉及获取SPS和PPS信息、检测视频编码格式、进行RTMP抖动矫正以及实际的HLS切片处理。在on_video函数中,重点进行的工作包括获取H编码的SPS和PPS信息,检查视频编码格式必须为H,执行RTMP抖动矫正,以及关键的HLS切片处理。
在HLS切片处理部分,主要完成两件事:首次或ts文件时间溢出时,调用reap_segment函数进行.m3u8和.ts文件的管理,包括创建、打开、关闭等操作;其他时间则进入flush_video函数,负责ts流编码和.ts文件的写入。编码过程中,会根据音视频类型获取不同PID,执行TS编码,并将PAT帧、PMT帧和音视频数据编码成TS包,写入文件。标签网站源码
总结,TS编码实质是按照TS协议重新整理数据,保持H/AAC的编码格式。TS切片遵循TS协议要求的字段长度对音视频数据进行分包处理。TS文件作为TS包的存储位置,其生成与HLS协议和TS协议之间有密切关系。
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() 首先读取一段数据包以分析输入流的编码器等信息,为了重用这些数据包,它们会被放入队列中。然而,zmq源码解析整个探测过程长达5秒,这意味着 FFplay 大概会读取5秒的数据来分析输入流。若开启nobuffer,则不会重复使用这些探测数据,FFplay 探测完输入流后,会读取新的数据包进行播放。无需缓存,从而降低了延迟。
通过在 ffpaly.c 文件中的 avformat_find_stream_info() 函数前后输出时间,可以发现两者相差5秒,直观展示了nobuffer对于降低延迟的作用。在实时场景下,缓存功能变得多余,它原本是为了分析本地文件,避免重复读取,但在实时场景中反而影响了性能。因此,在实时场景中,关闭缓存更为合适。
补充说明:若在本地虚拟机环境下,不启用缓存也能实现流畅播放。然而,如果 SRS 部署在局域网的另一台机器上,不开启缓存可能导致视频卡顿,原因可能是解码前未能及时读取视频帧,FFplay 不断丢弃视频帧,尤其是当视频比音频慢时,这种情况下缓存功能反而成为瓶颈。
RTMP 视频数据封装
RTMP协议,是一个基于TCP的实时消息传输协议,由Adobe Systems公司开发,用于Flash播放器和服务器之间的音频、视频和数据传输。在国内,RTMP广泛应用于直播领域,其默认端口为,与HTTP的默认端口不同。通过阅读Adobe的肥城网站源码协议规范并建立与服务器的TCP通信,按照协议格式生成和解析数据,即可使用RTMP进行直播操作,或者使用实现了RTMP协议的开源库来实现这一过程。
RTMPDump是一个开源工具包,专门用于处理RTMP流媒体。它能独立运行进行RTMP通信,也可以通过FFmpeg接口集成到FFmpeg中使用。RTMPDump的源代码可以从rtmpdump.mplayerhq.hu/d...下载。为了在Android中直接调用RTMPDump进行RTMP通信,需要在JNI层进行交叉编译。RTMPDump的源代码结构包括Makefile和一系列.c源文件。编译过程需要通过CMakeLists.txt进行,将其放入AS中,复制librtmp到src/main/cpp/librtmp,并编写CMakeLists.txt,导入app/CMakeLists.txt。
RTMP视频流格式与FLV很相似,理解FLV的格式文档可以帮助我们构建RTMP视频数据。RTMP中的数据由FLV的TAG中的数据区组成。在FLV中,第一个字节表示数据类型,如0x表示视频,数据大小为字节,时间戳和流ID分别由后续的字节表示,最后的字节表示数据块的总大小。在AVCVIDEOPACKET中,数据结构与类型决定了后续数据的内容,包括版本、合成时间、SPS与PPS等关键信息。在构建AVC序列头和非AVC序列头时,需要注意数据的类型区分。
H.码流在网络中传输时以NALU(Network Abstract Layer Unit)的形式进行。NALU是NAL(Network Abstract Layer)单元,是H.编码标准中的一个概念。编码后的H.数据被分割为多个NAL单元,每个单元包含了视频帧的一部分信息。在将数据封装到RTMP包中时,鸿蒙源码自制需要去除分隔符,然后将NAL数据加入到RTMPPacket中。完整的封包代码需要将这些步骤结合在一起实现。
综上所述,理解RTMP协议、RTMPDump的使用以及如何在不同环境下构建RTMP视频数据和封装H.数据是进行实时流媒体传输的关键步骤。正确地使用这些工具和技术,能够有效地实现直播和视频流的传输。
分析流媒体服务器源码:Rtmp发布流程的SRS解析
Rtmp发布流程在SRS服务器中主要通过单线程多协程模型来实现,以简化线程管理和数据同步。以下是关键步骤的解析:
SRS基于state-threads协程库工作,每个协程在单线程内独立执行,无需考虑线程安全问题。程序启动后,通过SrsStreamListener监听并处理TCP连接,创建SrsTcpListener和SrsReusableThread进行并发处理。
当接收到客户端连接时,会根据连接类型创建不同的SrsConnection,如RtmpConn。SrsRtmpConnFMLEPublish负责处理推流至服务器,会进入publishing函数,其中创建SrsPublishRecvThread协程,接收和处理客户端的消息。
消息处理中,视频数据会经过缓存H序列头、HLS分发和消费者分发等步骤。每个消费者有自己的SrsMessageQueue,队列大小由配置文件中的"queue_length"设置,队列满时旧消息会被丢弃,但关键的序列头不会被删除,避免影响客户端解码。
总结来说,SRS的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群:,领取学习资源。
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(完整帧是必需的)。
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信息非常关键,如果不告知解码器,根本无法播放视频。我写了一段代码,虽然技术有限,但希望能帮助到您。
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.原始码流由一个一个的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。如果nal_unit_type == 0x,则是idr帧,需要加上PPS和SPS信息(即一个idr通常包含3个startcode,SPS一个PPS一个idr帧数据一个)。
以下是完整代码:
rtmpTo.h
rtmpTo.cpp
main.cpp
原文链接:blog.csdn.net/qq_...