Esempio n. 1
0
u_int8_t OggFileParser::parseInitialPage() {
  u_int8_t header_type_flag;
  u_int32_t bitstream_serial_number;
  parseStartOfPage(header_type_flag, bitstream_serial_number);

  // If this is a BOS page, examine the first 8 bytes of the first 'packet', to see whether
  // the track data type is one that we know how to stream:
  OggTrack* track;
  if ((header_type_flag&0x02) != 0) { // BOS
    char const* mimeType = NULL; // if unknown
    if (fPacketSizeTable != NULL && fPacketSizeTable->size[0] >= 8) { // sanity check
      char buf[8];
      testBytes((u_int8_t*)buf, 8);

      if (strncmp(&buf[1], "vorbis", 6) == 0) {
	mimeType = "audio/VORBIS";
	++fNumUnfulfilledTracks;
      } else if (strncmp(buf, "OpusHead", 8) == 0) {
	mimeType = "audio/OPUS";
	++fNumUnfulfilledTracks;
      } else if (strncmp(&buf[1], "theora", 6) == 0) {
	mimeType = "video/THEORA";
	++fNumUnfulfilledTracks;
      }
    }

    // Add a new track descriptor for this track:
    track = new OggTrack;
    track->trackNumber = bitstream_serial_number;
    track->mimeType = mimeType;
    fOurFile.addTrack(track);
  } else { // not a BOS page
    // Because this is not a BOS page, the specified track should already have been seen:
    track = fOurFile.lookup(bitstream_serial_number);
  }

  if (track != NULL) { // sanity check
#ifdef DEBUG
    fprintf(stderr, "This track's MIME type: %s\n",
	    track->mimeType == NULL ? "(unknown)" : track->mimeType);
#endif
    if (track->mimeType != NULL &&
	(strcmp(track->mimeType, "audio/VORBIS") == 0 ||
	 strcmp(track->mimeType, "video/THEORA") == 0 ||
	 strcmp(track->mimeType, "audio/OPUS") == 0)) {
      // Special-case handling of Vorbis, Theora, or Opus tracks:
      // Make a copy of each packet, until we get the three special headers that we need:
      Boolean isVorbis = strcmp(track->mimeType, "audio/VORBIS") == 0;
      Boolean isTheora = strcmp(track->mimeType, "video/THEORA") == 0;

      for (unsigned j = 0; j < fPacketSizeTable->numCompletedPackets && track->weNeedHeaders(); ++j) {
	unsigned const packetSize = fPacketSizeTable->size[j];
	if (packetSize == 0) continue; // sanity check

	delete[] fSavedPacket/*if any*/; fSavedPacket = new u_int8_t[packetSize];
	getBytes(fSavedPacket, packetSize);
	fPacketSizeTable->totSizes -= packetSize;

	// The start of the packet tells us whether its a header that we know about:
	Boolean headerIsKnown = False;
	unsigned index = 0;
	if (isVorbis) {
	  u_int8_t const firstByte = fSavedPacket[0];

	  headerIsKnown = firstByte == 1 || firstByte == 3 || firstByte == 5;
	  index = (firstByte-1)/2; // 1, 3, or 5 => 0, 1, or 2
	} else if (isTheora) {
	  u_int8_t const firstByte = fSavedPacket[0];

	  headerIsKnown = firstByte == 0x80 || firstByte == 0x81 || firstByte == 0x82;
	  index = firstByte &~0x80; // 0x80, 0x81, or 0x82 => 0, 1, or 2
	} else { // Opus
	  if (strncmp((char const*)fSavedPacket, "OpusHead", 8) == 0) {
	    headerIsKnown = True;
	    index = 0; // "identification" header
	  } else if (strncmp((char const*)fSavedPacket, "OpusTags", 8) == 0) {
	    headerIsKnown = True;
	    index = 1; // "comment" header
	  }
	}
	if (headerIsKnown) {
#ifdef DEBUG
	  char const* headerName[3] = { "identification", "comment", "setup" };
	  fprintf(stderr, "Saved %d-byte %s \"%s\" header\n", packetSize, track->mimeType,
		  headerName[index]);
#endif
	  // This is a header, but first check it for validity:
	  if (!validateHeader(track, fSavedPacket, packetSize)) continue;

	  // Save this header (deleting any old header of the same type that we'd saved before)
	  delete[] track->vtoHdrs.header[index];
	  track->vtoHdrs.header[index] = fSavedPacket;
	  fSavedPacket = NULL;
	  track->vtoHdrs.headerSize[index] = packetSize;

	  if (!track->weNeedHeaders()) {
	    // We now have all of the needed Vorbis, Theora, or Opus headers for this track:
	    --fNumUnfulfilledTracks;
	  }
	  // Note: The above code won't work if a required header is fragmented over
	  // more than one 'page'.  We assume that that won't ever happen...
	}
      }
    }
  }

  // Skip over any remaining packet data bytes:
  if (fPacketSizeTable->totSizes > 0) {
#ifdef DEBUG
    fprintf(stderr, "Skipping %d remaining packet data bytes\n", fPacketSizeTable->totSizes);
#endif
    skipBytes(fPacketSizeTable->totSizes);
  }

  return header_type_flag;
}
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*/) {