unsigned MPEG1or2VideoStreamParser
::parseVideoSequenceHeader(Boolean haveSeenStartCode) {
#ifdef DEBUG
  fprintf(stderr, "parsing video sequence header\n");
#endif
  unsigned first4Bytes;
  if (!haveSeenStartCode) {
    while ((first4Bytes = test4Bytes()) != VIDEO_SEQUENCE_HEADER_START_CODE) {
#ifdef DEBUG
      fprintf(stderr, "ignoring non video sequence header: 0x%08x\n", first4Bytes);
#endif
      get1Byte(); setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
          // ensures we progress over bad data
    }
    first4Bytes = get4Bytes();
  } else {
    // We've already seen the start code
    first4Bytes = VIDEO_SEQUENCE_HEADER_START_CODE;
  }
  save4Bytes(first4Bytes);

  // Next, extract the size and rate parameters from the next 8 bytes
  unsigned paramWord1 = get4Bytes();
  save4Bytes(paramWord1);
  unsigned next4Bytes = get4Bytes();
#ifdef DEBUG
  unsigned short horizontal_size_value   = (paramWord1&0xFFF00000)>>(32-12);
  unsigned short vertical_size_value     = (paramWord1&0x000FFF00)>>8;
  unsigned char aspect_ratio_information = (paramWord1&0x000000F0)>>4;
#endif
  unsigned char frame_rate_code          = (paramWord1&0x0000000F);
  usingSource()->fFrameRate = frameRateFromCode[frame_rate_code];
#ifdef DEBUG
  unsigned bit_rate_value                = (next4Bytes&0xFFFFC000)>>(32-18);
  unsigned vbv_buffer_size_value         = (next4Bytes&0x00001FF8)>>3;
  fprintf(stderr, "horizontal_size_value: %d, vertical_size_value: %d, aspect_ratio_information: %d, frame_rate_code: %d (=>%f fps), bit_rate_value: %d (=>%d bps), vbv_buffer_size_value: %d\n", horizontal_size_value, vertical_size_value, aspect_ratio_information, frame_rate_code, usingSource()->fFrameRate, bit_rate_value, bit_rate_value*400, vbv_buffer_size_value);
#endif

  // Now, copy all bytes that we see, up until we reach a GROUP_START_CODE
  // or a PICTURE_START_CODE:
  do {
    saveToNextCode(next4Bytes);
  } while (next4Bytes != GROUP_START_CODE && next4Bytes != PICTURE_START_CODE);

  setParseState((next4Bytes == GROUP_START_CODE)
		? PARSING_GOP_HEADER_SEEN_CODE : PARSING_PICTURE_HEADER);

  // Compute this frame's timestamp by noting how many pictures we've seen
  // since the last GOP header:
  usingSource()->computePresentationTime(fPicturesSinceLastGOP);

  // Save this video_sequence_header, in case we need to insert a copy
  // into the stream later:
  saveCurrentVSH();

  return curFrameSize();
}
void H264or5VideoStreamParser
::analyze_sei_payload(unsigned payloadType, unsigned payloadSize, u_int8_t* payload) {
  if (payloadType == 1/* pic_timing, for both H.264 and H.265 */) {
    BitVector bv(payload, 0, 8*payloadSize);

    DEBUG_TAB;
    if (CpbDpbDelaysPresentFlag) {
      unsigned cpb_removal_delay = bv.getBits(cpb_removal_delay_length_minus1 + 1);
      DEBUG_PRINT(cpb_removal_delay);
      unsigned dpb_output_delay = bv.getBits(dpb_output_delay_length_minus1 + 1);
      DEBUG_PRINT(dpb_output_delay);
    }
    if (pic_struct_present_flag) {
      unsigned pic_struct = bv.getBits(4);
      DEBUG_PRINT(pic_struct);
      // Use this to set "DeltaTfiDivisor" (which is used to compute the frame rate):
      double prevDeltaTfiDivisor = DeltaTfiDivisor; 
      if (fHNumber == 264) {
	DeltaTfiDivisor =
	  pic_struct == 0 ? 2.0 :
	  pic_struct <= 2 ? 1.0 :
	  pic_struct <= 4 ? 2.0 :
	  pic_struct <= 6 ? 3.0 :
	  pic_struct == 7 ? 4.0 :
	  pic_struct == 8 ? 6.0 :
	  2.0;
      } else { // H.265
	DeltaTfiDivisor =
	  pic_struct == 0 ? 2.0 :
	  pic_struct <= 2 ? 1.0 :
	  pic_struct <= 4 ? 2.0 :
	  pic_struct <= 6 ? 3.0 :
	  pic_struct == 7 ? 2.0 :
	  pic_struct == 8 ? 3.0 :
	  pic_struct <= 12 ? 1.0 :
	  2.0;
      }
      // If "DeltaTfiDivisor" has changed, and we've already computed the frame rate, then
      // adjust it, based on the new value of "DeltaTfiDivisor":
      if (DeltaTfiDivisor != prevDeltaTfiDivisor && fParsedFrameRate != 0.0) {
	  usingSource()->fFrameRate = fParsedFrameRate
	    = fParsedFrameRate*(prevDeltaTfiDivisor/DeltaTfiDivisor);
#ifdef DEBUG
	  fprintf(stderr, "Changed frame rate to %f fps\n", usingSource()->fFrameRate);
#endif
      }
    }
    // Ignore the rest of the payload (timestamps) for now... #####
  }
}
Ejemplo n.º 3
0
unsigned MPEG4VideoStreamParser
::parseVisualObjectSequence(Boolean haveSeenStartCode) {
#ifdef DEBUG
  fprintf(stderr, "parsing VisualObjectSequence\n");
#endif
  usingSource()->startNewConfig();
  u_int32_t first4Bytes;
  if (!haveSeenStartCode) {
    while ((first4Bytes = test4Bytes()) != VISUAL_OBJECT_SEQUENCE_START_CODE) {
#ifdef DEBUG
      fprintf(stderr, "ignoring non VS header: 0x%08x\n", first4Bytes);
#endif
      get1Byte(); setParseState(PARSING_VISUAL_OBJECT_SEQUENCE);
          // ensures we progress over bad data
    }
    first4Bytes = get4Bytes();
  } else {
    // We've already seen the start code
    first4Bytes = VISUAL_OBJECT_SEQUENCE_START_CODE;
  }
  save4Bytes(first4Bytes);

  // The next byte is the "profile_and_level_indication":
  u_int8_t pali = get1Byte();
#ifdef DEBUG
  fprintf(stderr, "profile_and_level_indication: %02x\n", pali);
#endif
  saveByte(pali);
  usingSource()->fProfileAndLevelIndication = pali;

  // Now, copy all bytes that we see, up until we reach
  // a VISUAL_OBJECT_START_CODE:
  u_int32_t next4Bytes = get4Bytes();
  while (next4Bytes != VISUAL_OBJECT_START_CODE) {
    saveToNextCode(next4Bytes);
  }

  setParseState(PARSING_VISUAL_OBJECT);

  // Compute this frame's presentation time:
  usingSource()->computePresentationTime(fTotalTicksSinceLastTimeCode);

  // This header forms part of the 'configuration' information:
  usingSource()->appendToNewConfig(fStartOfFrame, curFrameSize());

  return curFrameSize();
}
void MPEG1or2VideoStreamParser::saveCurrentVSH() {
  unsigned frameSize = curFrameSize();
  if (frameSize > sizeof fSavedVSHBuffer) return; // too big to save

  memmove(fSavedVSHBuffer, fStartOfFrame, frameSize);
  fSavedVSHSize = frameSize;
  fSavedVSHTimestamp = usingSource()->getCurrentPTS();
}
unsigned MPEG1or2VideoStreamParser::parsePictureHeader()
{
#ifdef DEBUG
    fprintf(stderr, "parsing picture header\n");
#endif
    // Note that we've already read the PICTURE_START_CODE
    // Next, extract the temporal reference from the next 4 bytes:
    unsigned next4Bytes = get4Bytes();
    unsigned short temporal_reference = (next4Bytes & 0xFFC00000) >> (32 - 10);
    unsigned char picture_coding_type = (next4Bytes & 0x00380000) >> 19;
#ifdef DEBUG
    unsigned short vbv_delay          = (next4Bytes & 0x0007FFF8) >> 3;
    fprintf(stderr, "temporal_reference: %d, picture_coding_type: %d, vbv_delay: %d\n", temporal_reference, picture_coding_type, vbv_delay);
#endif

    fSkippingCurrentPicture = fIFramesOnly && picture_coding_type != 1;
    if (fSkippingCurrentPicture)
    {
        // Skip all bytes that we see, up until we reach a slice_start_code:
        do
        {
            skipToNextCode(next4Bytes);
        }
        while (!isSliceStartCode(next4Bytes));
    }
    else
    {
        // Save the PICTURE_START_CODE that we've already read:
        save4Bytes(PICTURE_START_CODE);

        // Copy all bytes that we see, up until we reach a slice_start_code:
        do
        {
            saveToNextCode(next4Bytes);
        }
        while (!isSliceStartCode(next4Bytes));
    }

    setParseState(PARSING_SLICE);

    fCurrentSliceNumber = next4Bytes & 0xFF;

    // Record the temporal reference:
    fCurPicTemporalReference = temporal_reference;

    // Compute this frame's timestamp:
    usingSource()->computePresentationTime(fCurPicTemporalReference);

    if (fSkippingCurrentPicture)
    {
        return parse(); // try again, until we get a non-skipped frame
    }
    else
    {
        return curFrameSize();
    }
}
unsigned MPEG1or2VideoStreamParser::useSavedVSH() {
  unsigned bytesToUse = fSavedVSHSize;
  unsigned maxBytesToUse = fLimit - fStartOfFrame;
  if (bytesToUse > maxBytesToUse) bytesToUse = maxBytesToUse;

  memmove(fStartOfFrame, fSavedVSHBuffer, bytesToUse);

  // Also reset the saved timestamp:
  fSavedVSHTimestamp = usingSource()->getCurrentPTS();

#ifdef DEBUG
  fprintf(stderr, "used saved video_sequence_header (%d bytes)\n", bytesToUse);
#endif
  return bytesToUse;
}
 Boolean isVCL(u_int8_t nal_unit_type) { return usingSource()->isVCL(nal_unit_type); }
Ejemplo n.º 8
0
unsigned H264VideoStreamParser::parse() {
  try {
    // 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
    }
    
    if (fOutputStartCodeSize > 0) {
      // Include a start code in the output:
      save4Bytes(0x00000001);
    }

    // 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": 
    if (haveSeenEOF()) {
      // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data
      // forms a complete NAL unit, and that there's no 'start code' at the end:
      unsigned remainingDataSize = totNumValidBytes() - curOffset();
      while (remainingDataSize > 0) {
	saveByte(get1Byte());
	--remainingDataSize;
      }

      if (!fHaveSeenFirstByteOfNALUnit) {
	// There's no remaining NAL unit.
	(void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time
	return 0;
      }
#ifdef DEBUG
      fprintf(stderr, "This NAL unit (%d bytes) ends with EOF\n", curFrameSize()-fOutputStartCodeSize);
#endif
    } else {
      u_int32_t next4Bytes = test4Bytes();
      if (!fHaveSeenFirstByteOfNALUnit) {
	fFirstByteOfNALUnit = next4Bytes>>24;
	fHaveSeenFirstByteOfNALUnit = True;
      }
      while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) {
	// We save at least some of "next4Bytes".
	if ((unsigned)(next4Bytes&0xFF) > 1) {
	  // 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);
	}
	setParseState(); // ensures forward progress
	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 = (fFirstByteOfNALUnit&0x60)>>5;
    u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F;
    fHaveSeenFirstByteOfNALUnit = False; // for the next NAL unit that we parse
#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

    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
	// 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);
#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 {
#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
	// 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

    // 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) {
	  // 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 {
	  // The next NAL unit is also a VCL.  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;
      ++usingSource()->fPictureCount;

      // 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*/) {
unsigned MPEG1or2VideoStreamParser::parseSlice()
{
    // Note that we've already read the slice_start_code:
    unsigned next4Bytes = PICTURE_START_CODE | fCurrentSliceNumber;
#ifdef DEBUG_SLICE
    fprintf(stderr, "parsing slice: 0x%08x\n", next4Bytes);
#endif

    if (fSkippingCurrentPicture)
    {
        // Skip all bytes that we see, up until we reach a code of some sort:
        skipToNextCode(next4Bytes);
    }
    else
    {
        // Copy all bytes that we see, up until we reach a code of some sort:
        saveToNextCode(next4Bytes);
    }

    // The next thing to parse depends on the code that we just saw:
    if (isSliceStartCode(next4Bytes))   // common case
    {
        setParseState(PARSING_SLICE);
        fCurrentSliceNumber = next4Bytes & 0xFF;
    }
    else
    {
        // Because we don't see any more slices, we are assumed to have ended
        // the current picture:
        ++fPicturesSinceLastGOP;
        ++usingSource()->fPictureCount;
        usingSource()->fPictureEndMarker = True; // HACK #####

        switch (next4Bytes)
        {
        case SEQUENCE_END_CODE:
        {
            setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
            break;
        }
        case VIDEO_SEQUENCE_HEADER_START_CODE:
        {
            setParseState(PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE);
            break;
        }
        case GROUP_START_CODE:
        {
            setParseState(PARSING_GOP_HEADER_SEEN_CODE);
            break;
        }
        case PICTURE_START_CODE:
        {
            setParseState(PARSING_PICTURE_HEADER);
            break;
        }
        default:
        {
            usingSource()->envir() << "MPEG1or2VideoStreamParser::parseSlice(): Saw unexpected code "
                                   << (void *)next4Bytes << "\n";
            setParseState(PARSING_SLICE); // the safest way to recover...
            break;
        }
        }
    }

    // Compute this frame's timestamp:
    usingSource()->computePresentationTime(fCurPicTemporalReference);

    if (fSkippingCurrentPicture)
    {
        return parse(); // try again, until we get a non-skipped frame
    }
    else
    {
        return curFrameSize();
    }
}
unsigned MPEG1or2VideoStreamParser::parseGOPHeader(Boolean haveSeenStartCode)
{
    // First check whether we should insert a previously-saved
    // 'video_sequence_header' here:
    if (needToUseSavedVSH()) return useSavedVSH();

#ifdef DEBUG
    fprintf(stderr, "parsing GOP header\n");
#endif
    unsigned first4Bytes;
    if (!haveSeenStartCode)
    {
        while ((first4Bytes = test4Bytes()) != GROUP_START_CODE)
        {
#ifdef DEBUG
            fprintf(stderr, "ignoring non GOP start code: 0x%08x\n", first4Bytes);
#endif
            get1Byte();
            setParseState(PARSING_GOP_HEADER);
            // ensures we progress over bad data
        }
        first4Bytes = get4Bytes();
    }
    else
    {
        // We've already seen the GROUP_START_CODE
        first4Bytes = GROUP_START_CODE;
    }
    save4Bytes(first4Bytes);

    // Next, extract the (25-bit) time code from the next 4 bytes:
    unsigned next4Bytes = get4Bytes();
    unsigned time_code = (next4Bytes & 0xFFFFFF80) >> (32 - 25);
#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
    Boolean drop_frame_flag     = (time_code & 0x01000000) != 0;
#endif
    unsigned time_code_hours    = (time_code & 0x00F80000) >> 19;
    unsigned time_code_minutes  = (time_code & 0x0007E000) >> 13;
    unsigned time_code_seconds  = (time_code & 0x00000FC0) >> 6;
    unsigned time_code_pictures = (time_code & 0x0000003F);
#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
    fprintf(stderr, "time_code: 0x%07x, drop_frame %d, hours %d, minutes %d, seconds %d, pictures %d\n", time_code, drop_frame_flag, time_code_hours, time_code_minutes, time_code_seconds, time_code_pictures);
#endif
#ifdef DEBUG
    Boolean closed_gop  = (next4Bytes & 0x00000040) != 0;
    Boolean broken_link = (next4Bytes & 0x00000020) != 0;
    fprintf(stderr, "closed_gop: %d, broken_link: %d\n", closed_gop, broken_link);
#endif

    // Now, copy all bytes that we see, up until we reach a PICTURE_START_CODE:
    do
    {
        saveToNextCode(next4Bytes);
    }
    while (next4Bytes != PICTURE_START_CODE);

    // Record the time code:
    usingSource()->setTimeCode(time_code_hours, time_code_minutes,
                               time_code_seconds, time_code_pictures,
                               fPicturesSinceLastGOP);

    fPicturesSinceLastGOP = 0;

    // Compute this frame's timestamp:
    usingSource()->computePresentationTime(0);

    setParseState(PARSING_PICTURE_HEADER);

    return curFrameSize();
}
Boolean MPEG1or2VideoStreamParser::needToUseSavedVSH()
{
    return usingSource()->getCurrentPTS() > fSavedVSHTimestamp + fVSHPeriod
           && fSavedVSHSize > 0;
}