live555源码分析----H264的数据处理

来源:互联网 发布:济南五龙潭传说知乎 编辑:程序博客网 时间:2024/05/22 00:11
 现在来分析live555中关于H264的处理部分,主要包括从文件中读取数据进行并进行frame(NALU)的分割,然后对frame进行分片,这些工作都是在frame交给RTP sink之前完成的。接着上篇分析文章(RTP的打包与发送)中提到的MultiFramedRTP::packFrame函数进行分析。
[cpp] view plaincopyprint?
  1. void MultiFramedRTPSink::packFrame() {  
  2.   if (fOutBuf->haveOverflowData()) {  
  3. ...  
  4.   } else {  
  5. ...  
  6.   
  7.   
  8.     //   
  9.     //从source中获取下一个frame   
  10.     //   
  11.     fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),  
  12.               afterGettingFrame, this, ourHandleClosure, this);  
  13.   }  
  14. }  


    getNextFrame是定义在FramedSource中的非虚函数,从source中获取下一个frame,然后调用回调函数afterGettingFrame。afterGettingFrame被定义为静态函数,因为在C++中类成员函数是不能作为回调用函数的。不过这里为什么要用回调函数回?
    注意,对于H264来说,上面的fSource并不是MPEGVideoStreamFramer类,因为在 H264VideoRTPSink::continuePlaying()函数中改变了fSource的值。    
[cpp] view plaincopyprint?
  1. Boolean H264VideoRTPSink::continuePlaying() {  
  2.   // First, check whether we have a 'fragmenter' class set up yet.  
  3.   // If not, create it now:   
  4.   if (fOurFragmenter == NULL) {  
  5.     //创建一个辅助类H264FUAFragmenter,用于H264按照RFC3984进行RTP打包  
  6.   
  7.     fOurFragmenter = new H264FUAFragmenter(envir(), fSource, OutPacketBuffer::maxSize,  
  8.                        ourMaxPacketSize() - 12/*RTP hdr size*/);  
  9.     fSource = fOurFragmenter;  
  10.   }  
  11.   
  12.   
  13.   // Then call the parent class's implementation:  
  14.   return MultiFramedRTPSink::continuePlaying();  
  15. }  


    fSource被指向了H264FUAFragmenter类,这个类主要实现了H264按照RFC3984进行RTP分包,不过这里的实现每个RTP中最多只包含一个NALU,没有实现组合封包的情形。这个类的继承关系如下:H264FUAFragmenter->FramedFilter->FramedSource。很明显,这是一个filter,包装了MPEGVideoStreamFramer类的对像。
    先来看来看getNextFrame的实现

[cpp] view plaincopyprint?
  1. void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,  
  2.                 afterGettingFunc* afterGettingFunc,  
  3.                 void* afterGettingClientData,  
  4.                 onCloseFunc* onCloseFunc,  
  5.                 void* onCloseClientData) {  
  6.   // Make sure we're not already being read:  
  7.   if (fIsCurrentlyAwaitingData) {  
  8.     envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";  
  9.     envir().internalError();  
  10.   }  
  11.   
  12.   
  13.   fTo = to;             //buffer地址  
  14.   fMaxSize = maxSize;   //buffer最大长度   
  15.   fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()  
  16.   fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()  
  17.   fAfterGettingFunc = afterGettingFunc;             //获取完一个frame后将执行这个函数  
  18.   fAfterGettingClientData = afterGettingClientData; //这个参数就是MultiFramedRTPSink类型指针  
  19.   fOnCloseFunc = onCloseFunc;  
  20.   fOnCloseClientData = onCloseClientData;  
  21.   fIsCurrentlyAwaitingData = True;  
  22.   
  23.   
  24.   doGetNextFrame();  
  25. }  


    上面的函数主要是进行一些成员变量的初始化,获取到的frame需要保存到fTo地址中,然后调用fAfterGettingFunc函数,若文件读取完毕,还需要调用fOnCloseFunc函数。重要的工作还是在doGetNextFrame函数中完成,不过它是定义在FramedSource类中的纯虚函数,需要在子类中重新实现。
    现在来看H264FUAFragmenter中对doGetNextFrame的实现

[cpp] view plaincopyprint?
  1. void H264FUAFragmenter::doGetNextFrame() {  
  2.   if (fNumValidDataBytes == 1) {  
  3.       //读取一个新的frame   
  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,  
  7.                    FramedSource::handleClosure, this);  
  8.   } else {  
  9.       //   
  10.       //现在buffer中已经存在NALU数据了,需要考虑三种情况   
  11.       //1.一个新的NALU,且足够小能投递给RTP sink。  
  12.       //2.一个新的NALU,但是比RTP sink要求的包大了,投递第一个分片作为一个FU-A packet, 并带上一个额外的头字节。  
  13.       //3.部分NALU数据,投递下一个分片作为一个FU-A packet,并带上2个额外的头字节。  
  14.     // We have NAL unit data in the buffer.  There are three cases to consider:  
  15.     // 1. There is a new NAL unit in the buffer, and it's small enough to deliver  
  16.     //    to the RTP sink (as is).   
  17.     // 2. There is a new NAL unit in the buffer, but it's too large to deliver to  
  18.     //    the RTP sink in its entirety.  Deliver the first fragment of this data,  
  19.     //    as a FU-A packet, with one extra preceding header byte.  
  20.     // 3. There is a NAL unit in the buffer, and we've already delivered some  
  21.     //    fragment(s) of this.  Deliver the next fragment of this data,  
  22.     //    as a FU-A packet, with two extra preceding header bytes.  
  23.   
  24.   
  25.     if (fMaxSize < fMaxOutputPacketSize) { // shouldn't happen  
  26.       envir() << "H264FUAFragmenter::doGetNextFrame(): fMaxSize ("  
  27.           << fMaxSize << ") is smaller than expected\n";  
  28.     } else {  
  29.       fMaxSize = fMaxOutputPacketSize;  
  30.     }  
  31.   
  32.   
  33.     fLastFragmentCompletedNALUnit = True; // by default  
  34.     if (fCurDataOffset == 1) { // case 1 or 2  
  35.       if (fNumValidDataBytes - 1 <= fMaxSize) { // case 1  
  36.           //   
  37.           //情况1, 处理整个NALU   
  38.           //   
  39.     memmove(fTo, &fInputBuffer[1], fNumValidDataBytes - 1);  
  40.     fFrameSize = fNumValidDataBytes - 1;  
  41.     fCurDataOffset = fNumValidDataBytes;  
  42.       } else { // case 2  
  43.           //   
  44.           //情况2,处理NALU的第1个分片。注意,我们添加FU指示符和FU头字节(with S bit)到包的最前面(  
  45.           //重用已经存在的NAL 头字节作为FU的头字节)  
  46.           //   
  47.     // We need to send the NAL unit data as FU-A packets.  Deliver the first  
  48.     // packet now.  Note that we add FU indicator and FU header bytes to the front  
  49.     // of the packet (reusing the existing NAL header byte for the FU header).  
  50.     fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator  
  51.     fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // FU header (with S bit)   重用NALU头字节  
  52.     memmove(fTo, fInputBuffer, fMaxSize);  
  53.     fFrameSize = fMaxSize;  
  54.     fCurDataOffset += fMaxSize - 1;  
  55.     fLastFragmentCompletedNALUnit = False;  
  56.       }  
  57.     } else { // case 3  
  58.         //   
  59.         //情况3,处理非第1个分片。需要添加FU指示符和FU头(我们重用了第一个分片中的字节,但是需要清除S位,  
  60.         //并在最后一个分片中添加E位)   
  61.         //   
  62.         //   
  63.       // We are sending this NAL unit data as FU-A packets.  We've already sent the  
  64.       // first packet (fragment).  Now, send the next fragment.  Note that we add  
  65.       // FU indicator and FU header bytes to the front.  (We reuse these bytes that  
  66.       // we already sent for the first fragment, but clear the S bit, and add the E  
  67.       // bit if this is the last fragment.)  
  68.       fInputBuffer[fCurDataOffset-2] = fInputBuffer[0]; // FU indicator  
  69.       fInputBuffer[fCurDataOffset-1] = fInputBuffer[1]&~0x80; // FU header (no S bit)  
  70.       unsigned numBytesToSend = 2 + fNumValidDataBytes - fCurDataOffset;  
  71.       if (numBytesToSend > fMaxSize) {  
  72.     // We can't send all of the remaining data this time:  
  73.     numBytesToSend = fMaxSize;  
  74.     fLastFragmentCompletedNALUnit = False;  
  75.       } else {  
  76.     //   
  77.     //最后一个分片,需要在FU头中设置E标志位   
  78.     // This is the last fragment:   
  79.     fInputBuffer[fCurDataOffset-1] |= 0x40; // set the E bit in the FU header  
  80.     fNumTruncatedBytes = fSaveNumTruncatedBytes;  
  81.       }  
  82.       memmove(fTo, &fInputBuffer[fCurDataOffset-2], numBytesToSend);  
  83.       fFrameSize = numBytesToSend;  
  84.       fCurDataOffset += numBytesToSend - 2;  
  85.     }  
  86.   
  87.   
  88.     if (fCurDataOffset >= fNumValidDataBytes) {  
  89.       // We're done with this data.  Reset the pointers for receiving new data:  
  90.       fNumValidDataBytes = fCurDataOffset = 1;  
  91.     }  
  92.   
  93.   
  94.     // Complete delivery to the client:   
  95.     FramedSource::afterGetting(this);  
  96.   }  
  97. }  


    H264FUAFragmenter::doGetNextFrame函数第一次执行时,执行条件1,需要调用 MPEGVideoStreamFramer::doGetNextFrame读取一个新的frame,获取frame的具体过程稍后再分析。现在先看获取frame之后的工作,afterGettingFrame函数
[cpp] view plaincopyprint?
  1. void H264FUAFragmenter::afterGettingFrame(void* clientData, unsigned frameSize,  
  2.                       unsigned numTruncatedBytes,  
  3.                       struct timeval presentationTime,  
  4.                       unsigned durationInMicroseconds) {  
  5.   H264FUAFragmenter* fragmenter = (H264FUAFragmenter*)clientData;  
  6.   fragmenter->afterGettingFrame1(frameSize, numTruncatedBytes, presentationTime,  
  7.                  durationInMicroseconds);  
  8. }  


没什么好说的,再看afterGettingFrame1函数


[cpp] view plaincopyprint?
  1. void H264FUAFragmenter::afterGettingFrame1(unsigned frameSize,  
  2.                        unsigned numTruncatedBytes,  
  3.                        struct timeval presentationTime,  
  4.                        unsigned durationInMicroseconds) {  
  5.   fNumValidDataBytes += frameSize;      //保存读到的frame长度  
  6.   fSaveNumTruncatedBytes = numTruncatedBytes;  
  7.   fPresentationTime = presentationTime;  
  8.   fDurationInMicroseconds = durationInMicroseconds;  
  9.   
  10.   
  11.   // Deliver data to the client:  
  12.   doGetNextFrame();  
  13. }  


    上面的代码首先记录几个数据到成员变量中,fNumValidDataBytes很重要,表示读取到的frame长度+1。然后又一次调用了H264FUAFragmenter::doGetNextFrame(),这里将进入H264FUAFragmenter::doGetNextFrame函数中第二个条件分支,这种循环调用很容易把人弄迷糊了。

    H264FUAFragmenter::doGetNextFrame函数中第二个条件分支中,处理H264的RTP分片问题,这里是按照RFC3984进行RTP封装的。你应该注意到,在上篇文章"RTP的打包与发送"中,也出现了分片的代码(MultiFramedRTPSink::packFrame函数中),那里直接将frame按MTU的长度来拆分。那为什么H264还要自定义一套RTP打包的标准呢?暂时我也不清楚。


    在H264FUAFragmenter::doGetNextFrame()最后调用了 FramedSource::afterGetting


[cpp] view plaincopyprint?
  1. void FramedSource::afterGetting(FramedSource* source) {  
  2.   source->fIsCurrentlyAwaitingData = False;     //表示已经获取到数据了,处于非等待状态  
  3.       // indicates that we can be read again  
  4.       // Note that this needs to be done here, in case the "fAfterFunc"  
  5.       // called below tries to read another frame (which it usually will)  
  6.   
  7.   
  8.   //通过回调用进行后续处理   
  9.   if (source->fAfterGettingFunc != NULL) {  
  10.     (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,  
  11.                    source->fFrameSize, source->fNumTruncatedBytes,  
  12.                    source->fPresentationTime,  
  13.                    source->fDurationInMicroseconds);  
  14.   }  
  15. }  



    上面的代码主要是调用了FramedSource::getNextFrame函数中传递下来的回调函数,这个回调函数就是MultiFramedRTPSink::afterGettingFrame,处理过程在上一篇文章"RTP的打包与发送"中已经分析过了。
  
    现在来看MPEGVideoStreamFramer::doGetNextFrame获取Frame的过程。继承关系:H264VideoStreamFramer->MPEGVideoStreamFramer->FramedFilter->FramedSource。在继承路径中存在FrameFilter,这说明H264VideoStreamFramer包装了其它source(包装的是读取文件的字节流source)。doGetNextFrame函数首先在MPEGVideoStreamFramer中实现。


[cpp] view plaincopyprint?
  1. void MPEGVideoStreamFramer::doGetNextFrame() {  
  2.   fParser->registerReadInterest(fTo, fMaxSize); //将目的buffer信息注册到语法分析类中  
  3.   continueReadProcessing();     //继续进行读数据  
  4. }  



    这里的MPEGVideoStreamFramer::fParser,是一个MPEGVideoStreamParser类型指针,作为语法分析器。再来看continueReadProcessing函数


[cpp] view plaincopyprint?
  1. void MPEGVideoStreamFramer::continueReadProcessing() {  
  2.   unsigned acquiredFrameSize = fParser->parse();    //文件的语法分析(即demux)  
  3.   if (acquiredFrameSize > 0) {  
  4.     // We were able to acquire a frame from the input.  
  5.     // It has already been copied to the reader's space.  
  6.     fFrameSize = acquiredFrameSize;  
  7.     fNumTruncatedBytes = fParser->numTruncatedBytes();  
  8.   
  9.   
  10.     // "fPresentationTime" should have already been computed.  
  11.   
  12.   
  13.     // Compute "fDurationInMicroseconds" now:  
  14.     fDurationInMicroseconds  
  15.       = (fFrameRate == 0.0 || ((int)fPictureCount) < 0) ? 0  
  16.       : (unsigned)((fPictureCount*1000000)/fFrameRate);  
  17. #ifdef DEBUG   
  18.     fprintf(stderr, "%d bytes @%u.%06d, fDurationInMicroseconds: %d ((%d*1000000)/%f)\n", acquiredFrameSize, fPresentationTime.tv_sec, fPresentationTime.tv_usec, fDurationInMicroseconds, fPictureCount, fFrameRate);  
  19. #endif   
  20.     fPictureCount = 0;  
  21.       
  22.     //   
  23.     //调用自身的afterGetting函数,因为这不一个"leaf" source, 所以可能直接调用,  
  24.     //而不用担心出现无限递归   
  25.     //   
  26.     // Call our own 'after getting' function.  Because we're not a 'leaf'  
  27.     // source, we can call this directly, without risking infinite recursion.  
  28.     afterGetting(this);  
  29.   } else {  
  30.     // We were unable to parse a complete frame from the input, because:  
  31.     // - we had to read more data from the source stream, or  
  32.     // - the source stream has ended.   
  33.   }  
  34. }  


    函数中首先调用了MPEGVideoStreamParser::parse函数,将一个完整的Frame分析出来,并copy到了fTo(fTo就是OutPacketBuffer中的缓冲区)中,这其中肯定也实现了从文件中读取数据的过程。这里的fNumTruncatedBytes变量需要注意,fNumTruncatedBytes>0的话,表明Frame的实际长度大于fTo的最大长度,这将导致数据丢失,这时就要考虑增加缓冲区的长度了。成功获取一个Frame后,将调用afterGetting函数处理后续工作。
    先来看parse函数,parse是定义在MPEGVideoStreamParser中的纯虚函数,在子类H264VideoStreamParser中实现。parse主要是从文件的字节流中,分离出一个个的Frame,对于H264而言其实就是对一个个的NALU。*.264文件的格式非常简单,每个NALU以 0x00000001 作为起始符号(中间的NALU也可以以0x000001作为起始符),顺序存放。


[cpp] view plaincopyprint?
  1. unsigned H264VideoStreamParser::parse() {  
  2.   try {  
  3.     //   
  4.     //首先找到起始符号, 并跳过。文件流的最开始必需以0x00000001开始,但后续的NALU充许以0x000001(3 bytes)作为分隔  
  5.     //   
  6.     // The stream must start with a 0x00000001:  
  7.     if (!fHaveSeenFirstStartCode) {  
  8.       // Skip over any input bytes that precede the first 0x00000001:  
  9.       u_int32_t first4Bytes;  
  10.       while ((first4Bytes = test4Bytes()) != 0x00000001) {  
  11.     get1Byte(); setParseState(); // ensures that we progress over bad data  
  12.       }  
  13.       skipBytes(4); // skip this initial code  
  14.         
  15.       setParseState();  
  16.       fHaveSeenFirstStartCode = True; // from now on  
  17.     }  
  18.     //   
  19.     //将起始标志也保存到目的缓冲区中   
  20.     //   
  21.     if (fOutputStartCodeSize > 0) {  
  22.       // Include a start code in the output:  
  23.       save4Bytes(0x00000001);     
  24.     }  
  25.       
  26.     //   
  27.     //保存所有数据,直至遇到起始标志,或者文件结束符。需要注意NALU中的第一个字节,因为它指示了NALU的类型  
  28.     //   
  29.     // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF.  
  30.     // Also make note of the first byte, because it contains the "nal_unit_type":   
  31.     u_int8_t firstByte;  
  32.     if (haveSeenEOF()) {  
  33.        //   
  34.        //已经设置了文件结束标志,将剩下的数据保存也来即可   
  35.        //   
  36.       // We hit EOF the last time that we tried to parse this data,  
  37.       // so we know that the remaining unparsed data forms a complete NAL unit:  
  38.       unsigned remainingDataSize = totNumValidBytes() - curOffset();  
  39.       if (remainingDataSize == 0) (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time  
  40. #ifdef DEBUG   
  41.       fprintf(stderr, "This NAL unit (%d bytes) ends with EOF\n", remainingDataSize);  
  42. #endif   
  43.       if (remainingDataSize == 0) return 0;  
  44.       firstByte = get1Byte();   //将第一个字节保存下来,其指示了NALU的类型  
  45.       saveByte(firstByte);  
  46.         
  47.       while (--remainingDataSize > 0) {  
  48.     saveByte(get1Byte());  
  49.       }  
  50.     } else {  
  51.       u_int32_t next4Bytes = test4Bytes();  
  52.       firstByte = next4Bytes>>24;   //将第一个字节保存下来  
  53.       //   
  54.       //将下一个起始符号之前的数据都保存下来   
  55.       //   
  56.       while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) {  
  57.     // We save at least some of "next4Bytes".  
  58.     if ((unsigned)(next4Bytes&0xFF) > 1) {  //一次可以保存4个字节,并不需要一个一个字节对比,除非到了结尾  
  59.       // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it:  
  60.       save4Bytes(next4Bytes);  
  61.       skipBytes(4);  
  62.     } else {  
  63.       // Save the first byte, and continue testing the rest:  
  64.       saveByte(next4Bytes>>24);  
  65.       skipBytes(1);  
  66.     }  
  67.     next4Bytes = test4Bytes();  
  68.       }  
  69.       // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit).  
  70.       // Skip over these remaining bytes, up until the start of the next NAL unit:  
  71.       if (next4Bytes == 0x00000001) {  
  72.     skipBytes(4);  
  73.       } else {  
  74.     skipBytes(3);  
  75.       }  
  76.     }  
  77.    
  78.     u_int8_t nal_ref_idc = (firstByte&0x60)>>5;  
  79.     u_int8_t nal_unit_type = firstByte&0x1F;  
  80. #ifdef DEBUG   
  81.     fprintf(stderr, "Parsed %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n",  
  82.         curFrameSize()-fOutputStartCodeSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]);  
  83. #endif   
  84.   
  85.   
  86.     //   
  87.     //下面根据NALU的类型来作具体的分析   
  88.     //   
  89.     switch (nal_unit_type) {  
  90.       case 6: { // Supplemental enhancement information (SEI)  
  91.     analyze_sei_data();  
  92.     // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? #####  
  93.     break;  
  94.       }  
  95.       case 7: { // Sequence parameter set (序列参数集)  
  96.       //   
  97.       //保存一份SPS的副本到H264VideoStreamFramer中,后面的pps也需要保存,sps中可能还包含了帧率信息  
  98.       //   
  99.     // First, save a copy of this NAL unit, in case the downstream object wants to see it:  
  100.     usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);  
  101.   
  102.   
  103.     // Parse this NAL unit to check whether frame rate information is present:  
  104.     unsigned num_units_in_tick, time_scale, fixed_frame_rate_flag;  
  105.     analyze_seq_parameter_set_data(num_units_in_tick, time_scale, fixed_frame_rate_flag);  
  106.     if (time_scale > 0 && num_units_in_tick > 0) {  
  107.       usingSource()->fFrameRate = time_scale/(2.0*num_units_in_tick);   //sps中包含了帧率信息  
  108. #ifdef DEBUG   
  109.       fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate);  
  110.       if (fixed_frame_rate_flag == 0) {  
  111.         fprintf(stderr, "\tWARNING: \"fixed_frame_rate_flag\" was not set\n");  
  112.       }  
  113. #endif   
  114.     } else {    //sps中不包含帧率信息,则使用source中设置的默认帧率  
  115. #ifdef DEBUG   
  116.       fprintf(stderr, "\tThis \"Sequence Parameter Set\" NAL unit contained no frame rate information, so we use a default frame rate of %f fps\n", usingSource()->fFrameRate);  
  117. #endif   
  118.     }  
  119.     break;  
  120.       }  
  121.       case 8: { // Picture parameter set (图像参数集PPS)  
  122.     // Save a copy of this NAL unit, in case the downstream object wants to see it:  
  123.     usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);  
  124.       }  
  125.     }  
  126.       
  127.     usingSource()->setPresentationTime();   //设置当前的时间  
  128. #ifdef DEBUG   
  129.     unsigned long secs = (unsigned long)usingSource()->fPresentationTime.tv_sec;  
  130.     unsigned uSecs = (unsigned)usingSource()->fPresentationTime.tv_usec;  
  131.     fprintf(stderr, "\tPresentation time: %lu.%06u\n", secs, uSecs);  
  132. #endif   
  133.       
  134.     //   
  135.     //如果这个NALU是一个VCL NALU(即包含的是视频数据),我们需要扫描下一个NALU的起始符,  
  136.     //以判断这个NALU是否是当前的"access unit"(这个"access unit"应该可以理解为一帧图像帧吧)。  
  137.     //我们需要根据这个信息去指明何时该增加"fPresentationTime"(RTP 打包时也需要根据这个信息,决定是否设置"M"位)。  
  138.     //   
  139.     // If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit  
  140.     // ends the current 'access unit'.  We need this information to figure out when to increment "fPresentationTime".  
  141.     // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.)  
  142.     Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise   
  143.     if (haveSeenEOF()) {  
  144.       // There is no next NAL unit, so we assume that this one ends the current 'access unit':  
  145.       thisNALUnitEndsAccessUnit = True;  
  146.     } else {  
  147.       Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC #####  
  148.       if (isVCL) {  
  149.     u_int32_t first4BytesOfNextNALUnit = test4Bytes();  
  150.     u_int8_t firstByteOfNextNALUnit = first4BytesOfNextNALUnit>>24;  
  151.     u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit&0x60)>>5;  
  152.     u_int8_t next_nal_unit_type = firstByteOfNextNALUnit&0x1F;  
  153.     if (next_nal_unit_type >= 6) {  
  154.         //下一个NALU不是VCL的,当前的"access unit"结束了  
  155.       // The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':  
  156. #ifdef DEBUG   
  157.       fprintf(stderr, "\t(The next NAL unit is not a VCL)\n");  
  158. #endif   
  159.       thisNALUnitEndsAccessUnit = True;  
  160.     } else {  
  161.         //下一个NALU也是VCL的,还需要检查一下它们是不是属于同一个"access unit"  
  162.       // The next NAL unit is also a VLC.  We need to examine it a little to figure out if it's a different 'access unit'.  
  163.       // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.)  
  164.       Boolean IdrPicFlag = nal_unit_type == 5;  
  165.       Boolean next_IdrPicFlag = next_nal_unit_type == 5;  
  166.       if (next_IdrPicFlag != IdrPicFlag) {  
  167.         // IdrPicFlag differs in value  
  168. #ifdef DEBUG   
  169.         fprintf(stderr, "\t(IdrPicFlag differs in value)\n");  
  170. #endif   
  171.         thisNALUnitEndsAccessUnit = True;  
  172.       } else if (next_nal_ref_idc != nal_ref_idc && next_nal_ref_idc*nal_ref_idc == 0) {  
  173.         // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0  
  174. #ifdef DEBUG   
  175.         fprintf(stderr, "\t(nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0)\n");  
  176. #endif   
  177.         thisNALUnitEndsAccessUnit = True;  
  178.       } else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5)  
  179.              && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) {  
  180.         // Both this and the next NAL units begin with a "slice_header".  
  181.         // Parse this (for each), to get parameters that we can compare:  
  182.           
  183.         // Current NAL unit's "slice_header":  
  184.         unsigned frame_num, pic_parameter_set_id, idr_pic_id;  
  185.         Boolean field_pic_flag, bottom_field_flag;  
  186.         analyze_slice_header(fStartOfFrame + fOutputStartCodeSize, fTo, nal_unit_type,  
  187.                  frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag);  
  188.           
  189.         // Next NAL unit's "slice_header":  
  190. #ifdef DEBUG   
  191.         fprintf(stderr, "    Next NAL unit's slice_header:\n");  
  192. #endif   
  193.         u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE];  
  194.         testBytes(next_slice_header, sizeof next_slice_header);  
  195.         unsigned next_frame_num, next_pic_parameter_set_id, next_idr_pic_id;  
  196.         Boolean next_field_pic_flag, next_bottom_field_flag;  
  197.         analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type,  
  198.                  next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag);  
  199.           
  200.         if (next_frame_num != frame_num) {  
  201.           // frame_num differs in value  
  202. #ifdef DEBUG   
  203.           fprintf(stderr, "\t(frame_num differs in value)\n");  
  204. #endif   
  205.           thisNALUnitEndsAccessUnit = True;  
  206.         } else if (next_pic_parameter_set_id != pic_parameter_set_id) {  
  207.           // pic_parameter_set_id differs in value  
  208. #ifdef DEBUG   
  209.           fprintf(stderr, "\t(pic_parameter_set_id differs in value)\n");  
  210. #endif   
  211.           thisNALUnitEndsAccessUnit = True;  
  212.         } else if (next_field_pic_flag != field_pic_flag) {  
  213.           // field_pic_flag differs in value  
  214. #ifdef DEBUG   
  215.           fprintf(stderr, "\t(field_pic_flag differs in value)\n");  
  216. #endif   
  217.           thisNALUnitEndsAccessUnit = True;  
  218.         } else if (next_bottom_field_flag != bottom_field_flag) {  
  219.           // bottom_field_flag differs in value  
  220. #ifdef DEBUG   
  221.           fprintf(stderr, "\t(bottom_field_flag differs in value)\n");  
  222. #endif   
  223.           thisNALUnitEndsAccessUnit = True;  
  224.         } else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) {  
  225.           // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value  
  226.           // Note: We already know that IdrPicFlag is the same for both.  
  227. #ifdef DEBUG   
  228.           fprintf(stderr, "\t(IdrPicFlag is equal to 1 for both and idr_pic_id differs in value)\n");  
  229. #endif   
  230.           thisNALUnitEndsAccessUnit = True;  
  231.         }  
  232.       }  
  233.     }  
  234.       }  
  235.     }  
  236.       
  237.     if (thisNALUnitEndsAccessUnit) {  
  238. #ifdef DEBUG   
  239.       fprintf(stderr, "*****This NAL unit ends the current access unit*****\n");  
  240. #endif   
  241.       usingSource()->fPictureEndMarker = True;  //这里就是设置RTP打包时用到的M标志了  
  242.       ++usingSource()->fPictureCount;  
  243.   
  244.   
  245.       //   
  246.       //下一个NALU不再属于当前"access unit""时,才改变时间  
  247.       //   
  248.       // Note that the presentation time for the next NAL unit will be different:  
  249.       struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias 这里是引用   
  250.       nextPT = usingSource()->fPresentationTime;  
  251.       double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate;   
  252.       unsigned nextSecsIncrement = (long)nextFraction;  
  253.       nextPT.tv_sec += (long)nextSecsIncrement;  
  254.       nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000);  
  255.     }  
  256.     setParseState();  
  257.   
  258.   
  259.     return curFrameSize();  
  260.   } catch (int /*e*/) {  
  261. #ifdef DEBUG   
  262.     fprintf(stderr, "H264VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");  
  263. #endif   
  264.     return 0;  // the parsing got interrupted  
  265.   }  
  266. }  


    H264VideoStreamParser::parse()函数除了取出Frame,还对NALU中的部分参数做了解释工作。对于PPS或者SPS类型的NALU,要保存到H264VideoStreamFramer中。"access unit",在这里可以理解为一副图像,一个"access unit"可以包含多个NALU,很显示这些NALU的时间戳应该是相同的。实际上,很多时候一个"access unit"单元只包含一个NALU,这里简单多了。分析过程是根据section 7.4.1.2.4 of the H.264 specification进行的。
原创粉丝点击