live555使用整理
live555使用、学习及遇到的问题的整理。
系统环境的抽象,主要用于消息的输入输出和用户交互功能,包含抽象类UsageEnviroment,TaskScheduler,DelayQueue类,HashTable。
UsageEnvironment代表了整个系统运行的环境,它提供了错误记录和错误报告的功能,无论哪一个类要输出错误,都需要保存UsageEnvironment的指针。
TaskScheduler类: 实现事件的异步处理、事件处理函数的注册等,它通过维护一个异步读取源实现对诸如通信消息到达等事件的处理,通过DelayQueue实现对其他注册事件的延时调度。TaskScheduler由于全局中只有一个,所以保存在UsageEnvironment中,而所有的类又都保存了UsageEnvironment的指针,所以谁想把自己的任务加入调度中,那是很容易的。
TaskScheduler中实现的三类调度任务:
socket任务(HandlerSet类管理)
延迟任务(DelayQueue类管理)
Event任务;
第一个及第三个在加入执行队列后会一直存在,而delay任务在执行完一次后会立即丢掉。
socket handler保存在BasicTaskScheduler0::HandlerSet* fHndlers中。
event hander保存在数据BasicTaskScheduler0::TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGRES]中。
delay task保存在队列BasicTaskScheduler0::DelayQueue fDelayQueue中。
HashTable类:实现了哈希表。
HandleSet类: 是一种专门用于执行socket操作的任务(函数),被TaskScheduler用来管理所有的socket任务(增删改查)。在整个系统中都可以用到它。
DelayQueue类:延迟队列,他就是在TaskScheduler中用于管理调度任务的东西。他是一个队列,队列中每一项代表了一个要调度的任务(在fToken变量中保存)。该类同时保存了这个任务离执行时间点的剩余时间。
该模块是UsageEnvoronment的一个控制台的实现。它针对控制台的输入输出和信号响应进行具体实现。
主要被设计用于支持多播,仅仅支持UDP协议。理论上讲那些需要操作udp socket的类应保存GroupSocket的实例,但事实上并不是,RTPSink,RTPSource,RTCPInstance等,他们都没有保存GroupSocket型的变量。 他们是通过RTPInterface类来进行socket操作的!
该模块是Live555最重要的模块,该模块声明了一个抽象类Medium,其它所有类都派生自该类。
liveMedia主要运转的是一个source和sink的循环。
source:数据来源的抽象,比如通过RTP读取数据;
sink:数据消费者的抽象。比如吧接收到的数据存储到文件,该文件就是一个sink。
source和sink通过RTP子会话(MediaSubSession)联系在一起, 数据的流通可能会经过多个Source和Sink。。
源码文件名以sink结尾的就是sink,像MediaSink.cpp,以source结尾的就是source,如MediaSource.cpp。
TCP传输方式,读取数据发送时注意TCPStreamSink::processBuffer()这个函数。
MediaSink是各种类型的Sink的基类,MediaSource是各种类型Source的基类。各种类型的流媒体格式和编码的支持就是通过对这两个类的派生实现的。
RTSPClient:该类实现RTSP请求的发送和响应的解析,同时根据解析的结果创建对应的RTP会话。
RTSPClientSession:每个该类的实例代表每个客户端的rtsp会话,保存了每个客户的socket。
MediaSession:用于表示一个RTP会话,一个MediaSession可能包含多个子会话(MediaSubSession),子会话可以是视频子会话、音频子会话等。
RTCPInstance: 此类实现RTCP协议的通信
RtspServer:服务器实例,一旦创建就永久存在,其余对象都是由他创建或由它引起它们的创建。直接掌管ServerMediaSession和RTSPClient,只管这两个类的实例的创建不负责销毁。
ServerMediaSession:对应一个媒体文件,ServerMediaSession在客户端发出对一个新文件的DESCRIBE时创建。建立ServerMediaSession的同时也创建了ServerMediaSubsession,被ServerMediaSession所管理,代表一个文件中的track。
SercverMediaSubSession:代表的是一个静态的流(或者说tack),也就是可以从它里面获取一个流的各种信息,但是不能获取流的传输状态。
如果添加多个ServerMediaSubsession那么ServerMediaSession与流名字所指定的文件是没有关系的,也就是说ServerMediaSession不会操作文件,文件的操作是放在ServerMediaSubSession中的。
由live555MediaServer.cpp中的main函数,可以知道程序执行env->taskScheduler().doEventLoop()后就一直在里面不停的执行,既是进入消息循环。
void BasicTaskScheduler0::doEventLoop(char* watchVariable) { // Repeatedly loop, handling readble sockets and timed events: while (1) { if (watchVariable != NULL && *watchVariable != 0) break; SingleStep(); } }
SingleStep函数中主要是socket的等待及事件的处理,总结为如下4步:
为所有需要的socket执行select;
找到第一个应执行的socke任务(handler)并执行值;
找到第一个应响应的事件,并执行值;
找到一个应执行的延迟任务并执行值;
在每一步中只会执行三个任务队列(socket任务、延迟任务,Event)中的一个。
DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) { int ourSocket = setUpOurSocket(env, ourPort); if (ourSocket == -1) return NULL; return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds); }
为了侦听客户端的连接,就需要利用任务调度机制,所以需添加一个socket handler。创建详细见RTSPServer类的构造函数。
RTSPServer::RTSPServer(UsageEnvironment& env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds)
当收到客户端的连接时,server会使用RTSPClientSession来保存客户信息。RTSPClientSession的创建见如下函数。
RTSPServer::incomingConnectionHandler(int serverSocket);
该函数内有一个设置传输包buffer大小的函数increaseSendBufferTo,在RTPInterface类中,传1080P码流时需要调大buffer,不然会数据丢失、花屏。
RTSSPClientSession需要监听客户端的rtsp请求并回应他,需要在DESCRIBE请求中返回所请求的流的信息,需要在SETUP请求中建立RTP会话,需要在TERDOWN请求中关闭RTP会话等,为了实现这些功能,RTSPClientSession要侦听客户端的请求,因此必须把自己的socket handler加入计划。
响应DESCRIBE请求函数(客户端连接请求)
void RTSPServer::RTSPClientConnection
::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr)
进行url解析、账户密码验证,ServerMediaSession查找或创建等。
该函数中的ServerMediaSession的建立规则:
RTSPClientSession在收到客户端的DESCRIBE请求时,根据地址中的媒体名字,去查找ServerMediaSession的列表。如果已有对应此媒体名字的ServerMediaSession。则利用它获取SDP信息。如果没有找到,则根据媒体名字中的扩展部分名部分,建立对应的ServerMediaSession。