`

live555学习笔记-RTP打包与发送

 
阅读更多

RTP打包与发送

rtp传送开始于函数:MediaSink::startPlaying()。想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当于directshow的拉模式)。

看一下这个函数:

[cpp]view plaincopy
 
  1. BooleanMediaSink::startPlaying(MediaSource&source,
  2. afterPlayingFunc*afterFunc,void*afterClientData)
  3. {
  4. //参数afterFunc是在播放结束时才被调用。
  5. //Makesurewe'renotalreadybeingplayed:
  6. if(fSource!=NULL){
  7. envir().setResultMsg("Thissinkisalreadybeingplayed");
  8. returnFalse;
  9. }
  10. //Makesureoursourceiscompatible:
  11. if(!sourceIsCompatibleWithUs(source)){
  12. envir().setResultMsg(
  13. "MediaSink::startPlaying():sourceisnotcompatible!");
  14. returnFalse;
  15. }
  16. //记下一些要使用的对象
  17. fSource=(FramedSource*)&source;
  18. fAfterFunc=afterFunc;
  19. fAfterClientData=afterClientData;
  20. returncontinuePlaying();
  21. }

为了进一步封装(让继承类少写一些代码),搞出了一个虚函数continuePlaying()。让我们来看一下:

[cpp]view plaincopy
 
  1. BooleanMultiFramedRTPSink::continuePlaying(){
  2. //Sendthefirstpacket.
  3. //(Thiswillalsoscheduleanyfuturesends.)
  4. buildAndSendPacket(True);
  5. returnTrue;
  6. }

MultiFramedRTPSink是与帧有关的类,其实它要求每次必须从source获得一个帧的数据,所以才叫这个name。可以看到continuePlaying()完全被buildAndSendPacket()代替。看一下buildAndSendPacket():

[cpp]view plaincopy
 
  1. voidMultiFramedRTPSink::buildAndSendPacket(BooleanisFirstPacket)
  2. {
  3. //此函数中主要是准备rtp包的头,为一些需要跟据实际数据改变的字段留出位置。
  4. fIsFirstPacket=isFirstPacket;
  5. //SetuptheRTPheader:
  6. unsignedrtpHdr=0x80000000;//RTPversion2;marker('M')bitnotset(bydefault;itcanbesetlater)
  7. rtpHdr|=(fRTPPayloadType<<16);
  8. rtpHdr|=fSeqNo;//sequencenumber
  9. fOutBuf->enqueueWord(rtpHdr);//向包中加入一个字
  10. //NotewheretheRTPtimestampwillgo.
  11. //(Wecan'tfillthisinuntilwestartpackingpayloadframes.)
  12. fTimestampPosition=fOutBuf->curPacketSize();
  13. fOutBuf->skipBytes(4);//leaveaholeforthetimestamp 在缓冲中空出时间戳的位置
  14. fOutBuf->enqueueWord(SSRC());
  15. //Allowforaspecial,payload-format-specificheaderfollowingthe
  16. //RTPheader:
  17. fSpecialHeaderPosition=fOutBuf->curPacketSize();
  18. fSpecialHeaderSize=specialHeaderSize();
  19. fOutBuf->skipBytes(fSpecialHeaderSize);
  20. //Beginpackingasmany(complete)framesintothepacketaswecan:
  21. fTotalFrameSpecificHeaderSizes=0;
  22. fNoFramesLeft=False;
  23. fNumFramesUsedSoFar=0;//一个包中已打入的帧数。
  24. //头准备好了,再打包帧数据
  25. packFrame();
  26. }

继续看packFrame():

[cpp]view plaincopy
 
  1. voidMultiFramedRTPSink::packFrame()
  2. {
  3. //First,seeifwehaveanoverflowframethatwastoobigforthelastpkt
  4. if(fOutBuf->haveOverflowData()){
  5. //如果有帧数据,则使用之。OverflowData是指上次打包时剩下的帧数据,因为一个包可能容纳不了一个帧。
  6. //Usethisframebeforereadinganewonefromthesource
  7. unsignedframeSize=fOutBuf->overflowDataSize();
  8. structtimevalpresentationTime=fOutBuf->overflowPresentationTime();
  9. unsigneddurationInMicroseconds=fOutBuf->overflowDurationInMicroseconds();
  10. fOutBuf->useOverflowData();
  11. afterGettingFrame1(frameSize,0,presentationTime,durationInMicroseconds);
  12. }else{
  13. //一点帧数据都没有,跟source要吧。
  14. //Normalcase:weneedtoreadanewframefromthesource
  15. if(fSource==NULL)
  16. return;
  17. //更新缓冲中的一些位置
  18. fCurFrameSpecificHeaderPosition=fOutBuf->curPacketSize();
  19. fCurFrameSpecificHeaderSize=frameSpecificHeaderSize();
  20. fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);
  21. fTotalFrameSpecificHeaderSizes+=fCurFrameSpecificHeaderSize;
  22. //从source获取下一帧
  23. fSource->getNextFrame(fOutBuf->curPtr(),//新数据存放开始的位置
  24. fOutBuf->totalBytesAvailable(),//缓冲中空余的空间大小
  25. afterGettingFrame,//因为可能source中的读数据函数会被放在任务调度中,所以把获取帧后应调用的函数传给source
  26. this,
  27. ourHandleClosure,//这个是source结束时(比如文件读完了)要调用的函数。
  28. this);
  29. }
  30. }

可以想像下面就是source从文件(或某个设备)中读取一帧数据,读完后返回给sink,当然不是从函数返回了,而是以调用afterGettingFrame这个回调函数的方式。所以下面看一下afterGettingFrame():

[cpp]view plaincopy
 
  1. voidMultiFramedRTPSink::afterGettingFrame(void*clientData,
  2. unsignednumBytesRead,unsignednumTruncatedBytes,
  3. structtimevalpresentationTime,unsigneddurationInMicroseconds)
  4. {
  5. MultiFramedRTPSink*sink=(MultiFramedRTPSink*)clientData;
  6. sink->afterGettingFrame1(numBytesRead,numTruncatedBytes,presentationTime,
  7. durationInMicroseconds);
  8. }

没什么可看的,只是过度为调用成员函数,所以afterGettingFrame1()才是重点:

[cpp]view plaincopy
 
  1. voidMultiFramedRTPSink::afterGettingFrame1(
  2. unsignedframeSize,
  3. unsignednumTruncatedBytes,
  4. structtimevalpresentationTime,
  5. unsigneddurationInMicroseconds)
  6. {
  7. if(fIsFirstPacket){
  8. //Recordthefactthatwe'restartingtoplaynow:
  9. gettimeofday(&fNextSendTime,NULL);
  10. }
  11. //如果给予一帧的缓冲不够大,就会发生截断一帧数据的现象。但也只能提示一下用户
  12. if(numTruncatedBytes>0){
  13. unsignedconstbufferSize=fOutBuf->totalBytesAvailable();
  14. envir()
  15. <<"MultiFramedRTPSink::afterGettingFrame1():Theinputframedatawastoolargeforourbuffersize("
  16. <<bufferSize
  17. <<")."
  18. <<numTruncatedBytes
  19. <<"bytesoftrailingdatawasdropped!Correctthisbyincreasing\"OutPacketBuffer::maxSize\"toatleast"
  20. <<OutPacketBuffer::maxSize+numTruncatedBytes
  21. <<",*before*creatingthis'RTPSink'.(Currentvalueis"
  22. <<OutPacketBuffer::maxSize<<".)\n";
  23. }
  24. unsignedcurFragmentationOffset=fCurFragmentationOffset;
  25. unsignednumFrameBytesToUse=frameSize;
  26. unsignedoverflowBytes=0;
  27. //如果包只已经打入帧数据了,并且不能再向这个包中加数据了,则把新获得的帧数据保存下来。
  28. //Ifwehavealreadypackedoneormoreframesintothispacket,
  29. //checkwhetherthisnewframeiseligibletobepackedafterthem.
  30. //(Thisisindependentofwhetherthepackethasenoughroomforthis
  31. //newframe;thatcheckcomeslater.)
  32. if(fNumFramesUsedSoFar>0){
  33. //如果包中已有了一个帧,并且不允许再打入新的帧了,则只记录下新的帧。
  34. if((fPreviousFrameEndedFragmentation&&!allowOtherFramesAfterLastFragment())
  35. ||!frameCanAppearAfterPacketStart(fOutBuf->curPtr(),frameSize))
  36. {
  37. //Saveawaythisframefornexttime:
  38. numFrameBytesToUse=0;
  39. fOutBuf->setOverflowData(fOutBuf->curPacketSize(),frameSize,
  40. presentationTime,durationInMicroseconds);
  41. }
  42. }
  43. //表示当前打入的是否是上一个帧的最后一块数据。
  44. fPreviousFrameEndedFragmentation=False;
  45. //下面是计算获取的帧中有多少数据可以打到当前包中,剩下的数据就作为overflow数据保存下来。
  46. if(numFrameBytesToUse>0){
  47. //Checkwhetherthisframeoverflowsthepacket
  48. if(fOutBuf->wouldOverflow(frameSize)){
  49. //Don'tusethisframenow;instead,saveitasoverflowdata,and
  50. //senditinthenextpacketinstead.However,iftheframeistoo
  51. //bigtofitinapacketbyitself,thenweneedtofragmentit(and
  52. //usesomeofitinthispacket,ifthepayloadformatpermitsthis.)
  53. if(isTooBigForAPacket(frameSize)
  54. &&(fNumFramesUsedSoFar==0||allowFragmentationAfterStart())){
  55. //Weneedtofragmentthisframe,andusesomeofitnow:
  56. overflowBytes=computeOverflowForNewFrame(frameSize);
  57. numFrameBytesToUse-=overflowBytes;
  58. fCurFragmentationOffset+=numFrameBytesToUse;
  59. }else{
  60. //Wedon'tuseanyofthisframenow:
  61. overflowBytes=frameSize;
  62. numFrameBytesToUse=0;
  63. }
  64. fOutBuf->setOverflowData(fOutBuf->curPacketSize()+numFrameBytesToUse,
  65. overflowBytes,presentationTime,durationInMicroseconds);
  66. }elseif(fCurFragmentationOffset>0){
  67. //Thisisthelastfragmentofaframethatwasfragmentedover
  68. //morethanonepacket.Doanyspecialhandlingforthiscase:
  69. fCurFragmentationOffset=0;
  70. fPreviousFrameEndedFragmentation=True;
  71. }
  72. }
  73. if(numFrameBytesToUse==0&&frameSize>0){
  74. //如果包中有数据并且没有新数据了,则发送之。(这种情况好像很难发生啊!)
  75. //Sendourpacketnow,becausewehavefilleditup:
  76. sendPacketIfNecessary();
  77. }else{
  78. //需要向包中打入数据。
  79. //Usethisframeinouroutgoingpacket:
  80. unsignedchar*frameStart=fOutBuf->curPtr();
  81. fOutBuf->increment(numFrameBytesToUse);
  82. //dothisnow,incase"doSpecialFrameHandling()"calls"setFramePadding()"toappendpaddingbytes
  83. //Here'swhereanypayloadformatspecificprocessinggetsdone:
  84. doSpecialFrameHandling(curFragmentationOffset,frameStart,
  85. numFrameBytesToUse,presentationTime,overflowBytes);
  86. ++fNumFramesUsedSoFar;
  87. //Updatethetimeatwhichthenextpacketshouldbesent,based
  88. //onthedurationoftheframethatwejustpackedintoit.
  89. //However,ifthisframehasoverflowdataremaining,thendon't
  90. //countitsdurationyet.
  91. if(overflowBytes==0){
  92. fNextSendTime.tv_usec+=durationInMicroseconds;
  93. fNextSendTime.tv_sec+=fNextSendTime.tv_usec/1000000;
  94. fNextSendTime.tv_usec%=1000000;
  95. }
  96. //如果需要,就发出包,否则继续打入数据。
  97. //Sendourpacketnowif(i)it'salreadyatourpreferredsize,or
  98. //(ii)(heuristic)anotherframeofthesamesizeastheonewejust
  99. //readwouldoverflowthepacket,or
  100. //(iii)itcontainsthelastfragmentofafragmentedframe,andwe
  101. //don'tallowanythingelsetofollowthisor
  102. //(iv)oneframeperpacketisallowed:
  103. if(fOutBuf->isPreferredSize()
  104. ||fOutBuf->wouldOverflow(numFrameBytesToUse)
  105. ||(fPreviousFrameEndedFragmentation
  106. &&!allowOtherFramesAfterLastFragment())
  107. ||!frameCanAppearAfterPacketStart(
  108. fOutBuf->curPtr()-frameSize,frameSize)){
  109. //Thepacketisreadytobesentnow
  110. sendPacketIfNecessary();
  111. }else{
  112. //There'sroomformoreframes;trygettinganother:
  113. packFrame();
  114. }
  115. }
  116. }


看一下发送数据的函数:

[cpp]view plaincopy
 
  1. voidMultiFramedRTPSink::sendPacketIfNecessary()
  2. {
  3. //发送包
  4. if(fNumFramesUsedSoFar>0){
  5. //Sendthepacket:
  6. #ifdefTEST_LOSS
  7. if((our_random()%10)!=0)//simulate10%packetloss#####
  8. #endif
  9. if(!fRTPInterface.sendPacket(fOutBuf->packet(),fOutBuf->curPacketSize())){
  10. //iffailurehandlerhasbeenspecified,callit
  11. if(fOnSendErrorFunc!=NULL)
  12. (*fOnSendErrorFunc)(fOnSendErrorData);
  13. }
  14. ++fPacketCount;
  15. fTotalOctetCount+=fOutBuf->curPacketSize();
  16. fOctetCount+=fOutBuf->curPacketSize()-rtpHeaderSize
  17. -fSpecialHeaderSize-fTotalFrameSpecificHeaderSizes;
  18. ++fSeqNo;//fornexttime
  19. }
  20. //如果还有剩余数据,则调整缓冲区
  21. if(fOutBuf->haveOverflowData()
  22. &&fOutBuf->totalBytesAvailable()>fOutBuf->totalBufferSize()/2){
  23. //Efficiencyhack:Resetthepacketstartpointertojustinfrontof
  24. //theoverflowdata(allowingfortheRTPheaderandspecialheaders),
  25. //sothatweprobablydon'thaveto"memmove()"theoverflowdata
  26. //intoplacewhenbuildingthenextpacket:
  27. unsignednewPacketStart=fOutBuf->curPacketSize()-
  28. (rtpHeaderSize+fSpecialHeaderSize+frameSpecificHeaderSize());
  29. fOutBuf->adjustPacketStart(newPacketStart);
  30. }else{
  31. //Normalcase:Resetthepacketstartpointerbacktothestart:
  32. fOutBuf->resetPacketStart();
  33. }
  34. fOutBuf->resetOffset();
  35. fNumFramesUsedSoFar=0;
  36. if(fNoFramesLeft){
  37. //如果再没有数据了,则结束之
  38. //We'redone:
  39. onSourceClosure(this);
  40. }else{
  41. //如果还有数据,则在下一次需要发送的时间再次打包发送。
  42. //Wehavemoreframeslefttosend.Figureoutwhenthenextframe
  43. //isduetostartplaying,thenmakesurethatwewaitthislongbefore
  44. //sendingthenextpacket.
  45. structtimevaltimeNow;
  46. gettimeofday(&timeNow,NULL);
  47. intsecsDiff=fNextSendTime.tv_sec-timeNow.tv_sec;
  48. int64_tuSecondsToGo=secsDiff*1000000
  49. +(fNextSendTime.tv_usec-timeNow.tv_usec);
  50. if(uSecondsToGo<0||secsDiff<0){//sanitycheck:Makesurethatthetime-to-delayisnon-negative:
  51. uSecondsToGo=0;
  52. }
  53. //Delaythisamountoftime:
  54. nextTask()=envir().taskScheduler().scheduleDelayedTask(uSecondsToGo,
  55. (TaskFunc*)sendNext,this);
  56. }
  57. }


可以看到为了延迟包的发送,使用了delay task来执行下次打包发送任务。

sendNext()中又调用了buildAndSendPacket()函数,呵呵,又是一个圈圈。

总结一下调用过程:

最后,再说明一下包缓冲区的使用:

MultiFramedRTPSink中的帧数据和包缓冲区共用一个,只是用一些额外的变量指明缓冲区中属于包的部分以及属于帧数据的部分(包以外的数据叫做overflow data)。它有时会把overflow data以mem move的方式移到包开始的位置,有时把包的开始位置直接设置到overflow data开始的地方。那么这个缓冲的大小是怎样确定的呢?是跟据调用者指定的的一个最大的包的大小+60000算出的。这个地方把我搞胡涂了:如果一次从source获取一个帧的话,那这个缓冲应设为不小于最大的一个帧的大小才是,为何是按包的大小设置呢?可以看到,当缓冲不够时只是提示一下:

[cpp]view plaincopy
 
  1. if(numTruncatedBytes>0){
  2. unsignedconstbufferSize=fOutBuf->totalBytesAvailable();
  3. envir()
  4. <<"MultiFramedRTPSink::afterGettingFrame1():Theinputframedatawastoolargeforourbuffersize("
  5. <<bufferSize
  6. <<")."
  7. <<numTruncatedBytes
  8. <<"bytesoftrailingdatawasdropped!Correctthisbyincreasing\"OutPacketBuffer::maxSize\"toatleast"
  9. <<OutPacketBuffer::maxSize+numTruncatedBytes
  10. <<",*before*creatingthis'RTPSink'.(Currentvalueis"
  11. <<OutPacketBuffer::maxSize<<".)\n";
  12. }

当然此时不会出错,但有可能导致时间戳计算不准,或增加时间戳计算与source端处理的复杂性(因为一次取一帧时间戳是很好计算的)。

 

原文地址:http://blog.csdn.net/niu_gao/article/details/6921145

live555源代码(VC6):http://download.csdn.net/detail/leixiaohua1020/6374387

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics