详细的帮助文档见www.live.com \ livemedia
1 。总体框架Live的网站上有doxgen产生的帮助文档以及各个类之间的相互关系,这里不再螯述,不过这里要提醒的是,live的库代码可以同时供服务器和客户端使用,因此如果只是开发单个程序或者需要把服务器和客户端的程序分割清楚的话,最好先将代码剥离,这里可以参考live的参考例子openRTSP以及TestOnDemandServer
2 。客户端框架
这里给出了openRTSP的流程,同时最后给出了接收packet循环中的操作顺序,最后将会叙述奖励客户端需要建立些什么。
Ø
“| |”目前,这个项目是依赖于输入执行参数
1。
插座环境的初始。
(2)
解析输入参数。
3。
CreateClient 。获取类RTSPClient结构(返回类媒体和一些瓦尔)
3.1类中等
3.1.1 / /首先生成一个新媒体的名称和投入的结果缓冲区
3.1.2 / /然后将它添加到我们的表:
(这是一个哈希表存储介质会议,应该有一个最大存储值,换句话说,客户端应如何处理有限的中小型会议)
3.2 RTSPClient变量的初始和构造的RTSP“用户代理”
4。
发送RTSP协议“选项”,并从服务器的选项。
4.1创建套接字连接
4.2 字符串发送选项
4.3获取从服务器的响应(如果响应代码是200,它支持的公共方法|选项)
5,
获取SDP描述由服务器的URL(返回值:SDPstring)
5.1 创建套接字连接
5.2检查URL的用户名和密码
5.3 字符串发送选项
5.3.1结构验证
5.3.2结构DESCRIPS字符串,并发送
5.4获取来自服务器的响应
5.4.1如果响应代码是什么,我们可以处理?
5.4.2发现的SDP描述,并做一些验证检查
6。
创建从上面的SDP描述媒体会话。
6.1 会议mediasession :: createNew
6.1.1类中等(这是不同从类RTSPClient的媒介)
(1.1) / /首先生成一个新媒体的名称和投入的结果缓冲区
(1.2) / /然后将它添加到我们的餐桌:
(这是一个哈希表存储介质会议,应当有一个最大存储值,换句话说,客户端应该处理有限的中小型会议)
(2)一些初始变量,如子会话(M =提出了一种新的子会话)和CNAME等
6.1.2初始与SDP信息mediasession
(1)解析SDP的字符串,得到VAR的关键和相关的价值。
(2)“=”(如果有)和创建 子会话
决定使用UDP或RTP Mediumname;协议;有效载荷的格式等。
6.2
最初的MediaSubsessionIterator,(使用会话和子会话(M))
6.1.1检查子会话的财产,并设置一些无功。
6.2.2为接收机接收数据,但不是“玩”流(S) ]
(1)子会话初始()
(1.1) 创建RTP和RTCP的 “Groupsocks上接收传入的数据。
(1.2),根据协议的名称,创建UDP或“RTP”的特殊来源
(1.3)创建RTCPInstance
[]
(1.3.1) / /安排他人处理传入的报告:
(1.3.2)/ / fRTCPInterface.startNetworkReading的(处理);
(1.3.3)/ /发送我们的第一次报告。RR和SDES(CNAME),服务器组成
( 2 )设置大的阈值时,对传入的数据包重新排序,并恢复它。也许的receiveBufferSize设置(如果我们设置它的输入参数)
6.2.3 球员(不重新编码流,而不是 “玩”流(S) )
这里只是什么也不做,等待后续行动。
6.3 SetupStreams(RTSP协议“设置”)
执行额外的每个子会话“设置”,前播放:
对于每个子会话,RTSPClient> setupMediaSubsession(*)
6.3.1 / /首先,构建一个验证字符串:
6.3.2 / /当发送多个“SETUP”的要求,包括在第二和后来的“设置”S“会话:”头。
6.3.3 / /构造一个标准的“运输:”头。[见附录(1)]
6.3.4发送请求字符串,并得到响应,
(1)检查验证(如响应代码
(2)/ /查找“会议:”头(设置会话ID),“运输:”头(设置服务器地址/端口)
(3)如果该子会话RTSP流的RTP接收(和发送/接收RTCP协议),然后以正确的方式改为插座连接
7,
创建输出文件:仅适用于接收器(存储流,但没有发挥它)
对于不同的文件格式,使用不同的 * FileSink类
这将使用QuickTime文件作为演示。 “
7.1 qtout = QuickTimeFileSink :: createNew(***)
7.1.1对于结构类中等再次看到前面的细节。
7.1.2一些变量的初始值
7.1.3 / /设置I / O状态,为每个输入子会话:
(1)
/ /忽略subsessions没有一个数据源:
(2) / /如果子会话的SDP描述指定屏幕尺寸或帧速率参数,然后使用这些。
“(请注意,这必须做呼叫前”setQTState()以下。)
(3)也许创建一个提示轨道,如果输入参数包含
(4) / /还设置了“BYE的”子会话的RTCP的实例处理程序:
(5)
/ /使用当前时间作为文件的创建和修改时间。
自1904年1月1日起,使用苹果的时间格式:秒
7.1.4 startPlaying(7.2中的细节)
| | 7.2 通用文件
7.2.1 filesink = FileSink :: createNew(***)
(1) first use MediaSink (use class Medium constructor again, see the front)
( 2 ) 一些变量有初始值。
7.2.2 filesink> startPlaying(实际使用父功能mediasink - > ST。)
(1)检查,如 / /确保我们不是已经正在播放我们的源代码是兼容的:
(2)ContinuePlaying()
(2.1) FramedSource :: getNextFrame
(源类型在startplaying任命为FrameSource ...)
检查和重视一些回调函数: / /确保我们不是已经被阅读:
“ 不同的媒体源“ - > doGetNextFrame()/ /例如Mp3FromADUSource虚拟功能。
在这个函数 返回一个框架之前,我们必须排队至少一个的ADU:
或/ /返回一帧:
8
startPlayingStreams
/ /最后,开始播放每个子会话,启动数据流:
8.1 rtspClient> playMediaSession(*)
8.1.1检查验证
/ /首先,确保我们有进步的RTSP会话
8.1.2
发送PLAY命令:
(1) / /首先,构建一个验证字符串:
(2) / /然后一个“范围”字符串:
(3) 建设“PLAY”字符串
(4)发送到服务器
(5)得到响应。并检查响应代码/的Cseq / ...
8.2 / /图拖延多久(如果有的话)之前关停,或重复播放
| | 8.3
checkForPacketArrival
/ /查看是否有任何数据包在subsessions“。
| |
8.4
checkInterPacketGaps
/ /检查每个子会话,计数已收到多少包:
9 taskScheduler()。doEventLoop()
从服务器和解析和存储的数据,或直接播放的主回路。
9.1
BasicTaskScheduler0 :: doEventLoop,
将循环使用 单步
9.2 BasicTaskScheduler ::单步
见,如果有任何可读的fReadSet插座(存储的子会话的套接字描述符),如果已处理
(1)
fDelayQueue。handleAlarm();
(2)
(*处理程序- > handlerProc)(处理程序- > clientData SOCKET_READABLE);循环处理子会话的任务。
[本MultiFramedRTPSource :: networkReadHandler的]
(3 M
ultiFramedRTPSource :: networkReadHandler
/ /获取一个自由BufferedPacket描述,举行新的网络数据包:
BufferedPacket * bPacket
=源> fReorderingBuffer - > getFreePacket(源);
/ /读取网络数据包,并进行健康检查RTP头:
( bPacket-> fillInData(源> fRTPInterface))/ /即将到来的数据包不属于当前会话
/ /处理RTP头的一部分
/ /其余的包是可用的数据
记录和保存它(的recordingBuffer)
布尔usableInJitterCalculatio
=源- > packetIsUsableInJitterCa
源> receptionStatsDB()
/ /请注意,我们有亮采一个RTP包
。noteIncomingPacket(rtpSSRC,rtpSeqNo,rtpTimestamp
源> timestampFrequency()
usableInJitterCalculatio
hasBeenSyncedUsingRTCP,bPacket - >数据大小());
/ /填充其余的包描述符,并将其存储:
bPacket-> assignMiscParams(rtpSeqNo,rtpTimestamp,presentationTime
hasBeenSyncedUsingRTCP,rtpMarkerBit,
timeNow);
/ /存储的数据包。
源> fReorderingBuffer - > storePacket(bPacket);
然后
源> doGetNextFrame1(); / /如果我们没有得到正确的数据,这个时候,我们会得到一个机会
9.3 MultiFramedRTPSource :: doGetNextFrame1()
到MultiFramedRTPSource或其他一些继承类
(1)/ /如果我们已经有可用的分组数据,然后实现它。
BufferedPacket * nextPacket
= fReorderingBuffer> getNextCompletedPacket(packetLossPrecededThis);
( 2 ) / /使用数据包之前,请检查是否有一个特殊的头
/ /需要处理:
如果 (!processSpecialHeader(nextPacket,specialHeaderSize))
这是为不同的数据包格式,特别是继承类会做什么...
( 3 )处理的数据包,不同的RTP包,它具有不同的结构,所以***
(4) / /数据包是可用的。它提供的所有或部分我们的来电:
nextPacket - > 使用(FTO,fMaxSize,帧大小,fNumTruncatedBytes
fCurPacketRTPSeqNum,fCurPacketRTPTimestamp,
fPresentationTime,fCurPacketHasBeenSynchro
fCurPacketMarkerBit);
--------- 无符号的 帧大小= nextEnclosedFrameSize(newFramePtr,fTail - fHead);
(5)
如果我们所有的数据,然后客户端希望:
/ /调用我们的'得到'功能后,自己的,
因为我们前面
/网络/读,我们可以调用直接,无风险
/ /无限递归。
afterGetting(本);
------------
无效 FramedSource :: afterGetting(FramedSource *源)
--------- 无效 FileSink :: afterGettingFrame(
无效 FileSink :: afterGettingFrame1
。
addData(fBuffer,帧大小,presentationTime)
。B
continuePlaying()/ /然后尝试得到下一帧:
“ ==
9.4布尔FileSink :: continuePlaying()
fSource> getNextFrame --------- FramedSource getNextFrame ------- MultiFramedRTPSource - >
9.5美国 无效 MultiFramedRTPSource :: doGetNextFrame()
(1)TaskScheduler :: BackgroundHandlerProc *处理程序
(TaskScheduler :: BackgroundHandlerProc *)networkReadHandler;
fRTPInterface startNetworkReading(处理);
doGetNextFrame1();
[回到第 9.3 ]
注意:
(1) 对于里尔流,使用一种特殊的“运输:”头,还添加了“挑战响应”。
(2)它们的详细关系不列出,因为它是一些复杂的,我们应该需要更多的时间。
(3)当我们到达了结束时间,从SDP线或服务器翻译拆解信息,然后客户端将停止
在启动功能startPlayingStream “它添加” ssessionTimerHandler时间表“。
从上面的流水帐我们可以看出利用live的代码创建一个传统的流媒体客户端的接收部分我们需要建立以下流程。
有效载荷格式得格式进行操作,我们当前实现得新媒体类型,在下面会有详细描述。
2.2.1 增加媒体的格式增加新媒体也是基于帧格式的这里每一帧称呼为 MAU的(媒体接入单位),而 MAU的在 RTP包中的组织不径相同。
如图所示,RTP负载格式的头球攻门被分为三个部分。每一节开始,一个字节的位域,由一个或多个可选字段。在某些情况下,整个两个部分可以省略的RTP负载格式的头。这可能会导致小到一个字节的RTP负载格式头。
所有应传输网络字节顺序,这意味着每个领域中最重要的字节首先传输的RTP负载格式领域。
其次是一个有效载荷的RTP负载格式头。有效载荷,可以组成一个完整的MAU的或MAU的片段。有效载荷可以包含部分MAU的,允许多个有效载荷分散在多个RTP包大的MAU。
额外的RTP负载格式头和RTP包的大小允许的有效载荷,对第一个有效载荷,可以遵循。
每一个包中 MAU 的组合形式有以下几种:

答: 从上可以看出,新的媒体的每个 RTP包当中,可能含有一个或多个 MAU 亦或者 MAU的的片段,而在解析每个 RTP包之后需要将每个完整 MAU的的信息(数据,大小,以及 PT:颁奖时间,包后首先手柄标准的 RTP协议
无效 MultiFramedRTPSource :: doGetNextFrame1()
{
而的 (fNeedDelivery)
/ /当然,看到前面
{
/ /如果我们已经有可用的数据包,然后交付它。
布尔packetLossPrecededThis;
BufferedPacket * nextPacket
/ /获取头包,也许我们只是处理
= fReorderingBuffer> getNextCompletedPacket(packetLossPrecededThis);
如果(nextPacket = NULL)
打破;
fNeedDelivery = FALSE;
(nextPacket useCount()== 0)
{
/ /使用数据包之前,检查是否有一个特殊的头
/ /需要处理:
无符号 specialHeaderSize;
如果 (!processSpecialHeader(nextPacket,specialHeaderSize)){
/ /有什么地方错头;拒绝数据包:
fReorderingBuffer-> releaseUsedPacket(nextPacket);
fNeedDelivery = TRUE;
打破;
}
nextPacket->跳跃(specialHeaderSize)的;
}
/ /检查我们是否是一个多包框架的一部分,以及是否
/ /有数据包丢失,会使这个包无法使用:
如果 (fCurrentPacketBeginsFram
/ /在processSpecialHeader的(),它会改变...
{
无符号 PT_tem = 0;
/ /亚历克西斯
FramePresentationTime(PT_tem);
nextPacket> setPresentTime(PT_tem);
/ /亚历克西斯04-11-10
如果 (packetLossPrecededThis | | fPacketLossInFragmentedF
{
/ /我们没有得到所有前一帧。
/ /忘记我们从它使用的任何数据:
FTO = fSavedTo;
fMaxSize = fSavedMaxSize;
fFrameSize = 0;
}
fPacketLossInFragmentedF
/ /开始帧,所以...
}
否则,
如果 (packetLossPrecededThis)
{
/ /我们在包多帧,与前一个数据包丢失
fPacketLossInFragmentedF
}
如果 (fPacketLossInFragmentedF
{
/ / ---亚历克西斯10-28
无符号 MauFragLength;
doLossFrontPacket(MauFragLength);
/ /得到长度MAU的片段,从现在到下一个MAU的开始
(MauFragLength!= 0)
{
nextPacket - >跳跃(MauFragLength);
fNeedDelivery = TRUE;
打破;
}
/ /原来的部分...
{
/ /正常情况下:这个包是不可用的;拒绝:
fReorderingBuffer-> releaseUsedPacket(nextPacket);
fNeedDelivery = TRUE;
打破;
}
}
/ /数据包是可用的。它提供的所有或部分我们的来电:
无符号的 帧大小;
nextPacket - >使用(FTO,fMaxSize,帧大小,fNumTruncatedBytes
fCurPacketRTPSeqNum,fCurPacketRTPTimestamp,
fPresentationTime,fCurPacketHasBeenSynchro
fCurPacketMarkerBit);
fFrameSize + =帧大小;
如果 (!nextPacket hasUsableData()){
/ /我们完全做到现在这个包
fReorderingBuffer-> releaseUsedPacket(nextPacket);
}
(fCurrentPacketCompletesF
{
/ /我们有客户想要的所有的数据。
(fNumTruncatedBytes> 0){
,游()<<“MultiFramedRTPSource :: doGetNextFrame1():总接收到的帧大小超过客户端的缓冲区大小(”
<< fSavedMaxSize <<“)。
“
<< fNumTruncatedBytes <<“尾随数据字节将被丢弃\ N”;
}
/ /调用我们的'得到'功能后,自己的,
因为我们前面
/网络/读,我们可以调用直接,无风险
/ /无限递归。
afterGetting(本);
/ /将存储的outbuffer的整体框架(在这里是文件)
}
其他
{
/ /这个包中分散的数据,并没有完成
/ /客户端想要的数据,
获取数据:
FTO =帧大小;
fMaxSize - =帧大小;
fNeedDelivery = TRUE;
}
}
}
04-11-10 就是这个意思。
C。
livem 的库分为 BasicUsageEnvironment,UsageEnvironment,groupsock 以及 livemedia 这 4 个部分,其中 BasicUsageEnvironment
2.3.1 BasicUsageEnvironment,UsageEnvironmentUsageEnvironment
哈希表 ( http://www.live.com/liveMedia/doxygen/html/classHashTable.html )
哈希链表的建立与维护
类中定义了类
的Iterator / /用于遍历表中的成员:
TaskScheduler
/ /计划任务发生延迟后,当我们到达一个调度点。
ScheduleDelayedTask(*)
unscheduleDelayedTask(*)
/ /处理套接字读取背景:
BackgroundHandlerProc(*)
turnOnBackgroundReadHand
turnOffBackgroundReadHan
doEventLoop (字符* watchVariable = NULL )= 0 ;
/ /停止当前线程,从程序的控制,但允许延迟的任务(和/或背景I / O处理)进行。
StrDup
字符串拷贝,
UsageEnvironment(http://www.live.com/liveMedia/do ... ageEnvironment.html)
/ /一个抽象基类,子类为每个库的使用
BasicUsageEnvironment
BasicHashTable ( http://www.live.com/liveMedia/doxygen/html/classBasicHashTable.html )
/ /一个简单的哈希表实现,哈希表的启发
/ /执行的Tcl 7.6使用:的< http://www.tcl.tk/> ;
类
BasicHashTable : 公众
的HashTable
内部类
的Iterator : 公众
的HashTable :: 迭代
BasicUsageEnvironment0
/ /抽象基类,有用的子类(例如,重新执行“操作符<<”)
类
BasicUsageEnvironment0 : 公共
定义了变量 无符号
fCurBufferSize
无符号
fBufferMaxSize ;
/ /一个抽象基类,子类非常有用(例如,重新实施插座事件处理)
类
: 公共
TaskScheduler
BasicUsageEnvironment
类
BasicUsageEnvironment : 公共
构造静态
BasicUsageEnvironment * createNew (TaskScheduler 和 taskScheduler )的;
类
BasicTaskScheduler : 公共
定义了
诠释
fMaxNumSockets ;
fd_set的
fReadSet ;
/
/要实施的背景如下:
/ /“mTunnel”组播接入服务
NetCommon
对于不同平台,弱者受制与不同的袜子头文件
的Win32 WinCE的VxWorks的UNIX的 SOLARIS系统
--- #包括<winsock2.h>
--- #包括<ws2tcpip.h>
NetAddress
/ /定义一个类型代表一个低级别的网络地址。
/ /目前,这是32位,为IPv4。
后来,推广,
/ /允许为IPv6。
类
NetAddress
类
NetAddressList
类
口岸
类
AddressPortLookupTable / /一个由(地址1,地址,端口)寻找对象的通用表
在MediaSubsession的initiate()时候使用NetAddressList来初始话服务器返回的SDP信息中”C=”后面的IP地址信息
NetInterface
(http://www.live.com/liveMedia/doxygen/html/classNetInterface.html)
类
NetInterface
类
DirectedNetInterface : 公众
负责写
类
DirectedNetInterfaceSet
负责 DirectedNetInterface 集的管理
类
插座: 公众
负责读
类
SocketLookupTable
负责查找袜子通过端口
类
NetInterfaceTrafficStats
负责计算流量(多少个包和字节5.3)
inet.c
定义了 initializeWinsockIfNeces
随机数产生函数,被 GroupsockHelper 调用
GroupsockHelper
定义 创建数据作者: UDP的或者流的TCP 组播源)的说明
TunnelEncaps
/隧道/封装拖车
类
TunnelEncapsulationTrail
IoHandlers
/ /在当前版本中不使用?
/ /传入的数据处理上的插座:
GroupEId
端点ID
/ /由groupsock使用
groupsock
(http://www.live.com/liveMedia/doxygen/html/classGroupsock.html)
/ /一个“OutputSocket”(默认)仅用于发送数据包。
/ /没有接收数据包(除非子类安排这)
类
OutputSocket : 公共
插座
/ /一个“OutputSocket”(默认)仅用于发送数据包。
/ /没有接收数据包(除非子类安排这)
类
destRecord
类
Groupsock :
OutputSocket
/ /一个“Groupsock”,用于发送和接收数据包。
/ /顾名思义,它最初被设计为发送/接收
/ /组播,但它可以发送/接收单播以及。
其中包含增加,移除地址, 同时对于判断(组 / 单播), TTL电
类
GroupsockLookupTable
/ /一种仰视“groupsock”的数据结构
/ /(组播地址,端口),或者插槽号码
这个部分是 live.com的得代码核心,要实现 RTSP协议得建立,控制,以及 RTP的传输得建立
以及各种 RTP协议 有效载荷的格式;
2.3.3.1 最小环境得建立1。our_md5/our_md5hl
如果你不需要对网址中进行验证得话,这个部分可以忽略
2,层次介绍
答:中等
/ /基本的LiveMedia类,订了一些纯虚函数
类
中等
后面所有的类都是继承中等或者其派生类的
对于每个媒体(subsession?),都会创建一个mediumname,同时多个medium将分享一个UsageEnvironment类的变量(内部定义了字符输出操作符以及任务调度函数)
B 。 RTSPClient(http://www.live.com/liveMedia/doxygen/html/classRTSPClient.html)
对于RTSPClient来首,Medium就是一个使用UsageEnvironment变量的一个中转站,或者说Medium是后面所有派生类的中转站^_^。
RTSPClient 主要有以下几个功能
DescribeURL()
/ /这是第一次使用的客户端函数来确定如果指定的URL验证
发送选项宣布描述请求到主机
setupMediaSubsession
playMediaSession
playMediaSubSession
pauseMediaSubsession
pauseMediaSession
recordMediaSubsession
setMediaSessionParameter
teardownMediaSubsession
teardownMediaSession
以上这些是public函数,同时RTSPClient还有以下一些内部函数,作为与Server之间的通信信息交互
sendRequest将
GETRESPONSE
parseResponseCode
parseTransportResponse
parseRTPInfoHeader
parseScaleHeader
而对于 RTSP协议
上面的函数功能就是发送RTSP规定的几种request到目的URL中,得到不同的反馈,比较重要的是DESCRIBE,它的response是server给的SDP信息
BTW : RTSPClient 中与服务器的连接处什么基于 TCP 的,因此,。
三RTCPInstance
RTCP的本分其实是脱离不了的RTP 连接的,它的端口什么对应的 RTP的插座连接端口+ 1 ,同时 RTP 数据包的到来都会引起 RTCP的内部(统计)数据的变化
D. MediaSession
(http://www.live.com/liveMedia/doxygen/html/classMediaSession.html)
需要说明的是,MediaSubSession才是基本的构成单位,MediaSession类只是对下面所属的SubSession做了一个统一管理,使用了一个Iterator
D.1中MediaSession
对session做总体控制,例如得到session的起始结束时间,session播放的scale以及Seek等
另外初始化整个 mediasession ,使用 RTSPClient 传来的 SDP的信息(必然需要解析的SDP 弱者受制与的各种信息)
D.2 MedaiSubSession
则是实现每个独立媒体的控制
对米之下的 SDP的信息详细解析 得到单个媒体的特殊信息,同时与 Groupsock *资料来源以及 *库建立关联,而后两者分别为处理接收以及接收器的 RTP分组数据
大肠杆菌MediaSink
的MediaSource的
FrameSource
类
MediaSource的:: 公共
中等
类
FramedSource : 公共
MediaSource的
这里将它们放在一起是因为我觉得它们可以合并在一起,(BTW:我就是这么做的)
该类是整个数据读取处理的中转站,同时也是下面函数的基类,定义了一些纯虚函数,供后面调用(例如MultiFramedRTPSource),FrameSource和MultiFramedRTPSource以及*Sink组成了一个完整的frame数据处理循环。
下面详细介绍 FrameSource 实现的函数
无效
FrameSource :: getNextFrame(无符号
的char * 来, 无符号
MAXSIZE
afterGettingFunc * afterGettingFunc
无效* afterGettingClientData
onCloseFunc * onCloseFunc
无效* onCloseClientData)
/ /共同所有框架的源代码级,这样定义它在这里,*接收器所使用的呢??
这个函数传递数据buffer指针以及大小,以及后期处理函数(得到数据后传给谁来处理 | 结束时如何处理),
{
/ /确保我们是不是已经被读取;
如果(fIsCurrentlyAwaitingData
{
,游()<< “FramedSource [” << 这 << “] :: getNextFrame():试图在同一时间阅读不止一次\ N” ;
出口;
}
FTO =
/ /输入缓冲区的指针
fMaxSize = MAXSIZE ;
/ /左缓冲区大小
fNumTruncatedBytes = 0 ;
/ /默认值,可以改变由doGetNextFrame();
fDurationInMicroseconds = 0 ;
/ /默认值,可以改变由doGetNextFrame();
fAfterGettingFunc = afterGettingFunc ;
fAfterGettingClientData = afterGettingClientData ;
fOnCloseFunc = onCloseFunc ;
fOnCloseClientData = onCloseClientData ;
fIsCurrentlyAwaitingData
doGetNextFrame ();
/,/ absolutly虚拟功能,将difineed。
}
同时将会调用doGetNextFrame()来处理下一帧数据,这里调用的MultiFramedRTPSource中的处理函数
下面的函数将在得到一帧数据后如何处理,这里将函数指到了上面得到的处理函数指针的地址上,live.com的代码则是在*Sink中。
/ /后的整体框架(MAU),已经得到了
无效
FramedSource :: afterGetting (FramedSource *资料 来源)
{
- > fIsCurrentlyAwaitingData
/ /表明我们可以再次读取
/ /注意,这需要在这里完成,在“fAfterFunc”
/ /以下称试图读取另一帧(它通常会)
(源- > fAfterGettingFunc != NULL ){
(*(源- > fAfterGettingFunc ))(来源- > fAfterGettingClientData
源- > fFrameSize , 源- > fNumTruncatedBytes
源- > fPresentationTime
源- “> fDurationInMicroseconds“ );
}
}
这里,将处理完一帧数据作为可以读写socket的判断标志之一,主要是因为怕读写过于频繁或者过少早成资源浪费或者是数据处理不及时的情况,我觉得这里可以变动下,因为现在的视频媒体很多是一帧放在几个packet中的,所以。。。
对于停止得到帧数据的处理,在FrameSource的派生类中有作用,而停止整个线程,则由handleColsure来处理,见下面函数,仍然调用初始时候传进来的函数指针来作用。
无效
FrameSource :: stopGettingFrames ()
{
fIsCurrentlyAwaitingData
doStopGettingFrames ();
}
无效
FrameSource :: doStopGettingFrames ()
{
/ /默认的实现:什么都不做
/ /子类,不妨专门会这样,以确保一个
/ /随后的读者可以拿起这一个不放过。
}
:无效
FrameSource :: handleColsure (无效* clientData ),
/ /这应该被称为源被发现,如果被关闭(即不再可读)
{
FrameSource * 源 =(FrameSource *)clientData ;
源- > fIsCurrentlyAwaitingData
/ /现在我们有接近,而不是,
如果(源- > fOnCloseFunc != NULL )
{
(*(源- > fOnCloseFunc ))(来源- > fOnCloseClientData );
}
}
。三,一些总结A。缓冲 管理
如何控制突发的输入数据包是一个大题目。泄漏圆桶模型可能是有用的,但是,如果到达一个更高速度的数据包长点射(在我们的系统),斗将溢出和我们的控制功能,将采取行动反对,突发包。
在我们的客户端系统,以获得库(经理会议,并接收线程)和播放器(用于显示图像,并把音箱的声音,这个地方,包括解码器),我们把一个中间层之间的服务器和播放器,这是移植容易。
下面给出了更详细的说明。
BTW:UPC(使用参数控制)和处理例外,如丢包的过程,是复杂的,我们不会在这里给一个完整的描述。
1 。
当会话已定,我们将准备接收流的包。现在,例如,有两个媒体subsessions哪一个是音频子会话和另一种是视频子会话,并为他们每个人都有一个缓冲,下面是接收缓冲区管理器的细节。
1)
在接收部分,我们已经定义了一个“包类,它用来存储和处理一个RTP包。
2)
对于每个子会话,有一个缓冲队列,其数量是可变的,并根据Maxim的延迟时间,我们确定了缓冲队列的数目。
3)
缓冲队列的数据包重新排序和别的负责。
4)
在接收缓冲区中,我们将尽快处理包(一包是由网络的延迟,我们会为它等待,直到抵达延迟阈值除外),并留下缓冲区溢出和下溢经理玩家。
图1:包接收流量
图2:数据包处理流程(解码器)
2,
播放器(解码)缓冲区
玩家存储介质从RTSP客户端的数据,为每个流的缓冲区。球员每流分配内存,根据预卷的最大长度。在初始阶段,玩家将等待缓冲,直到每一个流至少已收到的内容 预卷时间。因此,每一个缓冲区长度将 Prerollmax + C
(C是一个常数)。当缓冲区都准备好了,球员将开始播放线程和播放的内容。
图3:播放流缓冲
播放线程数为每一个流的时间邮票。在播放过程中,一个流可能被推迟,然后下运行相应的缓冲区。如果视频流被延迟,音频正常播放,但视频档。回放线程将继续依靠音频流的时间戳,但不会增加视频时间戳。当新的视频数据抵达回放线程将决定它应该跳过一些视频帧到下一个关键帧,或打快赶上音频时间戳。通常情况下,球员可以选择打快,如果它只是推迟了很短的时间。另一方面,如果它是停滞的音频流,播放器将缓冲再次到每一个缓冲区存储数据超过ţ时间。这里 T是与音频流的预卷时间很短的时间有关,它可以小于或等于预卷。处理这个问题是当网络抖动,减少音频间断。为了避免这种情况下,选择一个较高的 T值的,或选择一个更好的网络。
如果一个缓冲区溢出,这被视为一个错误。对于视频流,直到到达下一个关键帧,错误处理程序会删除一些数据。和音频流,错误处理程序会简单地丢弃一些数据。
图4:进程缓冲区溢出或下溢
B.如何控制接收循环在现场的 openRTSP 程式码的的主循环
ENV-> taskScheduler()。doEventLoop()
中,函数doEventLoop有一默认的参数,可以通过设置这个参数达到推出循环的目的,不过可以直接调用下面C与D所写的释放资源的方法pause接收或者推出整个线程。
OpenRTSP例子没有给具体的实现,最新的livemedia版本可以支持SEEK了(包括服务器部分)
/ /暂停:
playerIn.rtspClient> pauseMediaSession(*(playerIn.Session));
playerIn.rtspClient> playMediaSession(*(playerIn.Session),-1);
/ /将恢复
/ /搜索
浮动SessionLength会话> playEndTime()
/ / 先得到播放时间区域,在 SDP的解析中
先暂停。***
再 rtspClient> PlayMediaSession(会议开始);
/ /开始不到“SessionLength”
OpenRTSP给出的解决方案是shutdown()函数,而在我们将库与播放器连接过程中,发觉有线程始终不能推出,后来参考Mplayer(它的rtsp支持采用的就是live的代码)的释放方案,给出以下代码,目前运行一切正常。
,无效 OutRTSPClient()/ / rtpState的什么我们当前订的一个数据结构体,储存了一些会话信息
{
如果 (rtpState>会议== NULL)
返回;
(rtpState> rtspClient!= NULL){
MediaSubsessionIterator国际热核实验堆(*(rtpState - >会话));
MediaSubsession *子会话;
((子会话= iter.next())!=空){
接近中等::(子会话 - >汇);
子会话 - >水槽= NULL;
rtpState“> rtspClient> teardownMediaSubsession(*子会话);
}
}
UsageEnvironment * ENV =空;
TaskScheduler *调度=空;
(rtpState>会话!= NULL){
ENV =&(rtpState - >会话>,游());
调度=&(ENV-> taskScheduler());
}
接近中等::(rtpState->会议);
中型::密切(rtpState-> rtspClient)的;
ENV->回收();
删除 调度;
}