現(xiàn)在來分析live555中關于H264的處理部分,主要包括從文件中讀取數(shù)據(jù)進行并進行frame(NALU)的分割,然后對frame進行分片,這些工作都是在frame交給RTP sink之前完成的。接著上篇分析文章(RTP的打包與發(fā)送)中提到的MultiFramedRTP::packFrame函數(shù)進行分析。
- void MultiFramedRTPSink::packFrame() {
- if (fOutBuf->haveOverflowData()) {
- ...
- } else {
- ...
-
-
- //
- //從source中獲取下一個frame
- //
- fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),
- afterGettingFrame, this, ourHandleClosure, this);
- }
- }
getNextFrame是定義在FramedSource中的非虛函數(shù),從source中獲取下一個frame,然后調(diào)用回調(diào)函數(shù)afterGettingFrame。afterGettingFrame被定義為靜態(tài)函數(shù),因為在C++中類成員函數(shù)是不能作為回調(diào)用函數(shù)的。不過這里為什么要用回調(diào)函數(shù)回?
注意,對于H264來說,上面的fSource并不是MPEGVideoStreamFramer類,因為在 H264VideoRTPSink::continuePlaying()函數(shù)中改變了fSource的值。
- Boolean H264VideoRTPSink::continuePlaying() {
- // First, check whether we have a 'fragmenter' class set up yet.
- // If not, create it now:
- if (fOurFragmenter == NULL) {
- //創(chuàng)建一個輔助類H264FUAFragmenter,用于H264按照RFC3984進行RTP打包
-
- fOurFragmenter = new H264FUAFragmenter(envir(), fSource, OutPacketBuffer::maxSize,
- ourMaxPacketSize() - 12/*RTP hdr size*/);
- fSource = fOurFragmenter;
- }
-
-
- // Then call the parent class's implementation:
- return MultiFramedRTPSink::continuePlaying();
- }
fSource被指向了H264FUAFragmenter類,這個類主要實現(xiàn)了H264按照RFC3984進行RTP分包,不過這里的實現(xiàn)每個RTP中最多只包含一個NALU,沒有實現(xiàn)組合封包的情形。這個類的繼承關系如下:H264FUAFragmenter->FramedFilter->FramedSource。很明顯,這是一個filter,包裝了MPEGVideoStreamFramer類的對像。
先來看來看getNextFrame的實現(xiàn)
- void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,
- afterGettingFunc* afterGettingFunc,
- void* afterGettingClientData,
- onCloseFunc* onCloseFunc,
- void* onCloseClientData) {
- // Make sure we're not already being read:
- if (fIsCurrentlyAwaitingData) {
- envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";
- envir().internalError();
- }
-
-
- fTo = to; //buffer地址
- fMaxSize = maxSize; //buffer最大長度
- fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()
- fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()
- fAfterGettingFunc = afterGettingFunc; //獲取完一個frame后將執(zhí)行這個函數(shù)
- fAfterGettingClientData = afterGettingClientData; //這個參數(shù)就是MultiFramedRTPSink類型指針
- fOnCloseFunc = onCloseFunc;
- fOnCloseClientData = onCloseClientData;
- fIsCurrentlyAwaitingData = True;
-
-
- doGetNextFrame();
- }
上面的函數(shù)主要是進行一些成員變量的初始化,獲取到的frame需要保存到fTo地址中,然后調(diào)用fAfterGettingFunc函數(shù),若文件讀取完畢,還需要調(diào)用fOnCloseFunc函數(shù)。重要的工作還是在doGetNextFrame函數(shù)中完成,不過它是定義在FramedSource類中的純虛函數(shù),需要在子類中重新實現(xiàn)。
現(xiàn)在來看H264FUAFragmenter中對doGetNextFrame的實現(xiàn)
- void H264FUAFragmenter::doGetNextFrame() {
- if (fNumValidDataBytes == 1) {
- //讀取一個新的frame
- // We have no NAL unit data currently in the buffer. Read a new one:
- fInputSource->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,
- afterGettingFrame, this,
- FramedSource::handleClosure, this);
- } else {
- //
- //現(xiàn)在buffer中已經(jīng)存在NALU數(shù)據(jù)了,需要考慮三種情況
- //1.一個新的NALU,且足夠小能投遞給RTP sink。
- //2.一個新的NALU,但是比RTP sink要求的包大了,投遞第一個分片作為一個FU-A packet, 并帶上一個額外的頭字節(jié)。
- //3.部分NALU數(shù)據(jù),投遞下一個分片作為一個FU-A packet,并帶上2個額外的頭字節(jié)。
- // We have NAL unit data in the buffer. There are three cases to consider:
- // 1. There is a new NAL unit in the buffer, and it's small enough to deliver
- // to the RTP sink (as is).
- // 2. There is a new NAL unit in the buffer, but it's too large to deliver to
- // the RTP sink in its entirety. Deliver the first fragment of this data,
- // as a FU-A packet, with one extra preceding header byte.
- // 3. There is a NAL unit in the buffer, and we've already delivered some
- // fragment(s) of this. Deliver the next fragment of this data,
- // as a FU-A packet, with two extra preceding header bytes.
-
-
- if (fMaxSize < fMaxOutputPacketSize) { // shouldn't happen
- envir() << "H264FUAFragmenter::doGetNextFrame(): fMaxSize ("
- << fMaxSize << ") is smaller than expected\n";
- } else {
- fMaxSize = fMaxOutputPacketSize;
- }
-
-
- fLastFragmentCompletedNALUnit = True; // by default
- if (fCurDataOffset == 1) { // case 1 or 2
- if (fNumValidDataBytes - 1 <= fMaxSize) { // case 1
- //
- //情況1, 處理整個NALU
- //
- memmove(fTo, &fInputBuffer[1], fNumValidDataBytes - 1);
- fFrameSize = fNumValidDataBytes - 1;
- fCurDataOffset = fNumValidDataBytes;
- } else { // case 2
- //
- //情況2,處理NALU的第1個分片。注意,我們添加FU指示符和FU頭字節(jié)(with S bit)到包的最前面(
- //重用已經(jīng)存在的NAL 頭字節(jié)作為FU的頭字節(jié))
- //
- // We need to send the NAL unit data as FU-A packets. Deliver the first
- // packet now. Note that we add FU indicator and FU header bytes to the front
- // of the packet (reusing the existing NAL header byte for the FU header).
- fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator
- fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // FU header (with S bit) 重用NALU頭字節(jié)
- memmove(fTo, fInputBuffer, fMaxSize);
- fFrameSize = fMaxSize;
- fCurDataOffset += fMaxSize - 1;
- fLastFragmentCompletedNALUnit = False;
- }
- } else { // case 3
- //
- //情況3,處理非第1個分片。需要添加FU指示符和FU頭(我們重用了第一個分片中的字節(jié),但是需要清除S位,
- //并在最后一個分片中添加E位)
- //
- //
- // We are sending this NAL unit data as FU-A packets. We've already sent the
- // first packet (fragment). Now, send the next fragment. Note that we add
- // FU indicator and FU header bytes to the front. (We reuse these bytes that
- // we already sent for the first fragment, but clear the S bit, and add the E
- // bit if this is the last fragment.)
- fInputBuffer[fCurDataOffset-2] = fInputBuffer[0]; // FU indicator
- fInputBuffer[fCurDataOffset-1] = fInputBuffer[1]&~0x80; // FU header (no S bit)
- unsigned numBytesToSend = 2 + fNumValidDataBytes - fCurDataOffset;
- if (numBytesToSend > fMaxSize) {
- // We can't send all of the remaining data this time:
- numBytesToSend = fMaxSize;
- fLastFragmentCompletedNALUnit = False;
- } else {
- //
- //最后一個分片,需要在FU頭中設置E標志位
- // This is the last fragment:
- fInputBuffer[fCurDataOffset-1] |= 0x40; // set the E bit in the FU header
- fNumTruncatedBytes = fSaveNumTruncatedBytes;
- }
- memmove(fTo, &fInputBuffer[fCurDataOffset-2], numBytesToSend);
- fFrameSize = numBytesToSend;
- fCurDataOffset += numBytesToSend - 2;
- }
-
-
- if (fCurDataOffset >= fNumValidDataBytes) {
- // We're done with this data. Reset the pointers for receiving new data:
- fNumValidDataBytes = fCurDataOffset = 1;
- }
-
-
- // Complete delivery to the client:
- FramedSource::afterGetting(this);
- }
- }
H264FUAFragmenter::doGetNextFrame函數(shù)第一次執(zhí)行時,執(zhí)行條件1,需要調(diào)用 MPEGVideoStreamFramer::doGetNextFrame讀取一個新的frame,獲取frame的具體過程稍后再分析?,F(xiàn)在先看獲取frame之后的工作,afterGettingFrame函數(shù)
- void H264FUAFragmenter::afterGettingFrame(void* clientData, unsigned frameSize,
- unsigned numTruncatedBytes,
- struct timeval presentationTime,
- unsigned durationInMicroseconds) {
- H264FUAFragmenter* fragmenter = (H264FUAFragmenter*)clientData;
- fragmenter->afterGettingFrame1(frameSize, numTruncatedBytes, presentationTime,
- durationInMicroseconds);
- }
沒什么好說的,再看afterGettingFrame1函數(shù)
- void H264FUAFragmenter::afterGettingFrame1(unsigned frameSize,
- unsigned numTruncatedBytes,
- struct timeval presentationTime,
- unsigned durationInMicroseconds) {
- fNumValidDataBytes += frameSize; //保存讀到的frame長度
- fSaveNumTruncatedBytes = numTruncatedBytes;
- fPresentationTime = presentationTime;
- fDurationInMicroseconds = durationInMicroseconds;
-
-
- // Deliver data to the client:
- doGetNextFrame();
- }
上面的代碼首先記錄幾個數(shù)據(jù)到成員變量中,fNumValidDataBytes很重要,表示讀取到的frame長度+1。然后又一次調(diào)用了H264FUAFragmenter::doGetNextFrame(),這里將進入H264FUAFragmenter::doGetNextFrame函數(shù)中第二個條件分支,這種循環(huán)調(diào)用很容易把人弄迷糊了。
H264FUAFragmenter::doGetNextFrame函數(shù)中第二個條件分支中,處理H264的RTP分片問題,這里是按照RFC3984進行RTP封裝的。你應該注意到,在上篇文章"RTP的打包與發(fā)送"中,也出現(xiàn)了分片的代碼(MultiFramedRTPSink::packFrame函數(shù)中),那里直接將frame按MTU的長度來拆分。那為什么H264還要自定義一套RTP打包的標準呢?暫時我也不清楚。
在H264FUAFragmenter::doGetNextFrame()最后調(diào)用了 FramedSource::afterGetting
- void FramedSource::afterGetting(FramedSource* source) {
- source->fIsCurrentlyAwaitingData = False; //表示已經(jīng)獲取到數(shù)據(jù)了,處于非等待狀態(tài)
- // indicates that we can be read again
- // Note that this needs to be done here, in case the "fAfterFunc"
- // called below tries to read another frame (which it usually will)
-
-
- //通過回調(diào)用進行后續(xù)處理
- if (source->fAfterGettingFunc != NULL) {
- (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,
- source->fFrameSize, source->fNumTruncatedBytes,
- source->fPresentationTime,
- source->fDurationInMicroseconds);
- }
- }
上面的代碼主要是調(diào)用了FramedSource::getNextFrame函數(shù)中傳遞下來的回調(diào)函數(shù),這個回調(diào)函數(shù)就是MultiFramedRTPSink::afterGettingFrame,處理過程在上一篇文章"RTP的打包與發(fā)送"中已經(jīng)分析過了。
現(xiàn)在來看MPEGVideoStreamFramer::doGetNextFrame獲取Frame的過程。繼承關系:H264VideoStreamFramer->MPEGVideoStreamFramer->FramedFilter->FramedSource。在繼承路徑中存在FrameFilter,這說明H264VideoStreamFramer包裝了其它source(包裝的是讀取文件的字節(jié)流source)。doGetNextFrame函數(shù)首先在MPEGVideoStreamFramer中實現(xiàn)。
- void MPEGVideoStreamFramer::doGetNextFrame() {
- fParser->registerReadInterest(fTo, fMaxSize); //將目的buffer信息注冊到語法分析類中
- continueReadProcessing(); //繼續(xù)進行讀數(shù)據(jù)
- }
這里的MPEGVideoStreamFramer::fParser,是一個MPEGVideoStreamParser類型指針,作為語法分析器。再來看continueReadProcessing函數(shù)
- void MPEGVideoStreamFramer::continueReadProcessing() {
- unsigned acquiredFrameSize = fParser->parse(); //文件的語法分析(即demux)
- if (acquiredFrameSize > 0) {
- // We were able to acquire a frame from the input.
- // It has already been copied to the reader's space.
- fFrameSize = acquiredFrameSize;
- fNumTruncatedBytes = fParser->numTruncatedBytes();
-
-
- // "fPresentationTime" should have already been computed.
-
-
- // Compute "fDurationInMicroseconds" now:
- fDurationInMicroseconds
- = (fFrameRate == 0.0 || ((int)fPictureCount) < 0) ? 0
- : (unsigned)((fPictureCount*1000000)/fFrameRate);
- #ifdef DEBUG
- fprintf(stderr, "%d bytes @%u.%06d, fDurationInMicroseconds: %d ((%d*1000000)/%f)\n", acquiredFrameSize, fPresentationTime.tv_sec, fPresentationTime.tv_usec, fDurationInMicroseconds, fPictureCount, fFrameRate);
- #endif
- fPictureCount = 0;
-
- //
- //調(diào)用自身的afterGetting函數(shù),因為這不一個"leaf" source, 所以可能直接調(diào)用,
- //而不用擔心出現(xiàn)無限遞歸
- //
- // Call our own 'after getting' function. Because we're not a 'leaf'
- // source, we can call this directly, without risking infinite recursion.
- afterGetting(this);
- } else {
- // We were unable to parse a complete frame from the input, because:
- // - we had to read more data from the source stream, or
- // - the source stream has ended.
- }
- }
函數(shù)中首先調(diào)用了MPEGVideoStreamParser::parse函數(shù),將一個完整的Frame分析出來,并copy到了fTo(fTo就是OutPacketBuffer中的緩沖區(qū))中,這其中肯定也實現(xiàn)了從文件中讀取數(shù)據(jù)的過程。這里的fNumTruncatedBytes變量需要注意,fNumTruncatedBytes>0的話,表明Frame的實際長度大于fTo的最大長度,這將導致數(shù)據(jù)丟失,這時就要考慮增加緩沖區(qū)的長度了。成功獲取一個Frame后,將調(diào)用afterGetting函數(shù)處理后續(xù)工作。
先來看parse函數(shù),parse是定義在MPEGVideoStreamParser中的純虛函數(shù),在子類H264VideoStreamParser中實現(xiàn)。parse主要是從文件的字節(jié)流中,分離出一個個的Frame,對于H264而言其實就是對一個個的NALU。*.264文件的格式非常簡單,每個NALU以 0x00000001 作為起始符號(中間的NALU也可以以0x000001作為起始符),順序存放。
- unsigned H264VideoStreamParser::parse() {
- try {
- //
- //首先找到起始符號, 并跳過。文件流的最開始必需以0x00000001開始,但后續(xù)的NALU充許以0x000001(3 bytes)作為分隔
- //
- // The stream must start with a 0x00000001:
- if (!fHaveSeenFirstStartCode) {
- // Skip over any input bytes that precede the first 0x00000001:
- u_int32_t first4Bytes;
- while ((first4Bytes = test4Bytes()) != 0x00000001) {
- get1Byte(); setParseState(); // ensures that we progress over bad data
- }
- skipBytes(4); // skip this initial code
-
- setParseState();
- fHaveSeenFirstStartCode = True; // from now on
- }
- //
- //將起始標志也保存到目的緩沖區(qū)中
- //
- if (fOutputStartCodeSize > 0) {
- // Include a start code in the output:
- save4Bytes(0x00000001);
- }
-
- //
- //保存所有數(shù)據(jù),直至遇到起始標志,或者文件結束符。需要注意NALU中的第一個字節(jié),因為它指示了NALU的類型
- //
- // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF.
- // Also make note of the first byte, because it contains the "nal_unit_type":
- u_int8_t firstByte;
- if (haveSeenEOF()) {
- //
- //已經(jīng)設置了文件結束標志,將剩下的數(shù)據(jù)保存也來即可
- //
- // We hit EOF the last time that we tried to parse this data,
- // so we know that the remaining unparsed data forms a complete NAL unit:
- unsigned remainingDataSize = totNumValidBytes() - curOffset();
- if (remainingDataSize == 0) (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time
- #ifdef DEBUG
- fprintf(stderr, "This NAL unit (%d bytes) ends with EOF\n", remainingDataSize);
- #endif
- if (remainingDataSize == 0) return 0;
- firstByte = get1Byte(); //將第一個字節(jié)保存下來,其指示了NALU的類型
- saveByte(firstByte);
-
- while (--remainingDataSize > 0) {
- saveByte(get1Byte());
- }
- } else {
- u_int32_t next4Bytes = test4Bytes();
- firstByte = next4Bytes>>24; //將第一個字節(jié)保存下來
- //
- //將下一個起始符號之前的數(shù)據(jù)都保存下來
- //
- while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) {
- // We save at least some of "next4Bytes".
- if ((unsigned)(next4Bytes&0xFF) > 1) { //一次可以保存4個字節(jié),并不需要一個一個字節(jié)對比,除非到了結尾
- // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it:
- save4Bytes(next4Bytes);
- skipBytes(4);
- } else {
- // Save the first byte, and continue testing the rest:
- saveByte(next4Bytes>>24);
- skipBytes(1);
- }
- next4Bytes = test4Bytes();
- }
- // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit).
- // Skip over these remaining bytes, up until the start of the next NAL unit:
- if (next4Bytes == 0x00000001) {
- skipBytes(4);
- } else {
- skipBytes(3);
- }
- }
-
- u_int8_t nal_ref_idc = (firstByte&0x60)>>5;
- u_int8_t nal_unit_type = firstByte&0x1F;
- #ifdef DEBUG
- fprintf(stderr, "Parsed %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n",
- curFrameSize()-fOutputStartCodeSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]);
- #endif
-
-
- //
- //下面根據(jù)NALU的類型來作具體的分析
- //
- switch (nal_unit_type) {
- case 6: { // Supplemental enhancement information (SEI)
- analyze_sei_data();
- // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? #####
- break;
- }
- case 7: { // Sequence parameter set (序列參數(shù)集)
- //
- //保存一份SPS的副本到H264VideoStreamFramer中,后面的pps也需要保存,sps中可能還包含了幀率信息
- //
- // First, save a copy of this NAL unit, in case the downstream object wants to see it:
- usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);
-
-
- // Parse this NAL unit to check whether frame rate information is present:
- unsigned num_units_in_tick, time_scale, fixed_frame_rate_flag;
- analyze_seq_parameter_set_data(num_units_in_tick, time_scale, fixed_frame_rate_flag);
- if (time_scale > 0 && num_units_in_tick > 0) {
- usingSource()->fFrameRate = time_scale/(2.0*num_units_in_tick); //sps中包含了幀率信息
- #ifdef DEBUG
- fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate);
- if (fixed_frame_rate_flag == 0) {
- fprintf(stderr, "\tWARNING: \"fixed_frame_rate_flag\" was not set\n");
- }
- #endif
- } else { //sps中不包含幀率信息,則使用source中設置的默認幀率
- #ifdef DEBUG
- 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);
- #endif
- }
- break;
- }
- case 8: { // Picture parameter set (圖像參數(shù)集PPS)
- // Save a copy of this NAL unit, in case the downstream object wants to see it:
- usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);
- }
- }
-
- usingSource()->setPresentationTime(); //設置當前的時間
- #ifdef DEBUG
- unsigned long secs = (unsigned long)usingSource()->fPresentationTime.tv_sec;
- unsigned uSecs = (unsigned)usingSource()->fPresentationTime.tv_usec;
- fprintf(stderr, "\tPresentation time: %lu.%06u\n", secs, uSecs);
- #endif
-
- //
- //如果這個NALU是一個VCL NALU(即包含的是視頻數(shù)據(jù)),我們需要掃描下一個NALU的起始符,
- //以判斷這個NALU是否是當前的"access unit"(這個"access unit"應該可以理解為一幀圖像幀吧)。
- //我們需要根據(jù)這個信息去指明何時該增加"fPresentationTime"(RTP 打包時也需要根據(jù)這個信息,決定是否設置"M"位)。
- //
- // 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
- // ends the current 'access unit'. We need this information to figure out when to increment "fPresentationTime".
- // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.)
- Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise
- if (haveSeenEOF()) {
- // There is no next NAL unit, so we assume that this one ends the current 'access unit':
- thisNALUnitEndsAccessUnit = True;
- } else {
- Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC #####
- if (isVCL) {
- u_int32_t first4BytesOfNextNALUnit = test4Bytes();
- u_int8_t firstByteOfNextNALUnit = first4BytesOfNextNALUnit>>24;
- u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit&0x60)>>5;
- u_int8_t next_nal_unit_type = firstByteOfNextNALUnit&0x1F;
- if (next_nal_unit_type >= 6) {
- //下一個NALU不是VCL的,當前的"access unit"結束了
- // The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':
- #ifdef DEBUG
- fprintf(stderr, "\t(The next NAL unit is not a VCL)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else {
- //下一個NALU也是VCL的,還需要檢查一下它們是不是屬于同一個"access unit"
- // 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'.
- // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.)
- Boolean IdrPicFlag = nal_unit_type == 5;
- Boolean next_IdrPicFlag = next_nal_unit_type == 5;
- if (next_IdrPicFlag != IdrPicFlag) {
- // IdrPicFlag differs in value
- #ifdef DEBUG
- fprintf(stderr, "\t(IdrPicFlag differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if (next_nal_ref_idc != nal_ref_idc && next_nal_ref_idc*nal_ref_idc == 0) {
- // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0
- #ifdef DEBUG
- fprintf(stderr, "\t(nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5)
- && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) {
- // Both this and the next NAL units begin with a "slice_header".
- // Parse this (for each), to get parameters that we can compare:
-
- // Current NAL unit's "slice_header":
- unsigned frame_num, pic_parameter_set_id, idr_pic_id;
- Boolean field_pic_flag, bottom_field_flag;
- analyze_slice_header(fStartOfFrame + fOutputStartCodeSize, fTo, nal_unit_type,
- frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag);
-
- // Next NAL unit's "slice_header":
- #ifdef DEBUG
- fprintf(stderr, " Next NAL unit's slice_header:\n");
- #endif
- u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE];
- testBytes(next_slice_header, sizeof next_slice_header);
- unsigned next_frame_num, next_pic_parameter_set_id, next_idr_pic_id;
- Boolean next_field_pic_flag, next_bottom_field_flag;
- analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type,
- next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag);
-
- if (next_frame_num != frame_num) {
- // frame_num differs in value
- #ifdef DEBUG
- fprintf(stderr, "\t(frame_num differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if (next_pic_parameter_set_id != pic_parameter_set_id) {
- // pic_parameter_set_id differs in value
- #ifdef DEBUG
- fprintf(stderr, "\t(pic_parameter_set_id differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if (next_field_pic_flag != field_pic_flag) {
- // field_pic_flag differs in value
- #ifdef DEBUG
- fprintf(stderr, "\t(field_pic_flag differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if (next_bottom_field_flag != bottom_field_flag) {
- // bottom_field_flag differs in value
- #ifdef DEBUG
- fprintf(stderr, "\t(bottom_field_flag differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- } else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) {
- // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value
- // Note: We already know that IdrPicFlag is the same for both.
- #ifdef DEBUG
- fprintf(stderr, "\t(IdrPicFlag is equal to 1 for both and idr_pic_id differs in value)\n");
- #endif
- thisNALUnitEndsAccessUnit = True;
- }
- }
- }
- }
- }
-
- if (thisNALUnitEndsAccessUnit) {
- #ifdef DEBUG
- fprintf(stderr, "*****This NAL unit ends the current access unit*****\n");
- #endif
- usingSource()->fPictureEndMarker = True; //這里就是設置RTP打包時用到的M標志了
- ++usingSource()->fPictureCount;
-
-
- //
- //下一個NALU不再屬于當前"access unit""時,才改變時間
- //
- // Note that the presentation time for the next NAL unit will be different:
- struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias 這里是引用
- nextPT = usingSource()->fPresentationTime;
- double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate;
- unsigned nextSecsIncrement = (long)nextFraction;
- nextPT.tv_sec += (long)nextSecsIncrement;
- nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000);
- }
- setParseState();
-
-
- return curFrameSize();
- } catch (int /*e*/) {
- #ifdef DEBUG
- fprintf(stderr, "H264VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
- #endif
- return 0; // the parsing got interrupted
- }
- }
H264VideoStreamParser::parse()函數(shù)除了取出Frame,還對NALU中的部分參數(shù)做了解釋工作。對于PPS或者SPS類型的NALU,要保存到H264VideoStreamFramer中。"access unit",在這里可以理解為一副圖像,一個"access unit"可以包含多個NALU,很顯示這些NALU的時間戳應該是相同的。實際上,很多時候一個"access unit"單元只包含一個NALU,這里簡單多了。分析過程是根據(jù)section 7.4.1.2.4 of the
H.264 specification進行的。
|