开源项目live555学习心得
最近因项目开发需要,开始学习开源项目live555,特别将个人的一些学习心得做一下记录,如有理解不正确之处,欢迎各位朋友指出。
首先是源码的下载,这可以从http://www.live555.com上下载,同时上面亦提供了相关的文档,文档虽较为粗糙,但总比找不到任何文档说明强。当然,我在学习的过程中,也从网上查找了一段时间,收获还是有一点的,其中就有诸如:RTSP服务器实例live555源代码分析、live555源代码简介、live555代码解读系列、基于live555的rtp-rtcp研究等文章。
我的学习是在VS2008环境下进行的,要能够在VS环境下顺利进行,还得做相关工作。live开源虽说是用C++写的,代码风格也非常优秀,但是其是用makefile文件来做的,而对未接触过makefile文件的我是一个问题。幸运的是在网上偶然发现了一篇介绍在VC6环境下编译live的文章,然后照着上面所说的做,磕磕碰碰,编译成功了四个库并转移到了VS2008环境中。更幸运的是又是偶然在网上发现了一篇介绍用VS2008编译live的文章并提供了编译后的工程下载,download,然后就开始了相对漫长的学习过程。
从程序的结构来看,live项目包括了四个基本库、程序入口类(在mediaServer中)和一些测试代码(在testProgs中)。四个基本库是UsageEnvironment、BasicUsageEnvironment、groupsock和liveMedia。
UsageEnvironment包括抽象类UsageEnvironment和抽象类TaskScheduler,这两个类用于事件调度,其中包括实现了对事件的异步读取、对事件句柄的设置及对错误信息的输出等;该库中还有一个HashTable,这是一个通用的HashTable,在整个项目中都可以使用它,当然该HashTable也是一个抽象类。
BasicUsageEnvironment中的类主要是对UsageEnvironment中对应类的实现。
groupsock,顾名思义,用于数据包的接收和发送,其同时支持多播和单播。groupsock库中包括了GroupEId、Groupsock、GroupsockHelper、NetAddress、NetInterface等类,其中Groupsock类有两个构造函数,一个是“for a source-independent multicast group”,另一个是“for a source-specific multicast group”;而GroupsockHelper类主要用于读写Socket。
liveMedia是很重要的一个库,其不仅包含了实现RTSP Server的类,还包含了针对不同流媒体类型(如TS流、PS流等)编码的类。在该库中,基类是Medium,层次关系非常清晰。在该库中,有几个很重要的类,如RTSPServer、ServerMediaSession、RTPSink、RTPInterface、FramedSource等。
在http://www.live555.com上的相关文档中提到穿透防火墙的问题,方法是开启一个HTTP的tunnel,然后我们可以在liveMedia库中找到一个RTSPOverHTTPServer的类,该类解决了这样的问题。
mediaServer下的live555MediaServer提供了main函数,DynamicRTSPServer继承了RTSPServer并重写了虚函数lookupServerMediaSession。
前面已经讲到,通过不断地尝试(其实要在XP SP3环境下使用VS2008编译成功还是挺费神的),总算把源代码编译成功,同时又参考了新下载的一个用VS2008编译通过的live555源代码。结合这些,开始对主要类结构进行初步分析。
鉴于UsageEnvironment库、BasicUsageEnvironment库和groupsock库中的类较少,就暂且不作分析了。这里主要针对liveMedia库中的主要类结构进行分析。通过查看类关系图,可以从整体把握,但是苦于类太多,用类关系图看起来也不方便,于是就自己重新整理了一下,下面是 liveMedia库的主要类结构。(注:其他单类及结构体等不在此列出)
l Medium
n RTSPServer
n RTSPOverHTTPServer
n MediaSession
n ServerMediaSession
n ServerMediaSubsession
u OnDemandServerMediaSubsession
l FileServerMediaSubsession
n ADTSAudioFileServerMediaSubsession
n AMRAudioFileServerMediaSubsession
n H263plusVideoFileServerMediaSubsession
n MP3AudioFileServerMediaSubsession
n MPEG1or2VideoFileServerMediaSubsession
n MPEG2TransportFileServerMediaSubsession
n MPEG4VideoFileServerMediaSubsession
n WAVAudioFileServerMediaSubsession
l MPEG1or2DemuxedServerMediaSubsession
u PassiveServerMediaSubsession
n MediaSource
u FramedSource
l FramedFileSource
n ByteStreamFileSource
n ADTSAudioFileSource
n MP3FileSource
u MP3HTTPSource
l BasicUDPSource
l RTPSource
n MultiFramedRTPSource
u RawQCELPRTPSource
u AC3AudioRTPSource
u MPEG4GenericRTPSource
u RawAMRRTPSource
u H261VideoRTPSource
u H263plusVideoRTPSource
u H264VideoRTPSource
u JPEGVideoRTPSource
u MP3ADURTPSource
u MPEG1or2AudioRTPSource
u MPEG1or2VideoRTPSource
u MPEG4ESVideoRTPSource
u MPEG4GenericRTPSource
u MPEG4LATMAudioRTPSource
u DVVideoRTPSource
u QuickTimeGenericRTPSource
u SimpleRTPSource
l AMRAudioSource
n AMRDeinterleaver
n AMRAudioFileSource
l ByteStreamMultiFileSource
l DeviceSource
l JPEGVideoSource
l MPEG1or2DemuxedElementaryStream
l MPEG2TransportStreamMultiplexor
n MPEG2TransportStreamFromESSource
n MPEG2TransportStreamFromPESSource
l AudioInputDevice
n WAVAudioFileSource
l FramedFilter
n H264FUAFragmenter
n QCELPDeinterleaver
n AC3AudioStreamFramer
n ADUFromMP3Source
n uLawFromPCMAudioSource
n H264VideoStreamFramer
n MP3FromADUSource
u MP3Transcoder
n PCMFromuLawAudioSource
n MPEG2IFrameIndexFromTransportStream
n NetworkFromHostOrder16
n HostFromNetworkOrder16
n MP3ADUinterleaverBase
u MP3ADUinterleaver
u MP3ADUdeinterleaver
n MPEG2TransportStreamFramer
n EndianSwap16
n H263plusVideoStreamFramer
n MPEGVideoStreamFramer
u MPEG1or2VideoStreamFramer
l MPEG1or2VideoStreamDiscreteFramer
u MPEG4VideoStreamFramer
l MPEG4VideoStreamDiscreteFramer
n MPEG1or2AudioStreamFramer
n DVVideoStreamFramer
n MP3ADUTranscoder
n MPEG2TransportStreamTrickModeFilter
n MediaSink
u DummySink
u BasicUDPSink
u RTPSink
l MultiFramedRTPSink
n MPEG4GenericRTPSink
n VideoRTPSink
u H264VideoRTPSink
u MPEG1or2VideoRTPSink
u H263plusVideoRTPSink
u JPEGVideoRTPSink
u DVVideoRTPSink
u MPEG4ESVideoRTPSink
n AudioRTPSink
u AC3AudioRTPSink
u MPEG4LATMAudioRTPSink
u GSMAudioRTPSink
u MPEG1or2AudioRTPSink
u AMRAudioRTPSink
u MP3ADURTPSink
n SimpleRTPSink
u HTTPSink
l MPEG1or2VideoHTTPSink
u FileSink
l AMRAudioFileSink
l H264VideoFileSink
n RTCPInstance
n RTSPClient
n SIPClient
n DarwinInjector
n QuickTimeFileSink
n MPEG1or2Demux
n MPEG2TransportStreamIndexFile
n MPEG1or2FileServerDemux
n AVIFileSink
l BufferedPacketFactory
n QCELPBufferedPacketFactory
n AMRBufferedPacketFactory
n MPEG4GenericBufferedPacketFactory
n ADUBufferedPacketFactory
n QTGenericBufferedPacketFactory
n LATMBufferedPacketFactory
n H264BufferedPacketFactory
n JPEGBufferedPacketFactory
l BufferedPacket
n QCELPBufferedPacket
n AMRBufferedPacket
n MPEG4GenericBufferedPacket
n ADUBufferedPacket
n QTGenericBufferedPacket
n LATMBufferedPacket
n H264BufferedPacket
n JPEGBufferedPacket
l StreamParser
n AC3AudioStreamParser
n MPEGVideoStreamParser
u MPEG1or2VideoStreamParser
u MPEG4VideoStreamParser
n MPEG1or2AudioStreamParser
n H263plusVideoStreamParser
n MPEGProgramStreamParser
从上面这个主要的类结构可以看出,liveMedia库中的基类为Medium,其下又有几个非常重要的部分,一个是×××Subsession,除Medium父类外,所有的×××Subsession类都继承于ServerMediaSubsession;一个是×××Source,MediaSource的frameSource下主要包含FramedFileSource、RTPSource、FramedFilter等几个主要的部分;一个是MediaSink,以继承于RTPSink的类居多。
此外,还包含了用于处理packet的BufferedPacketFactory和BufferedPacket及其相关子类,用于处理流分析的StreamParser及其子类。
基本上,整个liveMedia库的主要类结构就是这样。不过,类太多了,分析起来还是有较大的困难。于是乎,采取去掉枝叶保留主干的做法,将整个服务器精简成支持一种格式的服务器,如MP3流服务器。经过分离,一个基于MP3的测试服务器的主要类结构如下所示。(注:其他单类及结构体等不在此列出)
l Medium
n RTSPServer
n MediaSession
n ServerMediaSession
n ServerMediaSubsession
u OnDemandServerMediaSubsession
l FileServerMediaSubsession
n MP3AudioFileServerMediaSubsession
n MediaSource
u FramedSource
l FramedFileSource
n MP3FileSource
u MP3HTTPSource
l BasicUDPSource
l RTPSource
n MultiFramedRTPSource
u MP3ADURTPSource
u MPEG1or2AudioRTPSource
u SimpleRTPSource
l FramedFilter
n ADUFromMP3Source
n MP3FromADUSource
u MP3Transcoder
n MP3ADUinterleaverBase
u MP3ADUinterleaver
u MP3ADUdeinterleaver
n MP3ADUTranscoder
n MediaSink
u BasicUDPSink
u RTPSink
l MultiFramedRTPSink
n AudioRTPSink
u MPEG1or2AudioRTPSink
u MP3ADURTPSink
n RTCPInstance
l BufferedPacketFactory
n ADUBufferedPacketFactory
l BufferedPacket
n ADUBufferedPacket
根据上面这种相对简单的类结构,分析起来就方便多了。于是,开始进入具体的分析细节了,这是一个相对漫长的过程,再加上本人的C++水平有限,这又将是一个相对艰苦的过程,一切慢慢来吧……
末了,讲讲启动服务器的过程:
LIVE555是一个纯粹的RTSP服务器,其服务器主类为liveMedia库下的RTSPServer;mediaServer下的live555MediaServer为主程序的入口类,DynamicRTSPServer是RTSPServer的实现类。
从live555MediaServer类的入口函数main中可以非常清晰地分析出服务器的启动过程。
首先是createNew一个TaskSchedulers对象和一个UsageEnvironment对象,这是初始工作。
之后是一段访问控制的代码。然后开始进入创建RTSP服务器的代码段,服务器指定了一个默认端口554和一个可供替代的端口8554。
接下来,createNew一个DynamicRTSPServer,这里建立了Socket(ourSocket)在TCP的554端口(默认端口)进行监听,然后把连接处理函数句柄和socket句柄传给任务调度器(即taskScheduler),既是RTSPServer类中的这句代码:env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,this)。紧接着就是对socket句柄和incomingConnectionHandler句柄的处理,主要是进行关联等。
最后,进入主循环(即env->taskScheduler().doEventLoop();),等待客户端连接。服务器启动完毕。
文章的最后,需要说明的是,在编译运行的过程中,我是使用VLC播放器来进行测试的,同时通过使用Ethereal的网络分析工具抓包分析其建立到传输的过程,虽然在live555源代码中关于RTSP建立及收发数据包的过程已经用代码写得非常清楚(这个好好分析一下源码就可以了),但我想,学习使用一下一些网络分析工具对自身也是颇为有益的。
处理连接请求的基本流程:
l Step 1:与客户端建立RTSP连接(调用incomingConnectionHandler方法),创建ClientSession并关联fClientSocket与incomingRequestHandler(调用incomingConnectionHandler1)。
l Step 2:接收客户端请求(调用incomingRequestHandler方法)。
l Step 3:从客户端Socket读取数据,并对请求数据(即the request string)进行转换(调用parseRTSPRequestString方法,该方法在RTSPCommon类中)。
l Step 4:根据分离出来的指令进行分别处理:
n OPTIONS→handleCmd_OPTIONS
n DESCRIBE→handleCmd_DESCRIBE
handleCmd_DESCRIBE这一个方法比较重要,首先根据urlSuffix查找ServerMediaSession是否存在(调用lookupServerMediaSession方法,该方法中通过HashTable来查找)。
在testOnDemandRTSPServer项目工程中,仅仅是通过streamName来确认session是否为NULL。而在完整的live555MediaServer项目工程中,则是通过DynamicRTSPServer类来处理,其首先是查找文件是否存在,若文件不存在,则判断ServerMediaSession(即smsExists)是否存在,如果存在则将其remove(调用removeServerMediaSession方法);若文件存在,则根据文件名创建一个ServerMediaSession(调用createNewSMS方法,若在该方法中找不到对应的文件扩展名,则将返回NULL)。
如果通过lookupServerMediaSession返回的是NULL,则向客户端发送响应消息并将fSessionIsActive置为FALSE;否则,为该session组装一个SDP描述信息(调用generateSDPDescription方法,该方法在ServerMediaSession类中),组装完成后,生成一个RTSP URL(调用rtspURL方法,该方法在RTSPServer类中)。
n SETUP→handleCmd_SETUP
handleCmd_SETUP方法中,有两个关键的名词,一个是urlPreSuffix,代表了session name(即stream name);一个是urlSuffix,代表了subsession name(即track name),后面经常用到的streamName和trackId分别与这两个名词有关。
接下来会创建session's state,包括incrementReferenceCount等。紧接着,会针对确定的subsession(track)查找相应的信息。接着,在request string查找一个"Transport:" header,目的是为了从中提取客户端请求的一些参数(调用parseTransportHeader方法,该方法在RTSPServer类中),如clientsDestinationAddressStr、ClientRTPPortNum等。
再接着是getStreamParameters(该方法在ServerMediaSession类中被定义为纯虚函数并在OnDemandServerMediaSubsession类中被重定义),然后通过fIsMulticast和streamingMode来组装不同的响应消息。
n PLAY→handleCmd_PLAY:处理播放请求,具体的实现流程请参见后面的步骤。
n PAUSE→handleCmd_PAUSE:处理暂停请求,在执行了该请求后,最终会调用StopPlaying方法,并将fAreCurrentlyPlaying置为FALSE。
n TEARDOWN→handleCmd_TEARDOWN:处理停止请求,将fSessionIsActive置为FALSE。
n GET_PARAMETER→handleCmd_GET_PARAMETER:该方法主要是维持客户端与服务器通信的生存状态,just for keep alive。
n SET_PARAMETER→handleCmd_SET_PARAMETER:该方法未针对SET_PARAMETER作实现,使用该方法会调用handleCmd_notSupported方法,并将最终引发与客户端断开连接。
l Step 5:根据Step 4的不同指令进行消息响应(调用send方法),该消息响应是即时的。
l Step 6:处理客户端发送“SETUP”指令后即开始播放的特殊情况。
l Step 7:将RequestBuffer进行重置,以便于为之后到来的请求做好准备。
l Step 8:检查fSessionIsActive是否为FALSE,如果是则删除当前的ClientSession。
处理PLAY的基本流程:
l Step 1:对rtspURL及相关header的处理,涉及较多的细节。
l Step 2:根据不同的header对流进行缩放比例或定位的处理。
如果为sawScaleHeader,则进行缩放比例的处理(调用setStreamScale方法,该方法在OnDemandServerMediaSubsession类中实现)。
如果为sawRangeHeader,则进行寻找流的处理(即是对流进行定位,调用seekStream方法,该方法在OnDemandServerMediaSubsession类中实现;同时,该方法的调用是在初始播放前及播放过程中由于用户拖动播放进度条而产生的系列请求)。
在OnDemandServerMediaSubsession类中,seekStream方法中调用了seekStreamSource方法,该方法在对应的媒体格式文件的FileServerMediaSubsession类中实现(如针对WAV格式,则在WAVAudioFileServerMediaSubsession类中实现;针对MP3格式,则在MP3AudioFileServerMediaSubsession类中实现)。
同理,OnDemandServerMediaSubsession类中的setStreamScale方法中所调用的setStreamSourceScale方法亦是类似的实现机制。
l Step 3:开始进行流式播放(调用startStream方法,该方法在OnDemandServerMediaSubsession类中实现)。
n Step 3.1:根据clientSessionId从fDestinationsHashTable中查找到destinations(包括了客户端的IP地址、RTP端口号、RTCP端口号等信息)。
n Step 3.2:调用startPlaying方法,在该方法中根据RTPSink或UDPSink分别调用startPlaying方法。
如果是调用RTPSink的startPlaying方法,则接着会调用MediaSink类中的startPlaying方法,并在该方法中调用MultiFramedRTPSink类中的continuePlaying方法,之后便是buildAndSendPacket了。这里已经来到重点了,即是关于不断读取Frame并Send的要点。在MultiFramedRTPSink类中,通过buildAndSendPacket、packFrame、afterGettingFrame、afterGettingFrame1、sendPacketIfNecessary和sendNext构成了一个循环圈,数据包的读取和发送在这里循环进行着。特别注意的是sendPacketIfNecessary方法中的后面代码(nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);),通过Delay amount of time后,继续下一个Task,并回过来继续调用buildAndSendPacket方法。
在packFrame方法中,正常情况下,需要调用getNextFrame方法(该方法在FramedSource类中,并且对不同媒体格式的Frame的获取出现在FramedSource类的getNextFrame方法中,通过调用doGetNextFrame方法来实现)来获取新的Frame。
如果是调用UDPSink的startPlaying方法,则接着会调用MediaSink类中的startPlaying方法,并在该方法中调用BasicUDPSink类中的continuePlaying方法。在这之后由若干个方法构成了一个循环圈:continuePlaying1、afterGettingFrame、afterGettingFrame1、sendNext。并在afterGettingFrame1方法中实现了packet的发送(fGS->output(envir(), fGS->ttl(), fOutputBuffer, frameSize);)。
Step 3.3:针对RTPSink创建RTCP instance(RTP与RTCP的配套使用决定了其必须这么做,否则可能就跟直接使用UDP发送数据包没什么两样了^_^),创建RTCP instance时,将incomingReportHandler句柄作为BackgroundHandlerProc,以便于处理RTCP的报告,并开始startNetworkReading。这里RTP/RTCP的使用方式有两种,一种建立在TCP之上,一种建立在UDP之上。
http://blog.csdn.net/huangxinfeng/archive/2010/03/11/5369391.aspx
http://blog.csdn.net/huangxinfeng/archive/2010/03/12/5374721.aspx
http://blog.csdn.net/huangxinfeng/archive/2010/03/22/5404233.aspx
开源项目live555学习心得的更多相关文章
- Android 开源项目及其学习
Android 系统研究:http://blog.csdn.net/luoshengyang/article/details/8923485 Android 腾讯技术人员博客 http://hukai ...
- 一个toolkit或者一个开源项目如何学习它并使用它
一个toolkit或者一个开源项目如何学习它并使用它 一般一个流行的toolkit和开源项目,一般都会被广泛地被应用: 那么,我们如何学习它,如何应用它在自己的业务场景中呢? 答案就是:学习源码并借鉴 ...
- iOS及Mac开源项目和学习资料【超级全面】
UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITable ...
- iOS开发--iOS及Mac开源项目和学习资料
文/零距离仰望星空(简书作者)原文链接:http://www.jianshu.com/p/f6cdbc8192ba著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 原文出处:codecl ...
- 开源项目AndroidReview学习小结(1)
多看多学涨姿势 最近学习了一个开源项目,感觉收获颇多,这里做下简要的记录,首先感谢作者的开源.先看个大概图 感觉框架非常简单,界面也很一般,不过底层的处理的一些处理还是有很多可圈可点之处,代码的处理一 ...
- 开源项目AndroidReview学习小结(2)
读书破万卷下笔如有神 作为入门级的android码农的我,还是需要多多研读开源代码 下面继续接着上一篇的分析,这一篇主要介绍第一个tab,ReviewFragment的分析,界面看起来简单,背后的逻辑 ...
- Android开源项目SlidingMenu学习(二)
前一篇SlidingMenu学习(一)文章中了解了导入SlidingMenu到我们项目经常出现的问题,下面我们正式学习. 先看一个效果: 看到两幅图片的差别了吗,左边的一栏时可以滑动的,可以隐藏掉,现 ...
- 开源项目几点心得,Java架构必会几大技术点
关于学习架构,必须会的几点技术 1. java反射技术 2. xml文件处理 3. properties属性文件处理 4. 线程安全机制 5. annocation注解 ...
- 分享海量 iOS 及 Mac 开源项目和学习资料
UI 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITable ...
随机推荐
- iOS7 NavigationController 右滑手势问题
苹果一直都在人机交互中尽力做到极致,在iOS7中,新增加了一个小小的功能,也就是这个api:self.navigationController.interactivePopGestureRecogni ...
- 通过class实例取得类的接口,父类,构造器
interface China { public static final String NATIONAL = "JAPAN"; public static fin ...
- android 布局常用混淆属性
1.如何控制某一控件在父控件中的相对位置呢? 在Android系统中提供了layout_margin,用来控制某一控件边缘相对于父控件的边距. 其中, android:layout_marginTop ...
- java面向对象编程(类、对象)
一.面向对象编程概述面向对象编程(Object Oriented Programming,缩写为OOP)是当今最流行的程序设计技术,它具有代码易于维护.可扩展性好和代码可常用等优点.面向对象的设计 ...
- AT&T汇编试讲--获取CPU Vendor ID
纯汇编代码如下: # a test program to get the processor vendor id # data segment .section .data output: .asci ...
- 优化器的使用oracle ---explain plan
如果要分析某条SQL的性能问题,通常我们要先看SQL的执行计划,看看SQL的每一步执行是否存在问题. 如果一条SQL平时执行的好好的,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本可以 ...
- 关于javascript面向对象之闭包
要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量,而在函数外部无法 ...
- iOS 中多线程的简单使用
iOS中常用的多线程操作有( NSThread, NSOperation GCD ) 为了能更直观的展现多线程操作在SB中做如下的界面布局: 当点击下载的时候从网络上下载图片: - (void)loa ...
- Junit 测试常见错误
1. java.lang.Exception: No tests found matching Method deleteUser(mybatis.dao.Usertest) from org.jun ...
- 经典union的使用
一个用户下广告位 某一天有收入和支出 有支出不一定有收入 有收入不一定有支出 下例为按用户查询 sanhao 下的信息 支出如下: 收入如下: 按天进行查询,例如查询: 得到结果如下: 使用一 ...