live555开发笔记(三):live555创建RTSP服务器源码分析,创建h264文件rtsp ...

打印 上一主题 下一主题

主题 1677|帖子 1677|积分 5031

若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/147879917
长沙红胖子Qt(长沙创微智科)博文大全:开发技能集合(包含Qt实用技能、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…
FFmpeg、SDL和流媒体开发专栏

上一篇:《live555开发笔记(二):live555创建RTSP服务器源码分析,创建rtsp服务器的根本流程总结》
下一篇:敬请期待…

媒介

  对于live555的rtsp服务器有了而根本的了解之后,进一步对示例源码进行分析,认识整个h264文件流媒体的开发步骤。

Demo

  

  

  播放本地文件,多路播放的时间,总是以第一个文件进度为准,以是当前这个Demo是同步播放的。
这对于摄像头收罗视频及时播放来说,这个是满足这个功能的。

根本概念

Source、Sink、Filter



  • Source:作为数据流的出发点,负责生成或获取原始数据。例如:ByteStreamFileSource:从文件读取原始字节省6,H264VideoStreamFramer:剖析H.264视频流并生成帧数据
  • Filter:在数据流从Souce流到Sink的过程中能够设置Filter,用于过滤或做进一步加工。例如:H264or5Fragmenter:将视频帧分片以顺应RTP包大小;解码器Filter:将编码数据解码为原始帧。
  • Sink:作为数据流的尽头,负责消费或转发数据。
      整个LiveMedia中,数据都是从Souce,经过一个或多个Filter。终于流向Sink。在server中数据流是从文件或装备流向网络,而在client数据流是从网络流向文件或屏幕。
      MediaSouce是全部Souce的基类,MediaSink是全部Sink的基类。
      从类数量和代码规模能够看到。LiveMedia类是整个LIVE555的焦点,其内部包括数十个操纵具体编码和封装格式的类。LiveMedia界说的各种Souce均是从文件读取,假设想实现从装备获得及时流的传输,能够界说自己的Souce。
ClientSession、ClientConnection



  • ClientSession:对于每一个连接到server的client。server会为其创建一个ClientSession对象,保存该client的socket、ip地址等。同一时间在该client中界说了各种响应函数用以处理和回应client的各种哀求。
  • ClientConnection:用于处理一些与正常播放无关的下令。如下令未找到、下令不支持或媒体文件未找到等。在ClientConnection处理DESCRIBE下令时会创建ClientSession对象。其它下令在ClientSession中处理。
MediaSession、MediaSubsession、Track

  LIVE555利用MediaSession管理一个包括音视频的媒体文件。每一个MediaSession利用文件名称唯一标识。利用SubSession管理MediaSession中的一个音频流或视频流,音频或视频均为一个媒体文件里的媒体流,因此一个MediaSession能够有多个MediaSubsession,可单独管理音频流、视频流,并为每一个媒体流分配一个TrackID,如视频流分配为Track1,音频流分配为Track2,此后client必须在URL指定要为谁人Track发送SETUP下令,因此我们能够觉得MediaSubsession代表Server端媒体文件的一个Track,也即相应一个媒体流。MediaSession代表Server端一个媒体文件。
  对于既包括音频又包括视频的媒体文件,MediaSession内包括两个MediaSubsession。但MediaSession和MediaSubsession仅代表静态信息。若多个client哀求同一个文件,server仅会创建一个MediaSession。各个client公用。为了区分各个MediaSession的状态又界说了StreamState类,用来管理每一个媒体流的状态。在MediaSubsession中完毕了Souce和Sink连接。Souce对指针象会被设置进sink。在Sink须要数据时,能够通过调用Souce的GetNextFrame来获得。
  LIVE555中大量利用简单工厂模式,每一个子类均有一个CreateNew静态成员。该子类的构造函数被设置为Protected,因此在外部不能直接通过new来构造。同一时间。每一个类的构造函数的參数中均有一个指向UsageEnvironment的指针,从而能够输出错误信息和将自己增加调度。
HashTable

  IVE555内部实现了一个简单哈希表类BasicHashTable。在LIVE555中。有非常多地方须要用到该哈希表类。如:媒体文件名称与MediaSession的映射,SessionID与ClientSession的映射,UserName和Password的映射等。
SDP

  SDP是Session Description Protocol的缩写。是一个用来描写叙述多媒领会话的应用层协议。它是基于文本的,用于会话建立过程中的媒体类型和编码方案的协商等。

rtp与rtcp与rtsp服务器

RTP(及时传输协议)

  负责传输及时音视频数据流,基于UDP/IP协议实现低耽误传输。支持时间戳、序列号等机制,确保数据包的时序性和同步性。不包管传输可靠性,需结合RTCP进行质量监控。
RTCP(及时传输控制协议)

  作为RTP的辅助协议,用于监控传输质量、反馈统计信息(如丢包率、耽误、抖动)。功能实现:通过发送SR(发送者报告)和RR(吸收者报告)反馈网络状态。支持带宽动态调解(如TMMBR/TMMBN)和流同步。
RTSP(及时流协议)服务器

  作为流媒领会话的控制中心,负责客户端与服务器的交互(如播放、停息、停止等指令)。


  • 会话管理:通过SETUP、PLAY、TEARDOWN等下令控制媒体流传输的生命周期。
  • 协议和谐:与RTP/RTCP协同工作,RTSP界说控制逻辑,RTP传输数据,RTCP监控质量。
  • 多流支持:可同时管理音视频等多路流,并通过SDP协议协商编解码参数。

关键类先容

H264VideoRTPSink:H264视频数据焦点组件

  H264VideoRTPSink是H.264视频流通过RTP/RTSP协议实现及时传输的关键模块,负责协议封装、数据分片和网络适配,确保视频流在及时场景下的高效性和兼容性。
  

  H264VideoRTPSink是Live555 流媒体框架中用于封装和传输H.264视频数据的焦点组件,其作用主要包含以下方面:


  • RTP数据封装:H264VideoRTPSink 将 H.264 视频数据按 RTP 协议规范封装成网络传输包。具体包括:添加 RTP 包头信息(如时间戳、序列号、负载类型等);根据 H.264 的 NALU(网络抽象层单元)布局对视频数据进行分片或重组,确保数据顺应网络传输的最大传输单元(MTU)限制。
  • 分片处理(Fragmentation):当单个 H.264 帧超过 MTU 限制时,H264VideoRTPSink 会将其拆分为多个 RTP 包(例如利用 FU-A 分片模式),并在包头中标记分片的起始、中间和结束位置。
  • 与 RTSP 协议协同工作:在RTSP会话中,H264VideoRTPSink作为数据传输的尽头(Sink),与MediaSource(数据源)共同,完成从数据读取到RTP封装的完整流程。在客户端发送PLAY哀求后,服务器通过H264VideoRTPSink将封装后的RTP流推送至网络。缓冲区管理处理大数据量H.264视频时,H264VideoRTPSink需依赖动态调解的缓冲区(如OutPacketBuffer::maxSize),防止因数据包过大导致的传输失败。
RTCPInstance:RTCP通讯类

  在Live555框架中,RTCPInstance与RTPSink、RTPInterface等类协作,共同实现完整的RTP/RTCP流媒体传输功能。其设计独立于其他高层协议模块,仅依赖基础网络组件,具备较好的封装性。
  

  RTCPInstance是Live555框架中封装RTCP协议通讯的焦点类:


  • RTCP协议通讯的封装与实现:负责RTCP数据包的收发处理,支持通过RTPInterface实现基于UDP或TCP的传输。吸收到的RTCP报文(如SR、RR、BYE等)在incomingReportHandler等回调函数中处理,实现网络状态反馈和会话控制。
  • 网络状态监测与统计:统计RTP包的收发情况,收集丢包率、耽误、抖动等指标,为流量控制提供数据支持。依赖RTPSink类获取发送端统计信息,实现与RTP流的关联。
  • 反馈与动态调解机制:通过发送SR(发送者报告)和RR(吸收者报告)及时反馈网络质量,触发发送速率调解或丢包重传。支持带宽控制机制(如TMMBR/TMMBN),根据网络状态动态调解媒体流传输参数。
  • 会话管理与流同步:处理BYE报文实现参与者退出通知,维护会话成员状态。通过SDES报文传递参与者形貌信息,辅助多流同步(如音视频同步)。
ByteStreamFileSource:基础数据源组件

  ByteStreamFileSource是Live555中处理文件型H.264流的焦点入口组件,承担数据读取与基础分块功能,并为上层剖析、封装模块提供标准化输入接口。
  

  ByteStreamFileSource在Live555 流媒体框架中作为基础数据源组件,焦点作用如下:


  • 原始字节省读取:负责从本地文件(如 H.264 裸流文件)中读取未封装的原始字节数据,并以分块(Framed)形式输出,供后续剖析模块处理。
  • 数据链的出发点:在典范的 H.264 传输链路(如 H264VideoFileServerMediaSubsession中)中,其作为初始节点启动数据流,后续连接剖析器(如H264VideoStreamParser)和分帧器(H264VideoStreamFramer),形成完整处理链路:ByteStreamFileSource → H264VideoStreamParser → H264VideoStreamFramer → … → RTP 封装。
  • 可扩展性支持:虽然默认实现为文件读取,但其继承自FramedSource基类,用户可通过自界说派生类(如及时收罗或编码的数据源)替代该组件,实现灵活的数据输入适配。
  • 关键参数配置:支持通过fileSize属性获取文件大小信息,便于预估传输带宽需求。在动态服务器场景中,需注意其与缓冲区大小(如OutPacketBuffer::maxSize)的协同配置,制止大帧溢出问题。
FrameSource:帧源抽象类

  FrameSource是Live555中实现按帧输入媒体数据的基础组件,为RTSP/RTP 传输提供标准化的数据源接口,支持多格式媒体流的灵活扩展。
  

  FrameSource 在 Live555 流媒体框架中作为数据源的焦点抽象类,其作用可归纳如下:


  • 基础数据源抽象:FrameSource 是负责按帧(Framed)提供流媒体数据的基类,界说了同一的帧数据读取接口。其子类需实现 doGetNextFrame 方法,用于逐帧获取原始媒体数据(如视频帧或音频块)。
  • RTP数据流出发点:在RTSP/RTP传输链路中,FrameSource 作为数据生产者,将媒体数据以帧为单位传递给下游处理模块(如剖析器、封装器)。例如,H264VideoStreamFramer 继承自 FrameSource,负责将裸 H.264 码流分帧后输出。
  • 接口标准化:FrameSource 通过纯虚函数强制子类实现关键操纵,包括:帧数据异步获取机制(通过 afterGetting 回调触发数据传输);帧数据的分块与缓冲区管理。
  • 与下游组件协同:FrameSource与MediaSink类(如H264VideoRTPSink)形成数据链路:MediaSink通过fSource成员绑定FrameSource,驱动数据拉取与封装流程;在RTSP会话中,FrameSource的数据最终被封装为RTP包并发送至客户端。
H264VideoStreamFramer:H264视频流帧器

  H264VideoStreamFramer在RTSP流媒体服务中通常由H264VideoFileServerMediaSubsession创建,是H.264及时流传输的关键剖析层,承担了从原始字节省到布局化视频数据的转换任务。
  

  H264VideoStreamFramer在Live555框架中作为视频流剖析的焦点组件,主要承担H.264根本流(ES)的剖析与重构,其焦点功能如下:


  • H.264原始流剖析‌:从ByteStreamFileSource等数据源读取原始字节省,通过内置的H264VideoStreamParser剖析器识别NALU(网络抽象层单元)边界,将连续字节省分割为独立NALU单元‌25处理H.264 Annex B格式的起始码(如0x00000001),实现NALU的精准定位‌56
  • 参数集处理‌:提取并缓存SPS(序列参数集)和PPS(图像参数集),用于后续解码器初始化‌,在流启动时优先发送SPS/PPS,确保解码端精确初始化‌。
  • 分帧逻辑控制‌:区分VCL(视频编码层)和非VCL NALU,根据帧类型(如IDR帧、非IDR帧)构造数据输出‌56;处理分片单元(Slice)的关联性,确保帧完整性‌
  • 时间戳生成机制:基于视频帧率或外部输入时钟盘算RTP时间戳,实现与音视频同步;处理B帧/P帧的表现时间戳(PTS)与解码时间戳(DTS)关系。
  • 与上下游组件协作‌:作为FramedSource的子类,向上连接ByteStreamFileSource获取原始数据,向下对接H264FUAFragmenter完成RTP分片‌;通过事件驱动模子触发数据读取,形成Source → Parser → Framer → Fragmenter → RTPSink的完整处理链路‌。
      H264VideoStreamFramer把自己的缓冲(实在是sink的)传给H264VideoStreamParser,每当H264VideoStreamFramer要获取一个NALU时,就跟H264VideoStreamParser要,而H264VideoStreamParser就从ByteStreamFileSource读一坨数据,然后进行分析,假如取得了一个NALU,就传给H264VideoStreamFramer。

Live555流媒体服务实现根本流程

步骤一:创建任务调度管理器

  

步骤二:创建rtp和rtcp

  在差别平台利用的socketaddr_storage类型有区别,有些事socketaddr_in,主要是groupsock的头文件构造函数类型的区别,判定是live555各种版本有区别。
  

步骤三:创建H264VideoRTPSink

  

步骤四:创建RTCPInstance

  

步骤五:RTSP服务器

  

步骤六:创建ServerMediaSession实例

  

步骤七:创建subsession实例

  

步骤八:开始播放

  

步骤九:服务器运行

  


Demo区别

  

  没有启动服务器http端口监听,而是直接play。

整理后的中文解释代码

  1. /*
  2. 为了使此应用程序正常工作,H.264 Elementary Stream视频文件*必须*包含SPS和PPS NAL单元,
  3. 最好在文件开头或附近。这些SPS和PPS NAL单元用于指定在输出流的SDP描述中设置的“配置”信息
  4. (由此应用程序内置的RTSP服务器设置)。另请注意,与其他一些“*Streamer”演示应用程序不同,
  5.   生成的流只能使用RTSP客户端(如“openRTSP”)接收
  6. */
  7. #include <liveMedia.hh>
  8. #include <BasicUsageEnvironment.hh>
  9. #include <GroupsockHelper.hh>
  10. UsageEnvironment* env;
  11. //char const* inputFileName = "test.264";
  12. char const* inputFileName = "T:/test/front/20250311_123244_0.h264";
  13. H264VideoStreamFramer* videoSource;
  14. RTPSink* videoSink;
  15. void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms)
  16. {
  17.     if(rtspServer == NULL || sms == NULL)
  18.     {
  19.         return;
  20.     }
  21.     UsageEnvironment& env = rtspServer->envir();
  22.     env << "Play this stream using the URL ";
  23.     if(weHaveAnIPv4Address(env))
  24.     {
  25.         char* url = rtspServer->ipv4rtspURL(sms);
  26.         env << """ << url << """;
  27.         delete[] url;
  28.         if (weHaveAnIPv6Address(env))
  29.         {
  30.             env << " or ";
  31.         }
  32.     }
  33.     if(weHaveAnIPv6Address(env))
  34.     {
  35.         char* url = rtspServer->ipv6rtspURL(sms);
  36.         env << """ << url << """;
  37.         delete[] url;
  38.     }
  39.     env << "\n";
  40. }
  41. void play(); // forward
  42. int main(int argc, char** argv)
  43. {
  44.     // 步骤一:创建任务调度器和运行信息环境
  45.     TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  46.     env = BasicUsageEnvironment::createNew(*scheduler);
  47.     // 步骤二:创建groupsocks用于RTP和RTCP
  48.     struct sockaddr_storage destinationAddress;
  49.     destinationAddress.ss_family = AF_INET;
  50.     ((struct sockaddr_in&)destinationAddress).sin_addr.s_addr = chooseRandomIPv4SSMAddress(*env);
  51.     // 这是一个多播地址。如果希望使用单播进行流式传输,那么应该使用“testOnDemand RTSPServer”测试程序(而不是此测试程序)作为模型。
  52.     const unsigned short rtpPortNum = 18888;
  53.     const unsigned short rtcpPortNum = rtpPortNum+1;
  54.     const unsigned char ttl = 255;
  55.     const Port rtpPort(rtpPortNum);
  56.     const Port rtcpPort(rtcpPortNum);
  57.     Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
  58.     rtpGroupsock.multicastSendOnly();
  59.     Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
  60.     rtcpGroupsock.multicastSendOnly(); //
  61.     // 步骤三:从RTP“groupsock”创建“H264视频RTP”接收器
  62.     OutPacketBuffer::maxSize = 100000;
  63.     videoSink = H264VideoRTPSink::createNew(*env, &rtpGroupsock, 96);
  64.     // 步骤四:为此RTP接收器创建(并启动)一个“RTCP实例” kbps为单位;RTCP b/w份额
  65.     const unsigned estimatedSessionBandwidth = 500;
  66.     const unsigned maxCNAMElen = 100;
  67.     unsigned char CNAME[maxCNAMElen+1];
  68.     gethostname((char*)CNAME, maxCNAMElen);
  69.     CNAME[maxCNAMElen] = '\0'; // just in case
  70.     RTCPInstance* rtcp = RTCPInstance::createNew(*env,
  71.                                                  &rtcpGroupsock,
  72.                                                  estimatedSessionBandwidth,
  73.                                                  CNAME,
  74.                                                  videoSink,
  75.                                                  NULL,    // 代表服务器
  76.                                                  True);   // 代表SSM源
  77.     // 步骤五:这将自动启动RTCP运行
  78.     RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
  79.     if (rtspServer == NULL)
  80.     {
  81.         *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
  82.         exit(1);
  83.     }
  84.     // 步骤六:创建ServerMediaSession
  85.     ServerMediaSession* sms = ServerMediaSession::createNew(*env,
  86.                                                             "testStream",
  87.                                                             inputFileName,
  88.                                                             "Session streamed by "testH264VideoStreamer"",
  89.                                                             True);
  90.     // 步骤七:创建subsession
  91.     sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
  92.     rtspServer->addServerMediaSession(sms);
  93.     announceURL(rtspServer, sms);
  94.     // 开始流播放:
  95.     *env << "Beginning streaming...\n";
  96.     // 这个开始播放函数调用不调用区别:
  97.     // 1.不调用时,有客户端输入,无法播放;必须调用play,进入则会开始调用播放,从头开始播放
  98.     // 2.后进入的客户端播放进度会主动同步首个连接的客户端播放进度
  99.     play();
  100.     // 服务器阻塞进入服务循环
  101.     env->taskScheduler().doEventLoop(); // does not return
  102.     return 0;
  103. }
  104. void afterPlaying(void* clientData)
  105. {
  106.     *env << "...done reading from file\n";
  107.     // 停止播放
  108.     videoSink->stopPlaying();
  109.     // 请注意,这也会关闭此源读取的输入文件。这是静态方法,可以直接关闭
  110.     Medium::close(videoSource);
  111.     // 再次开始播放
  112.     play();
  113. }
  114. void play()
  115. {
  116.     // 将输入文件作为“字节流文件源”打开
  117.     ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(*env, inputFileName);
  118.     if(fileSource == NULL)
  119.     {
  120.         *env << "Unable to open file "" << inputFileName
  121.              << "" as a byte-stream file source\n";
  122.         exit(1);
  123.     }
  124.     FramedSource* videoES = fileSource;
  125.     // 为视频基本流创建帧器
  126.     videoSource = H264VideoStreamFramer::createNew(*env, videoES);
  127.     // 最后,开始播放
  128.     *env << "Beginning to read from file...\n";
  129.     videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
  130. }
复制代码

工程模板v1.2.0

  


上一篇:《live555开发笔记(二):live555创建RTSP服务器源码分析,创建rtsp服务器的根本流程总结》
下一篇:敬请期待…

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/147879917

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

锦通

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表