1 发送数据帧
1.1 MP3SreamState
MP3StreamState直接读MP3文件。MP3FrameParams保存帧数据。
- 成员fFid是文件指针。
- 成员fCurrentFrame存放从文件读出的帧数据。
- 成员函数assignStream()指定MP3文件作为媒体源。
成员函数findNextHeader()和readFrame()配合使用,可以读取一帧数据。
- findNextHeader()从文件中找到下一帧的头,读到成员fCurrentFrame中,这是一个MP3FrameParams实例。
- readFrame()从成员fCurrentFrame得到帧。
1.2 MediaSource
MediaSource定义媒体源访问接口。(从命名看是这样,但实际上它没定义什么。)
FramedSource定义以帧为基础的媒体源接口。
- 成员函数getNextFrame()得到下一个帧。
- 调用getNextFrame时需要指定一个回调函数,在得到帧之后会调用它。为了调用方便,getNextFrame()把这个回调函数保存在成员fAfterGettingFunc中。
FramedFileSource是基于文件的FramedSource。
- 除了定义成员fFid保存文件指针,没有加其他东西。
MP3FileSource是MP3文件的媒体源的访问接口。
- 成员fStreamState是一个MP3StreamState实例。MP3FileSource将大部分工作委托给它。
- 成员函数assignStream()绑定一个MP3文件,成员函数initializeStream()初始化媒体源,也就是初始化成员fStreamState。
- 成员函数seekWithinFile()在文件中定位读位置。
用MP3FileSource::createNew()创建MP3FileSource实例。
- 调用OpenInputFile()打开MP3文件,文件名是createNew()的参数。它内部调用fopen()。
- 创建MP3FileSource实例,其中包括创建MP3StreamState实例。
- 用打开文件的指针调用assignStream(),给MP3StreamState指定媒体源的文件指针。
- 调用intializeStream()。
- 调用MP3StreamState::findNextHeader()尝试读一个帧。
- 调用MP3StreamState::checkForXingHeader()检查读到的帧格式是否正确。
以MP3FileSource为例说明成员函数getNextFrame(),它负责从媒体源中读取下一帧。
- 调用MP3FileSource::doGetNextFrame()。
- 调用MP3StreamState::findNextHeader(),从文件中读帧数据到成员fCurrentFrame。
- 调用MP3StreamState::readFrame(),将成员fCurrentFrame中的数据复制出来。
- 加入一个延时任务,延时调用FramedSource::afterGetting()。
在延时任务FramedSource::afterGetting()中,
- 调用成员fAfterGetttingFunc指定的回调函数。这是调用getNextFrame()时指定的回调函数。这样异步通知getNextFrame()的调用者,“读取已经完成”。
1.3 MediaSink
1.3.1 MediaSink接口
MediaSource是数据源,MediaSink则访问这个数据源,对外提供数据。
MediaSink定义了访问接口。
- startPlaying()开始播放数据。它调用虚拟函数continuePlaying(),派生类可以定制后者。
- stopPlaying()停止播放。
RTPSink在MediaSink基础上,
- 增加成员fRTPInterface,以便通过网络发送接收数据。
- 构造RTPSink时,需要提供GroupSock实例作为参数。这个实例是UDP目标,它用于构造成员fRTPInterface。
- addStreamSocket()、removeStreamSocket()用于增加和删除TCP目标。
1.3.2 MultiFrameRTPSink
MultiFramedRTPSink实现了continuePlaying()和stopPlaying()。
- MediaSink定义的成员fSource是FramedSource实例。
- 构造的帧存放在成员fOutBuf中。
以MultiFrameRTPSink为例,说明成员函数startPlaying()。它开始播放,也就是解析帧并发送给客户端。
-
调用sourceIsCompatibleWithUs()检查兼容,也就是支持相关函数调用。
-
调用MutliFrameRTPSink::continuePlaying()。它调用buildAndSendPacket()解析并发送帧。
-
在buildAndSendPacket()中,构造OutPacketBuffer实例,这是要发送的帧。
- 先调用OutPacketBuffer::enqueueWord()构造一部分,再调用packFrame()继续构造其他部分。
-
在packFrame()中,调用FramedSource::getNextFrame()继续构造,这里指定回调函数MultiFramedRTPSink::afterGettingFrame()。
- 前面说明过getNextFrame()。这个过程是异步进行的,构造全部完成后,在一个被延迟的任务中,调用MultiFramedRTPSink::afterGettingFrame()。
在MultiFramedRTPSink::afterGetttingFrame()中,调用sendPacketIfNecessary()。后者调用RTPInterface::sendPacket()将帧发送出去。
1.3.3 MPEG1or2AudioRTPSink
MultiFramedRTPSink也定义一个虚拟函数doSpecialFramedHandling()。这个函数在afterGettingFrame1()中被调用,以便它的派生类,比如MPEG1or2AudioRTPSink,对构造数据包的过程做更多定制。
1.4 另一种MediaSource
1.4.1 FramedFilter
FramedFilter是另一种MediaSource,更准确地说,是另一种FramedSource。
- 成员fInputSource指向另外一个FramedSource实例,比如MP3FileSource的实例。它自身不提供数据源,只是对fInputSource数据源做一些变换,再重新提供出去。这个概念类似于Decorate设计模式。(当然不是标准的Decorate,毕竟接口有变动)
1.4.2 Segment和SegmentQueue
Segment用于保存MP3帧的数据。
- 成员buf保存整个帧,包括帧头和帧数据部分。
- frameSize、descriptorSize、sizeInfoSize和aduSize是从buf中提取的元数据。
SegmentQueue是一个Segment的容器。
- 成员s是一个Segment数组。这里当做环形缓存使用。
- fNextFreeIndex是空闲元素的起始索引,fHeadIndex是已使用元素的起始位置。fTotalDataSize是所有帧数据(不包括帧头)的大小之和。
- 成员函数enqueueNewSegment()从FramedSource得到segment,并推入环形缓存s[]。headSegment()得到下一个segment。dequeue()则将segment从缓存剔除。
- fUsingSource是依赖的FramedSource实例。
成员函数enqueueNewSegment()从FramedSource实例中读取帧,写入成员s的下一个空闲元素。
- 这个FramedSource实例是函数的参数。将它保存在成员fUsingSource中,是为了便于引用。
- 调用nextFreeSegment()得到下一个空闲的Segment元素。
- 调用FramedSource::getNextFrame()读下一帧,到刚得到的Segment元素中。指定SegmentQueue::sqAfterGettingSegment()为完成后的回调函数。
读帧完成后,sqAfterGettingSegment()函数被调用。
- 调用nextFreeSegment()得到刚刚写入的Segment元素。
- 调用sqAfterGettingCommon(),解析帧,得到帧的元信息,保存在Segment的frameSize等成员中。除此之外,还调用nextIndex()得到下一个空闲位置,将fNextFreeIndex指向它。
1.4.3 ADUFromMP3Source和MP3FromADUSource
MP3FromADUSource和MP3FromADUSource是FramedFilter的两个例子。
ADUFromMP3Source::createNew()创建ADUFromMP3Source实例。
- 其中创建SegmentQueue实例,也就是它的成员fSegments。
ADUFromMP3Source定制了虚拟成员函数doGetNextFrame()。
- 调用SegmentQueue::enqueueNewSegment(),从成员fInputSource中读出帧,暂存在成员fSegments中。
- 调用doGetNextFrame1()。
- 调用memmove(),从fSegments中读出帧,然后调用SegmentQueue::dequeue(),并从fSegments从移除它。
网友评论