我无意评价Live555的源码是否优雅易懂,但对于我这种C++设计模式应用不熟的IT老兵,还是很难直接通过阅读源码深刻清晰,一目了然的理解其中的调用逻辑。Live555中关于RTSP的Session,SubSession的概念,以及FramedSource和Sink的抽象都很不错的。但对于其任务单步调用机制,以及如何读取一帧数据及时发出一帧数据的全部逻辑, 真不容易得到清晰的处理逻辑。实践出真知,笔者本文就介绍一种通过运行时堆栈信息迅速理解关键代码和关键逻辑的方法。
先来观察本文尾部的GDB打印的堆栈信息,是笔者在实际开发中在Live555中扩展了mp4文件格式的支持,凡是ffmpeg_server目录下的的都是我的私有源码独立实现,不要刻意追究源码,本文教的是方法。该堆栈是Live555服务器接收到PLAY请求后,发送第1帧数据时的状态。
1.查看堆栈第27层到第0层堆栈,首先我们关注的是getNextFrame函数,该函数是FramedSource的public函数,提供给所有派生类构造层层回调逻辑的函数,通过传入几个回调函数和回调指针。该函数在第3层堆栈,第10层堆栈,第12层堆栈有调用到。
2.其次需要关注的是doGetNextFrame函数,该函数是FramedSource的虚函数,这个函数是在前面的getNextFrame的实现里被调用的。该函数在第2层,第9层,第11层有调用。这里要提醒一下,堆栈的函数调用顺序是由高到底层层调用进来,最后执行到当前的0层的。
抓住了这2个关键函数,然后通过观察(可以在编辑器里查找)传入这2个函数的指针(afterGettingClientData)和this指针的地址值,就会恍然大悟的。其他的我不多说了,通过指针值可以知道传入的是哪一层的对象的指针。
3. 另外一个函数是getNextFrame函数的传入参数,也即回调函数afterGettingFunc需要关注,该回调函数(一般是FramedSource或其派生类中的静态函数(声明为static))是在读取完一帧数据后回调的,跟第2点提到的afterGettingClientData指针配合使用的。
Live555的读取一帧,处理,发送一帧的逻辑就是通过以上3个函数的配套调用实现的。
最后还有一个逻辑,需要关注,这个逻辑在堆栈信息中没有,就是Live555在何时发送下一帧的呢? 答案是通过【定时器任务】驱动。可以在源码中搜索以下语句,发现调用的地方很稀少, 这就是发送下一帧的关键代码,我们牵住问题的牛鼻子了。
这个就是增加一个定时器的语句,有几个需要发送下一帧地方用到了以上调用,这也是Live555发送任务调度的关键所在。
本文堆栈所用到的发送下一帧的逻辑在void MultiFramedRTPSink::sendPacketIfNecessary()函数的尾部,语句如下:
nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);
#0 FfmpegDemux::ContinueReadProcessing (this=0x41a123d8) at ffmpeg_server/ffmpeg_demux.cpp:629
#1 0x000b27e4 in FfmpegDemux::GetNextFrame (this=0x41a123d8, stream_id=0 '\000', to=0x41d00008 "", max_size=2048000,
AfterGettingFunc=0xb639c <FfmpegDemuxedElementaryStream::AfterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>, after_getting_client_data=0x41a7c338, OnCloseFunc=0xd76a4 <FramedSource::handleClosure(void*)>,
on_close_client_data=0x41a7c338, frameHandlerFunc=0) at ffmpeg_server/ffmpeg_demux.cpp:407
#2 0x000b6210 in FfmpegDemuxedElementaryStream::doGetNextFrame (this=0x41a7c338)
at ffmpeg_server/ffmpeg_demuxed_elementary_stream.cpp:70
#3 0x000d7610 in FramedSource::getNextFrame (this=0x41a7c338, to=0x41d00008 "", maxSize=2048000,
afterGettingFunc=0x121254 <StreamParser::afterGettingBytes(void*, unsigned int, unsigned int, timeval, unsigned int)>,
afterGettingClientData=0x41a96a80, onCloseFunc=0x121434 <StreamParser::onInputClosure(void*)>, onCloseClientData=0x41a96a80)
at FramedSource.cpp:78
#4 0x00121228 in StreamParser::ensureValidBytes1 (this=0x41a96a80, numBytesNeeded=1024000) at StreamParser.cpp:159
#5 0x000da62c in StreamParser::ensureValidBytes (this=0x41a96a80, numBytesNeeded=4) at StreamParser.hh:118
#6 0x000da3d0 in StreamParser::test4Bytes (this=0x41a96a80) at StreamParser.hh:54
#7 0x000d9cb4 in H264VideoStreamParser::parse (this=0x41a96a80) at H264VideoStreamFramer.cpp:527
#8 0x0012bf7c in MPEGVideoStreamFramer::continueReadProcessing (this=0x41a96650) at MPEGVideoStreamFramer.cpp:154
#9 0x0012bf00 in MPEGVideoStreamFramer::doGetNextFrame (this=0x41a96650) at MPEGVideoStreamFramer.cpp:142
#10 0x000d7610 in FramedSource::getNextFrame (this=0x41a96650, to=0x42b34009 "", maxSize=900000,
afterGettingFunc=0x149e34 <H264FUAFragmenter::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>,
afterGettingClientData=0x41a4ead0, onCloseFunc=0xd76a4 <FramedSource::handleClosure(void*)>, onCloseClientData=0x41a4ead0)
at FramedSource.cpp:78
#11 0x00149aa4 in H264FUAFragmenter::doGetNextFrame (this=0x41a4ead0) at H264VideoRTPSink.cpp:219
#12 0x000d7610 in FramedSource::getNextFrame (this=0x41a4ead0, to=0x41b00014 "", maxSize=900644,
afterGettingFunc=0xe06f4 <MultiFramedRTPSink::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>,
afterGettingClientData=0x41a96820, onCloseFunc=0xe11bc <MultiFramedRTPSink::ourHandleClosure(void*)>,
onCloseClientData=0x41a96820) at FramedSource.cpp:78
#13 0x000e06e4 in MultiFramedRTPSink::packFrame (this=0x41a96820) at MultiFramedRTPSink.cpp:218
#14 0x000e0538 in MultiFramedRTPSink::buildAndSendPacket (this=0x41a96820, isFirstPacket=1 '\001') at MultiFramedRTPSink.cpp:193
#15 0x000e038c in MultiFramedRTPSink::continuePlaying (this=0x41a96820) at MultiFramedRTPSink.cpp:154
#16 0x001492f0 in H264VideoRTPSink::continuePlaying (this=0x41a96820) at H264VideoRTPSink.cpp:116
#17 0x000db1e8 in MediaSink::startPlaying (this=0x41a96820, source=..., afterFunc=0x10db88 <afterPlayingStreamState(void*)>,
afterClientData=0x41a969b8) at MediaSink.cpp:78
#18 0x0010e1b0 in StreamState::startPlaying (this=0x41a969b8, dests=0x41a4fcb8, rtcpRRHandler=
0xf0ce4 <RTSPServer::RTSPClientSession::noteClientLiveness(RTSPServer::RTSPClientSession*)>,
rtcpRRHandlerClientData=0x41a96548,
serverRequestAlternativeByteHandler=0xeb768 <RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void*, unsigned char)>, serverRequestAlternativeByteHandlerClientData=0x41a0d550) at OnDemandServerMediaSubsession.cpp:474
#19 0x0010d178 in OnDemandServerMediaSubsession::startStream (this=0x41a95a08, clientSessionId=2633508190, streamToken=0x41a969b8,
rtcpRRHandler=0xf0ce4 <RTSPServer::RTSPClientSession::noteClientLiveness(RTSPServer::RTSPClientSession*)>,
rtcpRRHandlerClientData=0x41a96548, rtpSeqNum=@0x40b1543e: 0, rtpTimestamp=@0x40b15438: 0,
serverRequestAlternativeByteHandler=0xeb768 <RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void*, unsigned char)>, serverRequestAlternativeByteHandlerClientData=0x41a0d550) at OnDemandServerMediaSubsession.cpp:201
#20 0x000f05f0 in RTSPServer::RTSPClientSession::handleCmd_PLAY (this=0x41a96548, ourClientConnection=0x41a0d550, subsession=0x0,
fullRequestStr=0x41a0d574 "PLAY rtsp://192.168.1.200/test.mp4/ RTSP/1.0\r\nCSeq: 6\r\nUser-Agent: LibVLC/2.2.1 (LIVE555 Streaming Media v2014.07.25)\r\nSession: 9CF8255E\r\nRange: npt=0.000-\r\n\r\n") at RTSPServer.cpp:1991
#21 0x000ef888 in RTSPServer::RTSPClientSession::handleCmd_withinSession (this=0x41a96548, ourClientConnection=0x41a0d550,
cmdName=0x40b15a28 "PLAY", urlPreSuffix=0x40b15960 "test.mp4", urlSuffix=0x40b15898 "",
fullRequestStr=0x41a0d574 "PLAY rtsp://192.168.1.200/test.mp4/ RTSP/1.0\r\nCSeq: 6\r\nUser-Agent: LibVLC/2.2.1 (LIVE555 Streaming Media v2014.07.25)\r\nSession: 9CF8255E\r\nRange: npt=0.000-\r\n\r\n") at RTSPServer.cpp:1794
#22 0x000ec67c in RTSPServer::RTSPClientConnection::handleRequestBytes (this=0x41a0d550, newBytesRead=159) at RTSPServer.cpp:1021
#23 0x000eb760 in RTSPServer::RTSPClientConnection::incomingRequestHandler1 (this=0x41a0d550) at RTSPServer.cpp:792
#24 0x000eb6f0 in RTSPServer::RTSPClientConnection::incomingRequestHandler (instance=0x41a0d550) at RTSPServer.cpp:785
#25 0x0016bbb4 in BasicTaskScheduler::SingleStep(unsigned int) ()
#26 0x0016d85c in BasicTaskScheduler0::doEventLoop(char*) ()
#27 0x000bea38 in rtsp_main (param=0x0) at ffmpeg_server/media_server.cpp:60