MediaTime M2VParser::CountBFrames(){ //We count after the first chunk. MediaTime count = 0; if(m_eos) return 0; if(notReachedFirstGOP) return 0; for(size_t i = 1; i < chunks.size(); i++){ MPEGChunk* c = chunks[i]; if(c->GetType() == MPEG_VIDEO_PICTURE_START_CODE){ MPEG2PictureHeader h; ParsePictureHeader(c, h); if(h.frameType == MPEG2_B_FRAME){ count += GetFrameDuration(h); }else{ return count; } } } return -1; }
//Reads frames until it can timestamp them, usually reads until it has //an I, P, B+, P...this way it can stamp them all and set references. //NOTE: references aren't yet used by our system but they are kept up //with in this plugin. int32_t M2VParser::FillQueues(){ if(chunks.empty()){ return -1; } bool done = false; while(!done){ MediaTime myTime = currentStampingTime; MPEGChunk* chunk = chunks.front(); while (chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE) { if (chunk->GetType() == MPEG_VIDEO_GOP_START_CODE) { if (gopChunk) delete gopChunk; gopChunk = chunk; } else if (chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE) { if (seqHdrChunk) delete seqHdrChunk; ParseSequenceHeader(chunk, m_seqHdr); seqHdrChunk = chunk; } chunks.erase(chunks.begin()); if (chunks.empty()) return -1; chunk = chunks.front(); } MPEG2PictureHeader picHdr; ParsePictureHeader(chunk, picHdr); MediaTime bcount; if(myTime == nextSkip){ myTime+=nextSkipDuration; currentStampingTime=myTime; } switch(picHdr.frameType){ case MPEG2_I_FRAME: bcount = CountBFrames(); if(bcount > 0){ //..BBIBB.. myTime += bcount; nextSkip = myTime; nextSkipDuration = GetFrameDuration(picHdr); }else{ //IPBB.. if(bcount == -1 && !m_eos) return -1; currentStampingTime += GetFrameDuration(picHdr);; } ShoveRef(myTime); QueueFrame(chunk, myTime, picHdr); notReachedFirstGOP = false; break; case MPEG2_P_FRAME: bcount = CountBFrames(); if(firstRef == -1) break; if(bcount > 0){ //..PBB.. myTime += bcount; nextSkip = myTime; nextSkipDuration = GetFrameDuration(picHdr); }else{ //..PPBB.. if(bcount == -1 && !m_eos) return -1; currentStampingTime+=GetFrameDuration(picHdr); } ShoveRef(myTime); QueueFrame(chunk, myTime, picHdr); break; default: //B-frames if(firstRef == -1 || secondRef == -1) break; QueueFrame(chunk, myTime, picHdr); currentStampingTime+=GetFrameDuration(picHdr); } chunks.erase(chunks.begin()); delete chunk; if (chunks.empty()) return -1; } return 0; }
//Maintains the time of the last start of GOP and uses the temporal_reference //field as an offset. int32_t M2VParser::FillQueues(){ if(chunks.empty()){ return -1; } bool done = false; while(!done){ MediaTime myTime; MPEGChunk* chunk = chunks.front(); while (chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE) { if (chunk->GetType() == MPEG_VIDEO_GOP_START_CODE) { ParseGOPHeader(chunk, m_gopHdr); if (frameNum != 0) { gopPts = highestPts + 1; } if (gopChunk) delete gopChunk; gopChunk = chunk; gopNum++; /* Perform some sanity checks */ if(waitSecondField){ mxerror(Y("Single field frame before GOP header detected. Fix the MPEG2 video stream before attempting to multiplex it.\n")); } if(!waitQueue.empty()){ mxwarn(Y("Shortened GOP detected. Some frames have been dropped. You may want to fix the MPEG2 video stream before attempting to multiplex it.\n")); FlushWaitQueue(); } if(m_gopHdr.brokenLink){ mxinfo(Y("Found group of picture with broken link. You may want use smart reencode before attempting to multiplex it.\n")); } // There are too many broken videos to do the following so ReferenceBlock will be wrong for broken videos. /* if(m_gopHdr.closedGOP){ ClearRef(); } */ } else if (chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE) { if (seqHdrChunk) delete seqHdrChunk; ParseSequenceHeader(chunk, m_seqHdr); seqHdrChunk = chunk; } chunks.erase(chunks.begin()); if (chunks.empty()) return -1; chunk = chunks.front(); } MPEG2PictureHeader picHdr; ParsePictureHeader(chunk, picHdr); if (picHdr.pictureStructure == 0x03) { usePictureFrames = true; } myTime = gopPts + picHdr.temporalReference; invisible = false; if (myTime > highestPts) highestPts = myTime; switch(picHdr.frameType){ case MPEG2_I_FRAME: PrepareFrame(chunk, myTime, picHdr); notReachedFirstGOP = false; break; case MPEG2_P_FRAME: if(firstRef == -1) break; PrepareFrame(chunk, myTime, picHdr); break; default: //B-frames if(firstRef == -1 || secondRef == -1){ if(!m_gopHdr.closedGOP && !m_gopHdr.brokenLink){ if(gopNum > 0){ mxerror(Y("Found B frame without second reference in a non closed GOP. Fix the MPEG2 video stream before attempting to multiplex it.\n")); } else if (!probing && !bFrameMissingReferenceWarning){ mxwarn(Y("Found one or more B frames without second reference in the first GOP. You may want to fix the MPEG2 video stream or use smart reencode before attempting to multiplex it.\n")); bFrameMissingReferenceWarning = true; } } invisible = true; } PrepareFrame(chunk, myTime, picHdr); } frameNum++; chunks.erase(chunks.begin()); delete chunk; if (chunks.empty()) return -1; } return 0; }