16.live555mediaserver-rtp打包与发送

live555工程代码路径
live555工程在我的gitee下(doc下有思维导图、drawio图):
live555
https://gitee.com/lure_ai/live555/tree/master

章节目录链接
0.前言——章节目录链接与为何要写这个?
https://blog.csdn.net/yhb1206/article/details/127259190?spm=1001.2014.3001.5502

学习demo
live555mediaserver.cpp

学习线索和姿势
1.学习的线索和姿势

网络编程
流媒体的地基是网络编程(socket编程)。
[网络编程学习]-0.学习路线

绘图规则
本文的对象图和思维导图遵守的规则详见:
2.绘图规则

非阻塞服务端网络编程流程
socket创建、bind、listen、select、accept、select、recv/send-close。

rtsp协商流程
options、describe、setup、play、pause、teardown、get parameter、set parameter

rtp打包流程
打开媒体文件、读取一帧媒体数据、rtp打包、rtp发送

本节内容和目标
(1)rtp打包
(2)思维导图绘制
(3)wireshark抓包
(4)对象图

正式开始

server端收到play指令后,就开始进行流媒体数据打rtp包进行发送了。
一般rtp打包思路:
(1) 打开媒体文件
(2)读取一帧数据
(3) rtp打包
(4)rtp发送
(5) 文件读完结束断开rtsp链接或者从头开始不断循环
一般是2-4不断循环直至文件结尾,进入第5条。
因为live555是支持多种媒体文件类型的——264/265/mkv等等文件——那么这些流程是不是可以高度抽象出统一的基类,各媒体类型子类分别实现自身的处理方法?
是否如此还得学习下其流程。

1.打开媒体文件

打开文件的接口都是调用OpenInputFile函数,目前264文件和mkv文件分析,它被ByteStreamFileSource这个类的方法createNew调用,也就是统一到这个类里面了。

mkv:
在这里插入图片描述

264:
在这里插入图片描述

打开的时机:
describe获取sdp时会打开后又关闭。
mkv:
在这里插入图片描述
264:
在这里插入图片描述

还有setup,打开直到结尾才会关闭。

mkv:
在这里插入图片描述
264:
在这里插入图片描述
其他格式文件应该类似,把打开媒体文件都统一到了类ByteStreamFileSource里了。这也算是抽象了吧。

2.读取一帧数据

之前play的流程里有rtp打包的初始流程:
在这里插入图片描述

一如这篇文章所讲:https://blog.csdn.net/Lasuerte/article/details/80929769。
拿来该篇的流程图,画的很好,如下
在这里插入图片描述

稍微整理了下上面一点流程的对象图,如下:
在这里插入图片描述

来到MultiFramedRTPSink::packFrame,如下代码:

void MultiFramedRTPSink::packFrame()
{
  // Get the next frame.

  // First, skip over the space we'll use for any frame-specific header:
  fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();
  fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();
  fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);
  fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;

  // See if we have an overflow frame that was too big for the last pkt
  if (fOutBuf->haveOverflowData()) 
  {
        // Use this frame before reading a new one from the source
        unsigned frameSize = fOutBuf->overflowDataSize();
        struct timeval presentationTime = fOutBuf->overflowPresentationTime();
        unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();
        fOutBuf->useOverflowData();

        afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);
  } 
  else 
  {
        // Normal case: we need to read a new frame from the source
        if (fSource == NULL) return;
        fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),
    			  afterGettingFrame, this, ourHandleClosure, this);
  }
}

关键点是读取一帧数据,上面代码第1次的时候是走else也就是fSource->getNextFrame,以mkv文件为例,fSource是对象H264VideoStreamDiscreteFramer的父类的父类的父类的父类的父类FramedSource的指针。在这也能发现,其实框架一定,只是根据不同的媒体文件创建具体对象返回固定的基类指针或者引用而已。——也就是高度抽象化了。如下mkv为例的图:
在这里插入图片描述

在这里插入图片描述
算了,懒得截图了,可以去doc目录下查看对象图,没有图只有代码很难区分的,因为很多对象都是继承了FramedSource,都是调来调去,如果不知道子类对象你会觉得刚才是FramedSource现在怎么又进入FramedSource的方法了?

比较麻烦,总之,会进入具体的媒体对象子类,去读取数据,解析出一帧数据。
太累,跳过。

3.rtp打包

以mkv文件为例,解析完整一帧数据就会回调H264or5VideoStreamDiscreteFramer::afterGettingFrame,然后H264or5VideoStreamDiscreteFramer::afterGettingFrame1,然后一系列回调,来到MultiFramedRTPSink::afterGettingFrame1,填充RTP数据包,具体如何填充rtp包,待补充。

4.rtp发送

如上面流程图,接着流程会调到MultiFramedRTPSink::sendPacketIfNecessary进行发送,发送详情待补充。

5.循环流转

上面1-4这个流程第一帧是在play的这个响应里的,你想,接下来的那么多帧数据在哪里执行1-4的循环呢?它不可能一直在这个play响应里执行的读取帧它的,它肯定不能一直只处理你这路rtsp流,其他rtsp流都不管了吧?因为学的这个例程是个单线程(就一个main函数——只有主线程),那你能拉多个rtsp流是怎么做到的呢?肯定在rtsp的play阶段发送完一帧数据后,回到主线程,怎么做到的呢?如下
在这里插入图片描述
在MultiFramedRTPSink::sendPacketIfNecessary函数里,发送完rtp后,其代码如上(截取部分),根据下一帧发送的间隔,计算出下一帧发送的需要延时的时间,然后加入定时器列表,等待主线程去调。比如视频帧如果是30fps,那么计算出来的延时uSecondsToGo差不多就是33333us,然后加入定时器列表。(前面讲过,参见 setup
https://blog.csdn.net/yhb1206/article/details/128696468。)

等到时间到了,主线程会执行超时函数MultiFramedRTPSink::sendNext,如下代码,它会继续调用MultiFramedRTPSink::buildAndSendPacket,继续读取一帧,然后发送出去。

void MultiFramedRTPSink::sendNext(void* firstArg) 
{
    MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;
    sink->buildAndSendPacket(False);
}

于是,这就实现了回到主线程,不耽误其他rtsp流,并实现1-4的循环,之前的那个博客把流程图画出来了,但是这个机制没有说。

6.文件读完

当第5节循环到最后,媒体数据读完了,这个时候要断开rtsp链接和关闭媒体文件了。关闭媒体文件是CloseInputFile这个函数,它也是被集成到类ByteStreamFileSource的析构函数里面了。也就是整体对媒体文件的打开与关闭就继承到类ByteStreamFileSource里面了,抽象为这个类了。
创建这个类就会打开流媒体文件,销毁这个类就是关闭流媒体文件。
在这里插入图片描述

断开rtsp链接自然会销毁对象ByteStreamFileSource,销毁ByteStreamFileSource对象自然关闭媒体文件。

8.补充说明

上面1-6的媒体数据rtp发送流程是一个媒体流,什么意思?这涉及到live555的设计,它把一个rtsp会话对应一个ServerMediaSession类,而这路rtsp链接里有什么媒体流则由ServerMediaSubsession类来表示。比如一路rtsp链接就会创建基于ServerMediaSession类的对象,然后媒体文件sdp里有几个m媒体描述信息(是从媒体文件里解析出来的)就会创建几个基于ServerMediaSubsession类的对象。
如下图:
在这里插入图片描述
当然,一个rtsp链接传输什么样的流可以通过sdp进行协商,没有限制,可以多路流,比如多路视频流或一路视频流或多路音频流或一路音频流,个数不限,但是得在sdp协商好。

另外上面1-6的流程是一个rtsp链接中的一个媒体流ServerMediaSubsession的流程,各个媒体流是各自独立有这样的流程——ServerMediaSubsession对象不一样,一系列的也都不一样,都会加入定时器列表在主线程中进行rtp打包推送的。以mkv为例,一路视频流,一路音频流,那么这个有2个ServerMediaSubsession对象,在play的时候第一帧都是在play里打包发送出去的,然后都是分别创建了独立的定时器加入定时器列表,比如视频是30fps,音频是25fps,那么它们的延时分别是33333us和40000us,时间到了就执行各自的超时函数进行发送音视频数据。独立发送。

问题
问题1:从setup开始有65s的定时了,65s内不及时撤销这个定时任务,会删除这个rtsp会话的,play会撤销并重新加入这个定时任务,那play以后进行rtp发送任务时,在哪撤销这个定时任务的呢?
参见下一篇16.live555mediaserver-保活机制

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值