live555從RTSP服務器讀取數據到使用接收到的數據流程分析


進入testProgs目錄,執行./openRTSP rtsp://xxxx/test.mp4

對於RTSP協議的處理部分,可設置斷點在setupStreams函數中,並跟蹤即可進行分析。

這裏主要分析進入如下的while(1)循環中的代碼

  1. void BasicTaskScheduler0::doEventLoop(char* watchVariable)   
  2. {  
  3.   // Repeatedly loop, handling readble sockets and timed events:  
  4.   while (1)   
  5.   {  
  6.     if (watchVariable != NULL && *watchVariable != 0) break;  
  7.     SingleStep();  
  8.   }  
  9. }  

 

從這裏可知,live555在客戶端處理數據實際上是單線程的程序,不斷執行SingleStep()函數中的代碼。通過查看該函數代碼裏,下面一句代碼爲重點

  1. (*handler->handlerProc)(handler->clientData, resultConditionSet);  


 

其中該條代碼出現了兩次,通過調試跟蹤它的執行軌跡,第一次出現調用的函數是爲了處理和RTSP服務器的通信協議的商定,而第二次出現調用的函數纔是處理真正的視頻和音頻數據。對於RTSP通信協議的分析我們暫且不討論,而直接進入第二次調用該函數的部分。

在我們的調試過程中在執行到上面的函數時就直接調用到livemedia目錄下的如下函數

 

  1. void MultiFramedRTPSource::networkReadHandler(MultiFramedRTPSource* source, int /*mask*/)   
  2. {  
  3.   source->networkReadHandler1();  
  4. }  



//下面這個函數實現的主要功能就是從socket端讀取數據並存儲數據

 

  1. void MultiFramedRTPSource::networkReadHandler1()   
  2. {  
  3.   BufferedPacket* bPacket = fPacketReadInProgress;  
  4.   if (bPacket == NULL)  
  5.   {  
  6.     // Normal case: Get a free BufferedPacket descriptor to hold the new network packet:  
  7.     //分配一塊新的存儲空間來存儲從socket端讀取的數據  
  8.     bPacket = fReorderingBuffer->getFreePacket(this);  
  9.   }  
  10.   
  11.   // Read the network packet, and perform sanity checks on the RTP header:  
  12.   Boolean readSuccess = False;  
  13.   do   
  14.   {  
  15.     Boolean packetReadWasIncomplete = fPacketReadInProgress != NULL;  
  16.     //fillInData()函數封裝了從socket端獲取數據的過程,到此函數執行完已經將數據保存到了bPacket對象中  
  17.     if (!bPacket->fillInData(fRTPInterface, packetReadWasIncomplete))   
  18.    {  
  19.       if (bPacket->bytesAvailable() == 0)   
  20.       {  
  21.       envir() << "MultiFramedRTPSource error: Hit limit when reading incoming packet over TCP. Increase \"MAX_PACKET_SIZE\"\n";  
  22.       }  
  23.       break;  
  24.    }  
  25.     if (packetReadWasIncomplete)  
  26.     {  
  27.       // We need additional read(s) before we can process the incoming packet:  
  28.       fPacketReadInProgress = bPacket;  
  29.       return;  
  30.     } else   
  31.     {  
  32.       fPacketReadInProgress = NULL;  
  33.     }  
  34.       
  35.     //省略關於RTP包的處理  
  36.     ...  
  37.     ...  
  38.     ...  
  39.     //fReorderingBuffer爲MultiFramedRTPSource類中的對象,該對象建立了一個存儲Packet數據包對象的鏈表  
  40.     //下面的storePacket()函數即將上面獲取的數據包存儲在鏈表中  
  41.     if (!fReorderingBuffer->storePacket(bPacket)) break;   
  42.   
  43.     readSuccess = True;  
  44.   } while (0);  
  45.   if (!readSuccess) fReorderingBuffer->freePacket(bPacket);  
  46.   
  47.   doGetNextFrame1();  
  48.   // If we didn't get proper data this time, we'll get another chance  
  49. }  


 

//下面的這個函數則實現從上面函數中介紹的存儲數據包鏈表的對象(即fReorderingBuffer)中取出數據包並調用相應函數使用它

//代碼1.1

 

  1. void MultiFramedRTPSource::doGetNextFrame1()   
  2. {  
  3.   while (fNeedDelivery)   
  4.   {  
  5.     // If we already have packet data available, then deliver it now.  
  6.     Boolean packetLossPrecededThis;   
  7.     //從fReorderingBuffer對象中取出一個數據包  
  8.     BufferedPacket* nextPacket  
  9.       = fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);  
  10.     if (nextPacket == NULL) break;  
  11.   
  12.     fNeedDelivery = False;  
  13.   
  14.     if (nextPacket->useCount() == 0)   
  15.     {  
  16.       // Before using the packet, check whether it has a special header  
  17.       // that needs to be processed:  
  18.       unsigned specialHeaderSize;  
  19.       if (!processSpecialHeader(nextPacket, specialHeaderSize))  
  20.       {  
  21.         // Something's wrong with the header; reject the packet:  
  22.         fReorderingBuffer->releaseUsedPacket(nextPacket);  
  23.         fNeedDelivery = True;  
  24.         break;  
  25.       }  
  26.       nextPacket->skip(specialHeaderSize);  
  27.     }  
  28.   
  29.     // Check whether we're part of a multi-packet frame, and whether  
  30.     // there was packet loss that would render this packet unusable:  
  31.     if (fCurrentPacketBeginsFrame)   
  32.     {  
  33.       if (packetLossPrecededThis || fPacketLossInFragmentedFrame)   
  34.       {  
  35.         // We didn't get all of the previous frame.  
  36.         // Forget any data that we used from it:  
  37.         fTo = fSavedTo; fMaxSize = fSavedMaxSize;  
  38.         fFrameSize = 0;  
  39.       }  
  40.       fPacketLossInFragmentedFrame = False;  
  41.     } else if (packetLossPrecededThis)   
  42.     {  
  43.       // We're in a multi-packet frame, with preceding packet loss  
  44.       fPacketLossInFragmentedFrame = True;  
  45.     }  
  46.     if (fPacketLossInFragmentedFrame)  
  47.     {  
  48.       // This packet is unusable; reject it:  
  49.       fReorderingBuffer->releaseUsedPacket(nextPacket);  
  50.       fNeedDelivery = True;  
  51.       break;  
  52.     }  
  53.   
  54.     // The packet is usable. Deliver all or part of it to our caller:  
  55.     unsigned frameSize;  
  56.     //將上面取出的數據包拷貝到fTo指針所指向的地址  
  57.     nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,  
  58.             fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,  
  59.             fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,  
  60.             fCurPacketMarkerBit);  
  61.     fFrameSize += frameSize;  
  62.   
  63.     if (!nextPacket->hasUsableData())   
  64.     {  
  65.       // We're completely done with this packet now  
  66.       fReorderingBuffer->releaseUsedPacket(nextPacket);  
  67.     }  
  68.   
  69.     if (fCurrentPacketCompletesFrame) //如果完整的取出了一幀數據,則可調用需要該幀數據的函數去處理它  
  70.      {  
  71.       // We have all the data that the client wants.  
  72.       if (fNumTruncatedBytes > 0)   
  73.       {  
  74.     envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size ("  
  75.         << fSavedMaxSize << ").  "  
  76.         << fNumTruncatedBytes << " bytes of trailing data will be dropped!\n";  
  77.       }  
  78.       // Call our own 'after getting' function, so that the downstream object can consume the data:  
  79.       if (fReorderingBuffer->isEmpty())   
  80.       {  
  81.         // Common case optimization: There are no more queued incoming packets, so this code will not get  
  82.         // executed again without having first returned to the event loop.  Call our 'after getting' function  
  83.         // directly, because there's no risk of a long chain of recursion (and thus stack overflow):  
  84.     afterGetting(this);  //調用函數去處理取出的數據幀  
  85.        } else   
  86.       {  
  87.     // Special case: Call our 'after getting' function via the event loop.  
  88.     nextTask() = envir().taskScheduler().scheduleDelayedTask(0,  
  89.                                  (TaskFunc*)FramedSource::afterGetting, this);  
  90.       }  
  91.     }  
  92.     else       
  93.     {  
  94.       // This packet contained fragmented data, and does not complete  
  95.       // the data that the client wants.  Keep getting data:  
  96.       fTo += frameSize; fMaxSize -= frameSize;  
  97.       fNeedDelivery = True;  
  98.     }  
  99.   }  
  100. }  


//下面這個函數即開始調用執行需要該幀數據的函數

  1. void FramedSource::afterGetting(FramedSource* source)   
  2. {  
  3.   source->fIsCurrentlyAwaitingData = False;  
  4.       // indicates that we can be read again  
  5.       // Note that this needs to be done here, in case the "fAfterFunc"  
  6.       // called below tries to read another frame (which it usually will)  
  7.   
  8.   if (source->fAfterGettingFunc != NULL)     
  1.   {  
  2.     (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,  
  3.                    source->fFrameSize, source->fNumTruncatedBytes,  
  4.                    source->fPresentationTime,  
  5.                    source->fDurationInMicroseconds);  
  6.   }  
  7. }  


 

上面的fAfterGettingFunc爲我們自己註冊的函數,如果運行的是testProgs中的openRTSP實例,則該函數指向下列代碼中通過調用getNextFrame()註冊的afterGettingFrame()函數

  1. Boolean FileSink::continuePlaying()  
  2. {  
  3.   if (fSource == NULL) return False;  
  4.   
  5.   fSource->getNextFrame(fBuffer, fBufferSize,  
  6.             afterGettingFrame, this,  
  7.             onSourceClosure, this);  
  8.   
  9.   return True;  
  10. }  


如果運行的是testProgs中的testRTSPClient中的實例,則該函數指向這裏註冊的afterGettingFrame()函數

 

  1. Boolean DummySink::continuePlaying()  
  2. {  
  3.   if (fSource == NULL) return False; // sanity check (should not happen)  
  4.   
  5.   // Request the next frame of data from our input source.  "afterGettingFrame()" will get called later, when it arrives:  
  6.   fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,  
  7.                         afterGettingFrame, this,  
  8.                         onSourceClosure, this);  
  9.   return True;  
  10. }  


 

從上面的代碼中可以看到getNextFrame()函數的第一個參數爲分別在各自類中定義的buffer,我們繼續以openRTSP爲運行程序來分析,fBuffer爲FileSink類裏定義的指針:unsigned char* fBuffer;

這裏我們先繞一個彎,看看getNextFrame()函數裏做了什麼

 

  1. void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,  
  2.                 afterGettingFunc* afterGettingFunc,  
  3.                 void* afterGettingClientData,  
  4.                 onCloseFunc* onCloseFunc,  
  5.                 void* onCloseClientData)   
  6. {  
  7.   // Make sure we're not already being read:  
  8.   if (fIsCurrentlyAwaitingData)     
  9.   {  
  10.     envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";  
  11.     envir().internalError();  
  12.   }  
  13.   
  14.   fTo = to;  
  15.   fMaxSize = maxSize;  
  16.   fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()  
  17.   fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()  
  18.   fAfterGettingFunc = afterGettingFunc;  
  19.   fAfterGettingClientData = afterGettingClientData;  
  20.   fOnCloseFunc = onCloseFunc;  
  21.   fOnCloseClientData = onCloseClientData;  
  22.   fIsCurrentlyAwaitingData = True;  
  23.   
  24.   doGetNextFrame();  
  25. }  


 

從代碼可以知道上面getNextFrame()中傳入的第一個參數fBuffer指向了指針fTo,而我們在前面分析代碼1.1中的void MultiFramedRTPSource::doGetNextFrame1()函數中有下面一段代碼:

  1. //將上面取出的數據包拷貝到fTo指針所指向的地址  
  2.  nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,  
  3.    fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,  
  4.    fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,  
  5.    fCurPacketMarkerBit);  


實際上現在應該明白了,從getNextFrame()函數中傳入的第一個參數fBuffer最終存儲的即是從數據包鏈表對象中取出的數據,並且在調用上面的use()函數後就可以使用了。
而在void MultiFramedRTPSource::doGetNextFrame1()函數中代碼顯示的最終調用我們註冊的void FileSink::afterGettingFrame()正好是在use()函數調用之後的afterGetting(this)中調用。我們再看看afterGettingFrame()做了什麼處理:

 

  1. void FileSink::afterGettingFrame(void* clientData, unsigned frameSize,  
  2.                  unsigned numTruncatedBytes,  
  3.                  struct timeval presentationTime,  
  4.                  unsigned /*durationInMicroseconds*/)  
  5. {  
  6.   FileSink* sink = (FileSink*)clientData;  
  7.   sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);  
  8. }  
  9.   
  10. void FileSink::afterGettingFrame(unsigned frameSize,  
  11.                  unsigned numTruncatedBytes,  
  12.                  struct timeval presentationTime)   
  13. {  
  14.   if (numTruncatedBytes > 0)     
  15.   {  
  16.     envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size ("  
  17.         << fBufferSize << ").  "  
  18.             << numTruncatedBytes << " bytes of trailing data was dropped!  Correct this by increasing the \"bufferSize\" parameter in the \"createNew()\" call to at least "  
  19.             << fBufferSize + numTruncatedBytes << "\n";  
  20.   }  
  21.   addData(fBuffer, frameSize, presentationTime);  
  22.   
  23.   if (fOutFid == NULL || fflush(fOutFid) == EOF)     
  24.   {  
  25.     // The output file has closed.  Handle this the same way as if the  
  26.     // input source had closed:  
  27.     onSourceClosure(this);  
  28.   
  29.     stopPlaying();  
  30.     return;  
  31.   }  
  32.   
  33.   if (fPerFrameFileNameBuffer != NULL)     
  34.   {  
  35.     if (fOutFid != NULL) { fclose(fOutFid); fOutFid = NULL; }  
  36.   }  
  37.   
  38.   // Then try getting the next frame:  
  39.   continuePlaying();  
  40. }  


從上面代碼可以看到調用了addData()函數將數據保存到文件中,然後繼續continuePlaying()又去獲取下一幀數據然後處理,直到遇到循環結束然後依次退出調用函數。最後看看addData()函數的實現即可知:

 

  1. void FileSink::addData(unsigned char const* data, unsigned dataSize,  
  2.                struct timeval presentationTime)   
  3. {  
  4.   if (fPerFrameFileNameBuffer != NULL)     
  5.   {  
  6.     // Special case: Open a new file on-the-fly for this frame  
  7.     sprintf(fPerFrameFileNameBuffer, "%s-%lu.%06lu", fPerFrameFileNamePrefix,  
  8.         presentationTime.tv_sec, presentationTime.tv_usec);  
  9.     fOutFid = OpenOutputFile(envir(), fPerFrameFileNameBuffer);  
  10.   }  
  11.   
  12.   // Write to our file:  
  13. #ifdef TEST_LOSS  
  14.   static unsigned const framesPerPacket = 10;  
  15.   static unsigned const frameCount = 0;  
  16.   static Boolean const packetIsLost;  
  17.   if ((frameCount++)%framesPerPacket == 0)     
  18.   {  
  19.     packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss #####  
  20.   }  
  21.   
  22.   if (!packetIsLost)  
  23. #endif  
  24.   if (fOutFid != NULL && data != NULL)    
  25.   {  
  26.     fwrite(data, 1, dataSize, fOutFid);  
  27.   }  
  28. }  


最後調用系統函數fwrite()實現寫入文件功能。

總結:從上面的分析可知,如果要取得從RTSP服務器端接收並保存的數據幀,我們只需要定義一個類並實現如下格式兩個的函數,並聲明一個指針地址buffer用於指向數據幀,再在continuePlaying()函數中調用getNextFrame(buffer,...)即可。

  1. typedef void (afterGettingFunc)(void* clientData, unsigned frameSize,  
  2.           unsigned numTruncatedBytes,  
  3.           struct timeval presentationTime,  
  4.           unsigned durationInMicroseconds);  
  5. typedef void (onCloseFunc)(void* clientData);  


然後再在afterGettingFunc的函數中即可使用buffer。.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章