Live555源代碼解讀(9)

十 、h264 RTP傳輸詳解(2)session

上一章並無把打開文件分析文件的代碼找到,由於發現它隱藏得比較深,並且H264的Source又有多個,造成了連環計。因此此章中就將文件處理與H264的Source們並在一塊兒分析吧。
從哪裏開始呢?從source開始吧!爲何要從它開始呢?我就想從這裏開始,行了吧?app

[cpp] view plaincopy框架

  1. FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(  less

  2.         unsigned /*clientSessionId*/,  ide

  3.         unsigned& estBitrate)  函數

  4. {  ui

  5.     estBitrate = 500; // kbps, estimate  this

  6.   

  7.     // Create the video source:  spa

  8.     ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(),  .net

  9.             fFileName);  

  10.     if (fileSource == NULL)  

  11.         return NULL;  

  12.     fFileSize = fileSource->fileSize();  

  13.   

  14.     // Create a framer for the Video Elementary Stream:  

  15.     return H264VideoStreamFramer::createNew(envir(), fileSource);  

  16. }  

先建立一個ByteStreamFileSource,顯然這是一個從文件按字節讀取數據的source,沒什麼可細說的。可是,打開文件,讀寫文件操做的確就在其中。最終來處理h264文件,分析其格式,解析出幀或nal的應是這個source: H264VideoStreamFramer。打開文件的地方找到了,但分析文件的代碼纔是更有價值的。那咱們只能來看H264VideoStreamFramer。
H264VideoStreamFramer繼承自MPEGVideoStreamFramer,MPEGVideoStreamFramer繼承自FramedFilter,FramedFilter繼承自FramedSource。
啊,中間又冒出個Filter。看到它,是否是聯想到了DirectShow的filter?或者說Photoshop中的filter?它們的意義應該都差很少吧?即插入到source和render(sink)之間的處理媒體數據的東東?若是這樣理解,仍是更接近於photoshop中的概念。唉,說實話,我估計本身說的也不全對,反正就這樣認識吧,謬不了一千里。既然咱們這樣認識了,那麼咱們就有理由相信可能會出現多個filter們一個連一個,而後高唱:手牽着腳腳牽着手一塊兒向前走...
H264VideoStreamFramer繼承自MPEGVideoStreamFramer,MPEGVideoStreamFramer比較簡單,它只是把一些工做交給了MPEGVideoStreamParser(又出來個parser,這但是個新東西哦,先不要管它吧),重點來看一下。
構造函數:

[cpp] view plaincopy

  1. H264VideoStreamFramer::H264VideoStreamFramer(UsageEnvironment& env,  

  2.         FramedSource* inputSource,  

  3.         Boolean createParser,  

  4.         Boolean includeStartCodeInOutput)  

  5.         : MPEGVideoStreamFramer(env, inputSource),  

  6.                 fIncludeStartCodeInOutput(includeStartCodeInOutput),  

  7.                 fLastSeenSPS(NULL),  

  8.                 fLastSeenSPSSize(0),  

  9.                 fLastSeenPPS(NULL),  

  10.                 fLastSeenPPSSize(0)  

  11. {  

  12.     fParser = createParser ?  

  13.             new H264VideoStreamParser(this, inputSource,  

  14.                     includeStartCodeInOutput) : NULL;  

  15.     fNextPresentationTime = fPresentationTimeBase;  

  16.     fFrameRate = 25.0; // We assume a frame rate of 25 fps,   

  17.         //unless we learn otherwise (from parsing a Sequence Parameter Set NAL unit)  

  18. }  

因爲createParser確定爲真,因此主要內容是建立了H264VideoStreamParser對象(先無論這個parser)。
其它的函數就沒什麼可看的了,都集中在所保存的PPS與SPS上。看來分析工做移到了H264VideoStreamParser,Parser嘛,就是分析器。分析器的基類是StreamParser。StreamParser作了很多的工做,那咱們就先搞明白StreamParser作了哪些工做吧,而且可能爲繼承者提供什麼樣的調用框架呢?.....我看完了,呵呵。直接說分析結果吧:
StreamParser的主要工做是實現了對數據以位爲單位進行訪問。由於在處理媒體格式時,按位分析是很常見的狀況。這兩個函數skipBits(unsigned numBits)和unsigned getBits(unsigned numBits)很明顯是基於位的操做。unsigned char* fBank[2]這個變量中的兩個緩衝區被輪換使用。這個類中保存了一個Source,理所固然地它應該保存ByteStreamFileSource的實例,而不是FramedFilter的。那些getBytes()或getBits()最終會致使讀文件的操做。從文件讀取一次數據後,StreamParser::afterGettingBytes1()被調用,StreamParser::afterGettingBytes1()中作一點簡單的工做後便調用fClientContinueFunc這個回調函數。fClientContinueFunc可能指向Frame的函數體也多是指向RtpSink的函數體--由於Framer徹底能夠把RtpSink的函數體傳給Parser。至於到底指向哪一個,只能在進一步分析以後才得知。

下面再來分析StreamParser的兒子:MPEGVideoStreamParser。

[cpp] view plaincopy

  1. MPEGVideoStreamParser::MPEGVideoStreamParser(  

  2.         MPEGVideoStreamFramer* usingSource,  

  3.         FramedSource* inputSource)  

  4.         : StreamParser(inputSource,  

  5.             FramedSource::handleClosure,   

  6.             usingSource,  

  7.             &MPEGVideoStreamFramer::continueReadProcessing,  

  8.             usingSource),  

  9.             fUsingSource(usingSource)  

  10. {  

  11. }  

MPEGVideoStreamParser的構造函數中有不少有意思的東西。
首先參數usingSource是什麼意思?表示正在使用這個Parser的Source? inputSource 很明確,就是能獲取數據的source,也就是 ByteStreamFileSource。並且很明顯的,StreamParser中保存的source是ByteStreamFileSource。從傳入給StreamParser的回調函數以及它們的參數能夠看出,這些回調函數全是指向的StreamParser的子類的函數(爲啥不用虛函數的方式?哦,回調函數全是靜態函數,不能成爲虛函數)。這說明在每讀一次數據後,MPEGVideoStreamFramer::continueReadProcessing()被調用,在其中對幀進行界定和分析,完成後再調用RTPSink的相應函數,RTPSink中對幀進行打包和發送(還記得嗎,不記得了請回頭看之前的章節)。
MPEGVideoStreamParser的fTo是RTPSink傳入的緩衝指針,其saveByte(),save4Bytes()是把數據從StreamParser的緩衝把數據複製到fTo中,是給繼承類使用的。saveToNextCode()是複製數據直到遇到一個同步字節串(好比h264中分隔nal的那一陀東東,固然此處的跟h264還不同),也是給繼承類使用的。純虛函數parse()很明顯是留繼承類去寫幀分析代碼的地方。registerReadInterest()被調用者用來告訴MPEGVideoStreamParser其接收幀的緩衝地址與容量。
如今應該來分析一下MPEGVideoStreamFramer,以明確MPEGVideoStreamFramer與MPEGVideoStreamParser是怎樣配合的。
MPEGVideoStreamFramer中用到Parser的重要的函數只有兩個,一是:

[cpp] view plaincopy

  1. void MPEGVideoStreamFramer::doGetNextFrame()  

  2. {  

  3.     fParser->registerReadInterest(fTo, fMaxSize);  

  4.     continueReadProcessing();  

  5. }  

很簡單,只是告訴了Parser保存幀的緩衝和緩衝的大小,而後執行continueReadProcessing(),那麼來看一下continueReadProcessing():

[cpp] view plaincopy

  1. void MPEGVideoStreamFramer::continueReadProcessing()  

  2. {  

  3.     unsigned acquiredFrameSize = fParser->parse();  

  4.     if (acquiredFrameSize > 0) {  

  5.         // We were able to acquire a frame from the input.  

  6.         // It has already been copied to the reader's space.  

  7.         fFrameSize = acquiredFrameSize;  

  8.         fNumTruncatedBytes = fParser->numTruncatedBytes();  

  9.   

  10.         // "fPresentationTime" should have already been computed.  

  11.   

  12.         // Compute "fDurationInMicroseconds" now:  

  13.         fDurationInMicroseconds =  

  14.                 (fFrameRate == 0.0 || ((int) fPictureCount) < 0) ?  

  15.                         0 : (unsigned) ((fPictureCount * 1000000) / fFrameRate);  

  16.         fPictureCount = 0;  

  17.   

  18.         // Call our own 'after getting' function.  Because we're not a 'leaf'  

  19.         // source, we can call this directly, without risking infinite recursion.  

  20.         afterGetting(this);  

  21.     } else {  

  22.         // We were unable to parse a complete frame from the input, because:  

  23.         // - we had to read more data from the source stream, or  

  24.         // - the source stream has ended.  

  25.     }  

  26. }  

先利用Parser進行分析(應該是解析出一幀吧?),分析完成後,幀數據已到了MPEGVideoStreamFramer的緩衝fTo中。計算出幀的持續時間後,調用FrameSource的afterGetting(),最終會調用RTPSink的函數。
看到這裏,能夠總結一下,其實看來看去,Parser直正被外部使用的函數幾乎只有一個:parse()。
下面能夠看H264VideoStreamParser了。其實也很簡單,多了一些用於分析h264格式的函數,固然是非公開的,只供本身使用的。在哪裏使用呢?固然是在parser()中使用。至於H264VideoStreamFramer前面已經說過了,沒什麼太多的東西,因此就不看了。總結起來也就是這樣:RTPSink向H264VideoStreamFramer要下一幀(其實h264中確定不是一幀了,而是一個NAL Unit),H264VideoStreamFramer告訴H264VideoStreamParser輸出緩衝和容內的字節數,而後調用H264VideoStreamParser的parser()函數,parser()中調用ByteStreamFileSource從文件中讀取數據,直到parser()得到完整的一幀,parser()返回,H264VideoStreamFramer進行一些本身的處理後把這一幀返回給了RTPSink(固然是以回調函數的方式返回)。
還有一個東西,H264FUAFragmenter,被H264VideoRTPSink所使用,繼承自FramedFilter。它最初在RTPSink開始播放後建立,以下:

[cpp] view plaincopy

  1. Boolean H264VideoRTPSink::continuePlaying()  

  2. {  

  3.     // First, check whether we have a 'fragmenter' class set up yet.  

  4.     // If not, create it now:  

  5.     if (fOurFragmenter == NULL) {  

  6.         fOurFragmenter = new H264FUAFragmenter(envir(), fSource,  

  7.                 OutPacketBuffer::maxSize,  

  8.                 ourMaxPacketSize() - 12/*RTP hdr size*/);  

  9.         fSource = fOurFragmenter;  

  10.     }  

  11.   

  12.     // Then call the parent class's implementation:  

  13.     return MultiFramedRTPSink::continuePlaying();  

  14. }  

而且它取代了H264VideoStreamFramer成爲直接與RTPSink發生關係的source.如此一來,RTPSink要獲取幀時,都是從它獲取的.看它最主要的一個函數吧:

[cpp] view plaincopy

  1. void H264FUAFragmenter::doGetNextFrame()  

  2. {  

  3.     if (fNumValidDataBytes == 1) {  

  4.         // We have no NAL unit data currently in the buffer.  Read a new one:  

  5.         fInputSource->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,  

  6.                 afterGettingFrame, this, FramedSource::handleClosure, this);  

  7.     } else {  

  8.         // We have NAL unit data in the buffer.  There are three cases to consider:  

  9.         // 1. There is a new NAL unit in the buffer, and it's small enough to deliver  

  10.         //    to the RTP sink (as is).  

  11.         // 2. There is a new NAL unit in the buffer, but it's too large to deliver to  

  12.         //    the RTP sink in its entirety.  Deliver the first fragment of this data,  

  13.         //    as a FU-A packet, with one extra preceding header byte.  

  14.         // 3. There is a NAL unit in the buffer, and we've already delivered some  

  15.         //    fragment(s) of this.  Deliver the next fragment of this data,  

  16.         //    as a FU-A packet, with two extra preceding header bytes.  

  17.   

  18.         if (fMaxSize < fMaxOutputPacketSize) { // shouldn't happen  

  19.             envir() << "H264FUAFragmenter::doGetNextFrame(): fMaxSize ("  

  20.                     << fMaxSize << ") is smaller than expected\n";  

  21.         } else {  

  22.             fMaxSize = fMaxOutputPacketSize;  

  23.         }  

  24.   

  25.         fLastFragmentCompletedNALUnit = True; // by default  

  26.         if (fCurDataOffset == 1) { // case 1 or 2  

  27.             if (fNumValidDataBytes - 1 <= fMaxSize) { // case 1  

  28.                 memmove(fTo, &fInputBuffer[1], fNumValidDataBytes - 1);  

  29.                 fFrameSize = fNumValidDataBytes - 1;  

  30.                 fCurDataOffset = fNumValidDataBytes;  

  31.             } else { // case 2  

  32.                 // We need to send the NAL unit data as FU-A packets.  Deliver the first  

  33.                 // packet now.  Note that we add FU indicator and FU header bytes to the front  

  34.                 // of the packet (reusing the existing NAL header byte for the FU header).  

  35.                 fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator  

  36.                 fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // FU header (with S bit)  

  37.                 memmove(fTo, fInputBuffer, fMaxSize);  

  38.                 fFrameSize = fMaxSize;  

  39.                 fCurDataOffset += fMaxSize - 1;  

  40.                 fLastFragmentCompletedNALUnit = False;  

  41.             }  

  42.         } else { // case 3  

  43.             // We are sending this NAL unit data as FU-A packets.  We've already sent the  

  44.             // first packet (fragment).  Now, send the next fragment.  Note that we add  

  45.             // FU indicator and FU header bytes to the front.  (We reuse these bytes that  

  46.             // we already sent for the first fragment, but clear the S bit, and add the E  

  47.             // bit if this is the last fragment.)  

  48.             fInputBuffer[fCurDataOffset - 2] = fInputBuffer[0]; // FU indicator  

  49.             fInputBuffer[fCurDataOffset - 1] = fInputBuffer[1] & ~0x80; // FU header (no S bit)  

  50.             unsigned numBytesToSend = 2 + fNumValidDataBytes - fCurDataOffset;  

  51.             if (numBytesToSend > fMaxSize) {  

  52.                 // We can't send all of the remaining data this time:  

  53.                 numBytesToSend = fMaxSize;  

  54.                 fLastFragmentCompletedNALUnit = False;  

  55.             } else {  

  56.                 // This is the last fragment:  

  57.                 fInputBuffer[fCurDataOffset - 1] |= 0x40; // set the E bit in the FU header  

  58.                 fNumTruncatedBytes = fSaveNumTruncatedBytes;  

  59.             }  

  60.             memmove(fTo, &fInputBuffer[fCurDataOffset - 2], numBytesToSend);  

  61.             fFrameSize = numBytesToSend;  

  62.             fCurDataOffset += numBytesToSend - 2;  

  63.         }  

  64.   

  65.         if (fCurDataOffset >= fNumValidDataBytes) {  

  66.             // We're done with this data.  Reset the pointers for receiving new data:  

  67.             fNumValidDataBytes = fCurDataOffset = 1;  

  68.         }  

  69.   

  70.         // Complete delivery to the client:  

  71.         FramedSource::afterGetting(this);  

  72.     }  

  73. }  

若是輸入緩衝中沒有數據,調用fInputSource->getNextFrame(),fInputSource是H264VideoStreamFramer,H264VideoStreamFramer的getNextFrame()會調用H264VideoStreamParser的parser(),parser()又調用ByteStreamFileSource獲取數據,而後分析,parser()完成後會調用:

[cpp] view plaincopy

  1. void H264FUAFragmenter::afterGettingFrame1(  

  2.         unsigned frameSize,  

  3.         unsigned numTruncatedBytes,  

  4.         struct timeval presentationTime,  

  5.         unsigned durationInMicroseconds)  

  6. {  

  7.     fNumValidDataBytes += frameSize;  

  8.     fSaveNumTruncatedBytes = numTruncatedBytes;  

  9.     fPresentationTime = presentationTime;  

  10.     fDurationInMicroseconds = durationInMicroseconds;  

  11.   

  12.     // Deliver data to the client:  

  13.     doGetNextFrame();  

  14. }  

而後又調用回H264FUAFragmenter::doGetNextFrame(),此時輸入緩衝中有數據了,H264FUAFragmenter就進行分析處理.H264FUAFragmenter又對數據作了什麼呢?

相關文章
相關標籤/搜索