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;
}
示例#3
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;
}