int32_t M2VParser::InitParser(){ //Gotta find a sequence header now MPEGChunk* chunk; //MPEGChunk* seqHdrChunk; for(size_t i = 0; i < chunks.size(); i++){ chunk = chunks[i]; if(chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE){ //Copy the header for later, we must copy because the actual chunk will be deleted in a bit binary * hdrData = new binary[chunk->GetSize()]; memcpy(hdrData, chunk->GetPointer(), chunk->GetSize()); seqHdrChunk = new MPEGChunk(hdrData, chunk->GetSize()); //Save this for adding as private data... ParseSequenceHeader(chunk, m_seqHdr); //Look for sequence extension to identify mpeg2 binary* pData = chunk->GetPointer(); for(size_t j = 3; j < chunk->GetSize() - 4; j++){ if(pData[j] == 0x00 && pData[j+1] == 0x00 && pData[j+2] == 0x01 && pData[j+3] == 0xb5 && ((pData[j+4] & 0xF0) == 0x10)){ mpegVersion = 2; break; } } return 0; } } 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; }