Java 类com.google.android.exoplayer.util.MpegAudioHeader 实例源码

项目:miku    文件:Mp3Extractor.java   
/** Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary. */
private long maybeResynchronize(ExtractorInput extractorInput)
    throws IOException, InterruptedException {
  inputBuffer.mark();
  if (!inputBuffer.readAllowingEndOfInput(extractorInput, scratch.data, 0, 4)) {
    return RESULT_END_OF_INPUT;
  }
  inputBuffer.returnToMark();

  scratch.setPosition(0);
  int sampleHeaderData = scratch.readInt();
  if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) {
    int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData);
    if (frameSize != -1) {
      MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
      return RESULT_CONTINUE;
    }
  }

  synchronizedHeaderData = 0;
  inputBuffer.skip(extractorInput, 1);
  return synchronizeCatchingEndOfInput(extractorInput);
}
项目:miku    文件:Mp3Extractor.java   
/**
 * Sets {@link #seeker} to seek using metadata from {@link #inputBuffer}, which should have its
 * position set to the start of the first frame in the stream. On returning,
 * {@link #inputBuffer}'s position and mark will be set to the start of the first frame of audio.
 *
 * @param extractorInput Source of data for {@link #inputBuffer}.
 * @param headerPosition Position (byte offset) of the synchronized header in the stream.
 * @throws IOException Thrown if there was an error reading from the stream. Not expected if the
 *     next two frames were already read during synchronization.
 * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
 *     the next two frames were already read during synchronization.
 */
private void setupSeeker(ExtractorInput extractorInput, long headerPosition)
    throws IOException, InterruptedException {
  // Try to set up seeking based on a XING or VBRI header.
  if (parseSeekerFrame(extractorInput, headerPosition, extractorInput.getLength())) {
    // Discard the parsed header so we start reading from the first audio frame.
    inputBuffer.mark();
    if (seeker != null) {
      return;
    }

    // If there was a header but it was not usable, synchronize to the next frame so we don't
    // use an invalid bitrate for CBR seeking. This read is guaranteed to succeed if the frame was
    // already read during synchronization.
    inputBuffer.read(extractorInput, scratch.data, 0, 4);
    scratch.setPosition(0);
    headerPosition += synchronizedHeader.frameSize;
    MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  }

  inputBuffer.returnToMark();
  seeker = new ConstantBitrateSeeker(headerPosition, synchronizedHeader.bitrate * 1000,
      extractorInput.getLength());
}
项目:ExoPlayer-Demo    文件:Mp3Extractor.java   
@Override
public int read(ExtractorInput input, PositionHolder seekPosition)
    throws IOException, InterruptedException {
  if (synchronizedHeaderData == 0 && !synchronizeCatchingEndOfInput(input)) {
    return RESULT_END_OF_INPUT;
  }
  if (seeker == null) {
    setupSeeker(input);
    extractorOutput.seekMap(seeker);
    MediaFormat mediaFormat = MediaFormat.createAudioFormat(null, synchronizedHeader.mimeType,
        MediaFormat.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, seeker.getDurationUs(),
        synchronizedHeader.channels, synchronizedHeader.sampleRate, null, null);
    if (gaplessInfo != null) {
      mediaFormat =
          mediaFormat.copyWithGaplessInfo(gaplessInfo.encoderDelay, gaplessInfo.encoderPadding);
    }
    trackOutput.format(mediaFormat);
  }
  return readSample(input);
}
项目:ExoPlayer-Demo    文件:Mp3Extractor.java   
/**
 * Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary.
 */
private boolean maybeResynchronize(ExtractorInput extractorInput)
    throws IOException, InterruptedException {
  extractorInput.resetPeekPosition();
  if (!extractorInput.peekFully(scratch.data, 0, 4, true)) {
    return false;
  }

  scratch.setPosition(0);
  int sampleHeaderData = scratch.readInt();
  if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) {
    int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData);
    if (frameSize != -1) {
      MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
      return true;
    }
  }

  synchronizedHeaderData = 0;
  extractorInput.skipFully(1);
  return synchronizeCatchingEndOfInput(extractorInput);
}
项目:ExoPlayer    文件:Mp3Extractor.java   
/** Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary. */
private long maybeResynchronize(ExtractorInput extractorInput)
    throws IOException, InterruptedException {
  inputBuffer.mark();
  if (!inputBuffer.readAllowingEndOfInput(extractorInput, scratch.data, 0, 4)) {
    return RESULT_END_OF_INPUT;
  }
  inputBuffer.returnToMark();

  scratch.setPosition(0);
  int sampleHeaderData = scratch.readInt();
  if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) {
    int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData);
    if (frameSize != -1) {
      MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
      return RESULT_CONTINUE;
    }
  }

  synchronizedHeaderData = 0;
  inputBuffer.skip(extractorInput, 1);
  return synchronizeCatchingEndOfInput(extractorInput);
}
项目:ExoPlayer    文件:Mp3Extractor.java   
/**
 * Sets {@link #seeker} to seek using metadata from {@link #inputBuffer}, which should have its
 * position set to the start of the first frame in the stream. On returning,
 * {@link #inputBuffer}'s position and mark will be set to the start of the first frame of audio.
 *
 * @param extractorInput Source of data for {@link #inputBuffer}.
 * @param headerPosition Position (byte offset) of the synchronized header in the stream.
 * @throws IOException Thrown if there was an error reading from the stream. Not expected if the
 *     next two frames were already read during synchronization.
 * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
 *     the next two frames were already read during synchronization.
 */
private void setupSeeker(ExtractorInput extractorInput, long headerPosition)
    throws IOException, InterruptedException {
  // Try to set up seeking based on a XING or VBRI header.
  if (parseSeekerFrame(extractorInput, headerPosition, extractorInput.getLength())) {
    // Discard the parsed header so we start reading from the first audio frame.
    inputBuffer.mark();
    if (seeker != null) {
      return;
    }

    // If there was a header but it was not usable, synchronize to the next frame so we don't
    // use an invalid bitrate for CBR seeking. This read is guaranteed to succeed if the frame was
    // already read during synchronization.
    inputBuffer.read(extractorInput, scratch.data, 0, 4);
    scratch.setPosition(0);
    headerPosition += synchronizedHeader.frameSize;
    MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  }

  inputBuffer.returnToMark();
  seeker = new ConstantBitrateSeeker(headerPosition, synchronizedHeader.bitrate * 1000,
      extractorInput.getLength());
}
项目:miku    文件:MpegAudioReader.java   
public MpegAudioReader(TrackOutput output) {
  super(output);
  state = STATE_FINDING_HEADER;
  // The first byte of an MPEG Audio frame header is always 0xFF.
  headerScratch = new ParsableByteArray(4);
  headerScratch.data[0] = (byte) 0xFF;
  header = new MpegAudioHeader();
}
项目:miku    文件:MpegAudioReader.java   
/**
 * Attempts to read the remaining two bytes of the frame header.
 * <p>
 * If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
 * the media format is output if this has not previously occurred, the four header bytes are
 * output as sample data, and the position of the source is advanced to the byte that immediately
 * follows the header.
 * <p>
 * If a frame header is read in full but cannot be parsed then the state is changed to
 * {@link #STATE_READING_HEADER}.
 * <p>
 * If a frame header is not read in full then the position of the source is advanced to the limit,
 * and the method should be called again with the next source to continue the read.
 *
 * @param source The source from which to read.
 */
private void readHeaderRemainder(ParsableByteArray source) {
  int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
  source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
  frameBytesRead += bytesToRead;
  if (frameBytesRead < HEADER_SIZE) {
    // We haven't read the whole header yet.
    return;
  }

  headerScratch.setPosition(0);
  boolean parsedHeader = MpegAudioHeader.populateHeader(headerScratch.readInt(), header);
  if (!parsedHeader) {
    // We thought we'd located a frame header, but we hadn't.
    frameBytesRead = 0;
    state = STATE_READING_HEADER;
    return;
  }

  frameSize = header.frameSize;
  if (!hasOutputFormat) {
    frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
    MediaFormat mediaFormat = MediaFormat.createAudioFormat(header.mimeType,
        MpegAudioHeader.MAX_FRAME_SIZE_BYTES, C.UNKNOWN_TIME_US, header.channels,
        header.sampleRate, null);
    output.format(mediaFormat);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
项目:miku    文件:XingSeeker.java   
/**
 * Returns a {@link XingSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *    'XING' or 'INFO' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @param inputLength The length of the stream in bytes.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static XingSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position, long inputLength) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;
  long firstFramePosition = position + mpegAudioHeader.frameSize;

  int flags = frame.readInt();
  // Frame count, size and table of contents are required to use this header.
  if ((flags & 0x07) != 0x07) {
    return null;
  }

  // Read frame count, as (flags & 1) == 1.
  int frameCount = frame.readUnsignedIntToInt();
  if (frameCount == 0) {
    return null;
  }
  long durationUs =
      Util.scaleLargeTimestamp(frameCount, samplesPerFrame * 1000000L, sampleRate);

  // Read size in bytes, as (flags & 2) == 2.
  long sizeBytes = frame.readUnsignedIntToInt();

  // Read table-of-contents as (flags & 4) == 4.
  frame.skipBytes(1);
  long[] tableOfContents = new long[99];
  for (int i = 0; i < 99; i++) {
    tableOfContents[i] = frame.readUnsignedByte();
  }

  // TODO: Handle encoder delay and padding in 3 bytes offset by xingBase + 213 bytes:
  // delay = (frame.readUnsignedByte() << 4) + (frame.readUnsignedByte() >> 4);
  // padding = ((frame.readUnsignedByte() & 0x0F) << 8) + frame.readUnsignedByte();
  return new XingSeeker(tableOfContents, firstFramePosition, sizeBytes, durationUs, inputLength);
}
项目:ExoPlayer-Demo    文件:MpegAudioReader.java   
public MpegAudioReader(TrackOutput output) {
  super(output);
  state = STATE_FINDING_HEADER;
  // The first byte of an MPEG Audio frame header is always 0xFF.
  headerScratch = new ParsableByteArray(4);
  headerScratch.data[0] = (byte) 0xFF;
  header = new MpegAudioHeader();
}
项目:ExoPlayer-Demo    文件:MpegAudioReader.java   
/**
 * Attempts to read the remaining two bytes of the frame header.
 * <p>
 * If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
 * the media format is output if this has not previously occurred, the four header bytes are
 * output as sample data, and the position of the source is advanced to the byte that immediately
 * follows the header.
 * <p>
 * If a frame header is read in full but cannot be parsed then the state is changed to
 * {@link #STATE_READING_HEADER}.
 * <p>
 * If a frame header is not read in full then the position of the source is advanced to the limit,
 * and the method should be called again with the next source to continue the read.
 *
 * @param source The source from which to read.
 */
private void readHeaderRemainder(ParsableByteArray source) {
  int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
  source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
  frameBytesRead += bytesToRead;
  if (frameBytesRead < HEADER_SIZE) {
    // We haven't read the whole header yet.
    return;
  }

  headerScratch.setPosition(0);
  boolean parsedHeader = MpegAudioHeader.populateHeader(headerScratch.readInt(), header);
  if (!parsedHeader) {
    // We thought we'd located a frame header, but we hadn't.
    frameBytesRead = 0;
    state = STATE_READING_HEADER;
    return;
  }

  frameSize = header.frameSize;
  if (!hasOutputFormat) {
    frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
    MediaFormat mediaFormat = MediaFormat.createAudioFormat(null, header.mimeType,
        MediaFormat.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, C.UNKNOWN_TIME_US,
        header.channels, header.sampleRate, null, null);
    output.format(mediaFormat);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
项目:ExoPlayer-Demo    文件:Mp3Extractor.java   
/**
 * Constructs a new {@link Mp3Extractor}.
 *
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or -1 if forcing
 *     is not required.
 */
public Mp3Extractor(long forcedFirstSampleTimestampUs) {
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(4);
  synchronizedHeader = new MpegAudioHeader();
  basisTimeUs = -1;
}
项目:ExoPlayer-Demo    文件:XingSeeker.java   
/**
 * Returns a {@link XingSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *    'Xing' or 'Info' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @param inputLength The length of the stream in bytes.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static XingSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position, long inputLength) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;
  long firstFramePosition = position + mpegAudioHeader.frameSize;

  int flags = frame.readInt();
  int frameCount;
  if ((flags & 0x01) != 0x01 || (frameCount = frame.readUnsignedIntToInt()) == 0) {
    // If the frame count is missing/invalid, the header can't be used to determine the duration.
    return null;
  }
  long durationUs = Util.scaleLargeTimestamp(frameCount, samplesPerFrame * C.MICROS_PER_SECOND,
      sampleRate);
  if ((flags & 0x06) != 0x06) {
    // If the size in bytes or table of contents is missing, the stream is not seekable.
    return new XingSeeker(firstFramePosition, durationUs, inputLength);
  }

  long sizeBytes = frame.readUnsignedIntToInt();
  frame.skipBytes(1);
  long[] tableOfContents = new long[99];
  for (int i = 0; i < 99; i++) {
    tableOfContents[i] = frame.readUnsignedByte();
  }

  // TODO: Handle encoder delay and padding in 3 bytes offset by xingBase + 213 bytes:
  // delay = (frame.readUnsignedByte() << 4) + (frame.readUnsignedByte() >> 4);
  // padding = ((frame.readUnsignedByte() & 0x0F) << 8) + frame.readUnsignedByte();
  return new XingSeeker(firstFramePosition, durationUs, inputLength, tableOfContents,
      sizeBytes, mpegAudioHeader.frameSize);
}
项目:ExoPlayer-Demo    文件:XingSeekerTest.java   
@Override
public void setUp() throws Exception {
  MpegAudioHeader xingFrameHeader = new MpegAudioHeader();
  MpegAudioHeader.populateHeader(XING_FRAME_HEADER_DATA, xingFrameHeader);
  seeker = XingSeeker.create(xingFrameHeader, new ParsableByteArray(XING_FRAME_PAYLOAD),
      XING_FRAME_POSITION, C.UNKNOWN_TIME_US);
  seekerWithInputLength = XingSeeker.create(xingFrameHeader,
      new ParsableByteArray(XING_FRAME_PAYLOAD), XING_FRAME_POSITION, INPUT_LENGTH);
  xingFrameSize = xingFrameHeader.frameSize;
}
项目:ExoPlayer    文件:MpegAudioReader.java   
public MpegAudioReader(TrackOutput output) {
  super(output);
  state = STATE_FINDING_HEADER;
  // The first byte of an MPEG Audio frame header is always 0xFF.
  headerScratch = new ParsableByteArray(4);
  headerScratch.data[0] = (byte) 0xFF;
  header = new MpegAudioHeader();
}
项目:ExoPlayer    文件:MpegAudioReader.java   
/**
 * Attempts to read the remaining two bytes of the frame header.
 * <p>
 * If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
 * the media format is output if this has not previously occurred, the four header bytes are
 * output as sample data, and the position of the source is advanced to the byte that immediately
 * follows the header.
 * <p>
 * If a frame header is read in full but cannot be parsed then the state is changed to
 * {@link #STATE_READING_HEADER}.
 * <p>
 * If a frame header is not read in full then the position of the source is advanced to the limit,
 * and the method should be called again with the next source to continue the read.
 *
 * @param source The source from which to read.
 */
private void readHeaderRemainder(ParsableByteArray source) {
  int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
  source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
  frameBytesRead += bytesToRead;
  if (frameBytesRead < HEADER_SIZE) {
    // We haven't read the whole header yet.
    return;
  }

  headerScratch.setPosition(0);
  boolean parsedHeader = MpegAudioHeader.populateHeader(headerScratch.readInt(), header);
  if (!parsedHeader) {
    // We thought we'd located a frame header, but we hadn't.
    frameBytesRead = 0;
    state = STATE_READING_HEADER;
    return;
  }

  frameSize = header.frameSize;
  if (!hasOutputFormat) {
    frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
    MediaFormat mediaFormat = MediaFormat.createAudioFormat(header.mimeType,
        MpegAudioHeader.MAX_FRAME_SIZE_BYTES, C.UNKNOWN_TIME_US, header.channels,
        header.sampleRate, null);
    output.format(mediaFormat);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
项目:ExoPlayer    文件:XingSeeker.java   
/**
 * Returns a {@link XingSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *    'XING' or 'INFO' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @param inputLength The length of the stream in bytes.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static XingSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position, long inputLength) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;
  long firstFramePosition = position + mpegAudioHeader.frameSize;

  int flags = frame.readInt();
  // Frame count, size and table of contents are required to use this header.
  if ((flags & 0x07) != 0x07) {
    return null;
  }

  // Read frame count, as (flags & 1) == 1.
  int frameCount = frame.readUnsignedIntToInt();
  if (frameCount == 0) {
    return null;
  }
  long durationUs =
      Util.scaleLargeTimestamp(frameCount, samplesPerFrame * 1000000L, sampleRate);

  // Read size in bytes, as (flags & 2) == 2.
  long sizeBytes = frame.readUnsignedIntToInt();

  // Read table-of-contents as (flags & 4) == 4.
  frame.skipBytes(1);
  long[] tableOfContents = new long[99];
  for (int i = 0; i < 99; i++) {
    tableOfContents[i] = frame.readUnsignedByte();
  }

  // TODO: Handle encoder delay and padding in 3 bytes offset by xingBase + 213 bytes:
  // delay = (frame.readUnsignedByte() << 4) + (frame.readUnsignedByte() >> 4);
  // padding = ((frame.readUnsignedByte() & 0x0F) << 8) + frame.readUnsignedByte();
  return new XingSeeker(tableOfContents, firstFramePosition, sizeBytes, durationUs, inputLength);
}
项目:miku    文件:Mp3Extractor.java   
/** Constructs a new {@link Mp3Extractor}. */
public Mp3Extractor() {
  inputBuffer = new BufferingInput(MpegAudioHeader.MAX_FRAME_SIZE_BYTES * 3);
  scratch = new ParsableByteArray(4);
  synchronizedHeader = new MpegAudioHeader();
}
项目:miku    文件:Mp3Extractor.java   
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
  ParsableByteArray scratch = new ParsableByteArray(4);
  int startPosition = 0;
  input.peekFully(scratch.data, 0, 3);
  if (scratch.readUnsignedInt24() == ID3_TAG) {
    input.advancePeekPosition(3);
    input.peekFully(scratch.data, 0, 4);
    int headerLength = ((scratch.data[0] & 0x7F) << 21) | ((scratch.data[1] & 0x7F) << 14)
        | ((scratch.data[2] & 0x7F) << 7) | (scratch.data[3] & 0x7F);
    input.advancePeekPosition(headerLength);
    startPosition = 3 + 3 + 4 + headerLength;
  } else {
    input.resetPeekPosition();
  }

  // Try to find four consecutive valid MPEG audio frames.
  int headerPosition = startPosition;
  int validFrameCount = 0;
  int candidateSynchronizedHeaderData = 0;
  while (true) {
    if (headerPosition - startPosition >= MAX_SNIFF_BYTES) {
      return false;
    }

    input.peekFully(scratch.data, 0, 4);
    scratch.setPosition(0);
    int headerData = scratch.readInt();
    int frameSize;
    if ((candidateSynchronizedHeaderData != 0
        && (headerData & HEADER_MASK) != (candidateSynchronizedHeaderData & HEADER_MASK))
        || (frameSize = MpegAudioHeader.getFrameSize(headerData)) == -1) {
      validFrameCount = 0;
      candidateSynchronizedHeaderData = 0;

      // Try reading a header starting at the next byte.
      input.resetPeekPosition();
      input.advancePeekPosition(++headerPosition);
      continue;
    }

    if (validFrameCount == 0) {
      candidateSynchronizedHeaderData = headerData;
    }

    // The header was valid and matching (if appropriate). Check another or end synchronization.
    if (++validFrameCount == 4) {
      return true;
    }

    // Look for more headers.
    input.advancePeekPosition(frameSize - 4);
  }
}
项目:miku    文件:VbriSeeker.java   
/**
 * Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *     'VBRI' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static VbriSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position) {
  frame.skipBytes(10);
  int numFrames = frame.readInt();
  if (numFrames <= 0) {
    return null;
  }
  int sampleRate = mpegAudioHeader.sampleRate;
  long durationUs = Util.scaleLargeTimestamp(
      numFrames, 1000000L * (sampleRate >= 32000 ? 1152 : 576), sampleRate);
  int numEntries = frame.readUnsignedShort();
  int scale = frame.readUnsignedShort();
  int entrySize = frame.readUnsignedShort();

  // Read entries in the VBRI header.
  long[] timesUs = new long[numEntries];
  long[] offsets = new long[numEntries];
  long segmentDurationUs = durationUs / numEntries;
  long now = 0;
  int segmentIndex = 0;
  while (segmentIndex < numEntries) {
    int numBytes;
    switch (entrySize) {
      case 1:
        numBytes = frame.readUnsignedByte();
        break;
      case 2:
        numBytes = frame.readUnsignedShort();
        break;
      case 3:
        numBytes = frame.readUnsignedInt24();
        break;
      case 4:
        numBytes = frame.readUnsignedIntToInt();
        break;
      default:
        return null;
    }
    now += segmentDurationUs;
    timesUs[segmentIndex] = now;
    position += numBytes * scale;
    offsets[segmentIndex] = position;

    segmentIndex++;
  }
  return new VbriSeeker(timesUs, offsets, position + mpegAudioHeader.frameSize, durationUs);
}
项目:ExoPlayer-Demo    文件:Mp3Extractor.java   
private boolean synchronize(ExtractorInput input, boolean sniffing)
    throws IOException, InterruptedException {
  int searched = 0;
  int validFrameCount = 0;
  int candidateSynchronizedHeaderData = 0;
  int peekedId3Bytes = 0;
  input.resetPeekPosition();
  if (input.getPosition() == 0) {
    gaplessInfo = Id3Util.parseId3(input);
    peekedId3Bytes = (int) input.getPeekPosition();
    if (!sniffing) {
      input.skipFully(peekedId3Bytes);
    }
  }
  while (true) {
    if (sniffing && searched == MAX_SNIFF_BYTES) {
      return false;
    }
    if (!sniffing && searched == MAX_SYNC_BYTES) {
      throw new ParserException("Searched too many bytes.");
    }
    if (!input.peekFully(scratch.data, 0, 4, true)) {
      return false;
    }
    scratch.setPosition(0);
    int headerData = scratch.readInt();
    int frameSize;
    if ((candidateSynchronizedHeaderData != 0
        && (headerData & HEADER_MASK) != (candidateSynchronizedHeaderData & HEADER_MASK))
        || (frameSize = MpegAudioHeader.getFrameSize(headerData)) == -1) {
      // The header is invalid or doesn't match the candidate header. Try the next byte offset.
      validFrameCount = 0;
      candidateSynchronizedHeaderData = 0;
      searched++;
      if (sniffing) {
        input.resetPeekPosition();
        input.advancePeekPosition(peekedId3Bytes + searched);
      } else {
        input.skipFully(1);
      }
    } else {
      // The header is valid and matches the candidate header.
      validFrameCount++;
      if (validFrameCount == 1) {
        MpegAudioHeader.populateHeader(headerData, synchronizedHeader);
        candidateSynchronizedHeaderData = headerData;
      } else if (validFrameCount == 4) {
        break;
      }
      input.advancePeekPosition(frameSize - 4);
    }
  }
  // Prepare to read the synchronized frame.
  if (sniffing) {
    input.skipFully(peekedId3Bytes + searched);
  } else {
    input.resetPeekPosition();
  }
  synchronizedHeaderData = candidateSynchronizedHeaderData;
  return true;
}
项目:ExoPlayer-Demo    文件:Mp3Extractor.java   
/**
 * Sets {@link #seeker} to seek using metadata read from {@code input}, which should provide data
 * from the start of the first frame in the stream. On returning, the input's position will be set
 * to the start of the first frame of audio.
 *
 * @param input The {@link ExtractorInput} from which to read.
 * @throws IOException Thrown if there was an error reading from the stream. Not expected if the
 *     next two frames were already peeked during synchronization.
 * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
 *     the next two frames were already peeked during synchronization.
 */
private void setupSeeker(ExtractorInput input) throws IOException, InterruptedException {
  // Read the first frame which may contain a Xing or VBRI header with seeking metadata.
  ParsableByteArray frame = new ParsableByteArray(synchronizedHeader.frameSize);
  input.peekFully(frame.data, 0, synchronizedHeader.frameSize);

  long position = input.getPosition();
  long length = input.getLength();

  // Check if there is a Xing header.
  int xingBase = (synchronizedHeader.version & 1) != 0
      ? (synchronizedHeader.channels != 1 ? 36 : 21) // MPEG 1
      : (synchronizedHeader.channels != 1 ? 21 : 13); // MPEG 2 or 2.5
  frame.setPosition(xingBase);
  int headerData = frame.readInt();
  if (headerData == XING_HEADER || headerData == INFO_HEADER) {
    seeker = XingSeeker.create(synchronizedHeader, frame, position, length);
    if (seeker != null && gaplessInfo == null) {
      // If there is a Xing header, read gapless playback metadata at a fixed offset.
      input.resetPeekPosition();
      input.advancePeekPosition(xingBase + 141);
      input.peekFully(scratch.data, 0, 3);
      scratch.setPosition(0);
      gaplessInfo = GaplessInfo.createFromXingHeaderValue(scratch.readUnsignedInt24());
    }
    input.skipFully(synchronizedHeader.frameSize);
  } else {
    // Check if there is a VBRI header.
    frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes.
    headerData = frame.readInt();
    if (headerData == VBRI_HEADER) {
      seeker = VbriSeeker.create(synchronizedHeader, frame, position, length);
      input.skipFully(synchronizedHeader.frameSize);
    }
  }

  if (seeker == null) {
    // Repopulate the synchronized header in case we had to skip an invalid seeking header, which
    // would give an invalid CBR bitrate.
    input.resetPeekPosition();
    input.peekFully(scratch.data, 0, 4);
    scratch.setPosition(0);
    MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
    seeker = new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate, length);
  }
}
项目:ExoPlayer-Demo    文件:VbriSeeker.java   
/**
 * Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *     'VBRI' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @param inputLength The length of the stream in bytes.
 * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static VbriSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position, long inputLength) {
  frame.skipBytes(10);
  int numFrames = frame.readInt();
  if (numFrames <= 0) {
    return null;
  }
  int sampleRate = mpegAudioHeader.sampleRate;
  long durationUs = Util.scaleLargeTimestamp(numFrames,
      C.MICROS_PER_SECOND * (sampleRate >= 32000 ? 1152 : 576), sampleRate);
  int entryCount = frame.readUnsignedShort();
  int scale = frame.readUnsignedShort();
  int entrySize = frame.readUnsignedShort();
  frame.skipBytes(2);

  // Skip the frame containing the VBRI header.
  position += mpegAudioHeader.frameSize;

  // Read table of contents entries.
  long[] timesUs = new long[entryCount + 1];
  long[] positions = new long[entryCount + 1];
  timesUs[0] = 0L;
  positions[0] = position;
  for (int index = 1; index < timesUs.length; index++) {
    int segmentSize;
    switch (entrySize) {
      case 1:
        segmentSize = frame.readUnsignedByte();
        break;
      case 2:
        segmentSize = frame.readUnsignedShort();
        break;
      case 3:
        segmentSize = frame.readUnsignedInt24();
        break;
      case 4:
        segmentSize = frame.readUnsignedIntToInt();
        break;
      default:
        return null;
    }
    position += segmentSize * scale;
    timesUs[index] = index * durationUs / entryCount;
    positions[index] =
        inputLength == C.LENGTH_UNBOUNDED ? position : Math.min(inputLength, position);
  }
  return new VbriSeeker(timesUs, positions, durationUs);
}
项目:ExoPlayer    文件:Mp3Extractor.java   
/** Constructs a new {@link Mp3Extractor}. */
public Mp3Extractor() {
  inputBuffer = new BufferingInput(MpegAudioHeader.MAX_FRAME_SIZE_BYTES * 3);
  scratch = new ParsableByteArray(4);
  synchronizedHeader = new MpegAudioHeader();
}
项目:ExoPlayer    文件:Mp3Extractor.java   
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
  ParsableByteArray scratch = new ParsableByteArray(4);
  int startPosition = 0;
  input.peekFully(scratch.data, 0, 3);
  if (scratch.readUnsignedInt24() == ID3_TAG) {
    input.advancePeekPosition(3);
    input.peekFully(scratch.data, 0, 4);
    int headerLength = ((scratch.data[0] & 0x7F) << 21) | ((scratch.data[1] & 0x7F) << 14)
        | ((scratch.data[2] & 0x7F) << 7) | (scratch.data[3] & 0x7F);
    input.advancePeekPosition(headerLength);
    startPosition = 3 + 3 + 4 + headerLength;
  } else {
    input.resetPeekPosition();
  }

  // Try to find four consecutive valid MPEG audio frames.
  int headerPosition = startPosition;
  int validFrameCount = 0;
  int candidateSynchronizedHeaderData = 0;
  while (true) {
    if (headerPosition - startPosition >= MAX_SNIFF_BYTES) {
      return false;
    }

    input.peekFully(scratch.data, 0, 4);
    scratch.setPosition(0);
    int headerData = scratch.readInt();
    int frameSize;
    if ((candidateSynchronizedHeaderData != 0
        && (headerData & HEADER_MASK) != (candidateSynchronizedHeaderData & HEADER_MASK))
        || (frameSize = MpegAudioHeader.getFrameSize(headerData)) == -1) {
      validFrameCount = 0;
      candidateSynchronizedHeaderData = 0;

      // Try reading a header starting at the next byte.
      input.resetPeekPosition();
      input.advancePeekPosition(++headerPosition);
      continue;
    }

    if (validFrameCount == 0) {
      candidateSynchronizedHeaderData = headerData;
    }

    // The header was valid and matching (if appropriate). Check another or end synchronization.
    if (++validFrameCount == 4) {
      return true;
    }

    // Look for more headers.
    input.advancePeekPosition(frameSize - 4);
  }
}
项目:ExoPlayer    文件:VbriSeeker.java   
/**
 * Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
 * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
 * caller should reset it.
 *
 * @param mpegAudioHeader The MPEG audio header associated with the frame.
 * @param frame The data in this audio frame, with its position set to immediately after the
 *     'VBRI' tag.
 * @param position The position (byte offset) of the start of this frame in the stream.
 * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static VbriSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame,
    long position) {
  frame.skipBytes(10);
  int numFrames = frame.readInt();
  if (numFrames <= 0) {
    return null;
  }
  int sampleRate = mpegAudioHeader.sampleRate;
  long durationUs = Util.scaleLargeTimestamp(
      numFrames, 1000000L * (sampleRate >= 32000 ? 1152 : 576), sampleRate);
  int numEntries = frame.readUnsignedShort();
  int scale = frame.readUnsignedShort();
  int entrySize = frame.readUnsignedShort();

  // Read entries in the VBRI header.
  long[] timesUs = new long[numEntries];
  long[] offsets = new long[numEntries];
  long segmentDurationUs = durationUs / numEntries;
  long now = 0;
  int segmentIndex = 0;
  while (segmentIndex < numEntries) {
    int numBytes;
    switch (entrySize) {
      case 1:
        numBytes = frame.readUnsignedByte();
        break;
      case 2:
        numBytes = frame.readUnsignedShort();
        break;
      case 3:
        numBytes = frame.readUnsignedInt24();
        break;
      case 4:
        numBytes = frame.readUnsignedIntToInt();
        break;
      default:
        return null;
    }
    now += segmentDurationUs;
    timesUs[segmentIndex] = now;
    position += numBytes * scale;
    offsets[segmentIndex] = position;

    segmentIndex++;
  }
  return new VbriSeeker(timesUs, offsets, position + mpegAudioHeader.frameSize, durationUs);
}