com.google.android.exoplayer2.extractor.MpegAudioHeader Java Examples

The following examples show how to use com.google.android.exoplayer2.extractor.MpegAudioHeader. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Mp3Extractor.java    From K-Sonic with MIT License 6 votes vote down vote up
@Override
public int read(ExtractorInput input, PositionHolder seekPosition)
    throws IOException, InterruptedException {
  if (synchronizedHeaderData == 0) {
    try {
      synchronize(input, false);
    } catch (EOFException e) {
      return RESULT_END_OF_INPUT;
    }
  }
  if (seeker == null) {
    seeker = setupSeeker(input);
    extractorOutput.seekMap(seeker);
    trackOutput.format(Format.createAudioSampleFormat(null, synchronizedHeader.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, synchronizedHeader.channels,
        synchronizedHeader.sampleRate, Format.NO_VALUE, gaplessInfoHolder.encoderDelay,
        gaplessInfoHolder.encoderPadding, null, null, 0, null,
        (flags & FLAG_DISABLE_ID3_METADATA) != 0 ? null : metadata));
  }
  return readSample(input);
}
 
Example #2
Source File: MpegAudioReader.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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;
    Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
        null, null, 0, language);
    output.format(format);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
 
Example #3
Source File: Mp3Extractor.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (!extractorInput.peekFully(scratch.data, 0, 4, true)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if (!headersMatch(sampleHeaderData, synchronizedHeaderData)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example #4
Source File: Mp3Extractor.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate.
 */
private Seeker getConstantBitrateSeeker(ExtractorInput input)
    throws IOException, InterruptedException {
  input.peekFully(scratch.data, 0, 4);
  scratch.setPosition(0);
  MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  return new ConstantBitrateSeeker(input.getLength(), input.getPosition(), synchronizedHeader);
}
 
Example #5
Source File: MpegAudioReader.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
public MpegAudioReader(String language) {
  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();
  this.language = language;
}
 
Example #6
Source File: MpegAudioReader.java    From K-Sonic with MIT License 5 votes vote down vote up
public MpegAudioReader(String language) {
  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();
  this.language = language;
}
 
Example #7
Source File: MpegAudioReader.java    From K-Sonic with MIT License 5 votes vote down vote up
/**
 * 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;
    Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
        null, null, 0, language);
    output.format(format);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
 
Example #8
Source File: Mp3Extractor.java    From K-Sonic with MIT License 5 votes vote down vote up
/**
 * Constructs a new {@link Mp3Extractor}.
 *
 * @param flags Flags that control the extractor's behavior.
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or
 *     {@link C#TIME_UNSET} if forcing is not required.
 */
public Mp3Extractor(@Flags int flags, long forcedFirstSampleTimestampUs) {
  this.flags = flags;
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(SCRATCH_LENGTH);
  synchronizedHeader = new MpegAudioHeader();
  gaplessInfoHolder = new GaplessInfoHolder();
  basisTimeUs = C.TIME_UNSET;
}
 
Example #9
Source File: Mp3Extractor.java    From K-Sonic with MIT License 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (!extractorInput.peekFully(scratch.data, 0, 4, true)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if ((sampleHeaderData & HEADER_MASK) != (synchronizedHeaderData & HEADER_MASK)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example #10
Source File: XingSeeker.java    From K-Sonic with MIT License 5 votes vote down vote up
/**
 * 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);
}
 
Example #11
Source File: MpegAudioReader.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
public MpegAudioReader(String language) {
  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();
  this.language = language;
}
 
Example #12
Source File: XingSeeker.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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 inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @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.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static XingSeeker create(long inputLength, long position, MpegAudioHeader mpegAudioHeader,
    ParsableByteArray frame) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;

  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(position, mpegAudioHeader.frameSize, durationUs);
  }

  long dataSize = frame.readUnsignedIntToInt();
  long[] tableOfContents = new long[100];
  for (int i = 0; i < 100; 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();

  if (inputLength != C.LENGTH_UNSET && inputLength != position + dataSize) {
    Log.w(TAG, "XING data size mismatch: " + inputLength + ", " + (position + dataSize));
  }
  return new XingSeeker(position, mpegAudioHeader.frameSize, durationUs, dataSize,
      tableOfContents);
}
 
Example #13
Source File: Mp3Extractor.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * @param flags Flags that control the extractor's behavior.
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or
 *     {@link C#TIME_UNSET} if forcing is not required.
 */
public Mp3Extractor(@Flags int flags, long forcedFirstSampleTimestampUs) {
  this.flags = flags;
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(SCRATCH_LENGTH);
  synchronizedHeader = new MpegAudioHeader();
  gaplessInfoHolder = new GaplessInfoHolder();
  basisTimeUs = C.TIME_UNSET;
  id3Peeker = new Id3Peeker();
}
 
Example #14
Source File: Mp3Extractor.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (peekEndOfStreamOrHeader(extractorInput)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if (!headersMatch(sampleHeaderData, synchronizedHeaderData)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example #15
Source File: Mp3Extractor.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate.
 */
private Seeker getConstantBitrateSeeker(ExtractorInput input)
    throws IOException, InterruptedException {
  input.peekFully(scratch.data, 0, 4);
  scratch.setPosition(0);
  MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  return new ConstantBitrateSeeker(input.getLength(), input.getPosition(), synchronizedHeader);
}
 
Example #16
Source File: XingSeeker.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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 inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @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.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static @Nullable XingSeeker create(
    long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;

  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(position, mpegAudioHeader.frameSize, durationUs);
  }

  long dataSize = frame.readUnsignedIntToInt();
  long[] tableOfContents = new long[100];
  for (int i = 0; i < 100; 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();

  if (inputLength != C.LENGTH_UNSET && inputLength != position + dataSize) {
    Log.w(TAG, "XING data size mismatch: " + inputLength + ", " + (position + dataSize));
  }
  return new XingSeeker(
      position, mpegAudioHeader.frameSize, durationUs, dataSize, tableOfContents);
}
 
Example #17
Source File: MpegAudioReader.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
public MpegAudioReader(String language) {
  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();
  this.language = language;
}
 
Example #18
Source File: MpegAudioReader.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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;
    Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
        null, null, 0, language);
    output.format(format);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
 
Example #19
Source File: Mp3Extractor.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
/**
 * @param flags Flags that control the extractor's behavior.
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or
 *     {@link C#TIME_UNSET} if forcing is not required.
 */
public Mp3Extractor(@Flags int flags, long forcedFirstSampleTimestampUs) {
  this.flags = flags;
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(SCRATCH_LENGTH);
  synchronizedHeader = new MpegAudioHeader();
  gaplessInfoHolder = new GaplessInfoHolder();
  basisTimeUs = C.TIME_UNSET;
  id3Peeker = new Id3Peeker();
}
 
Example #20
Source File: Mp3Extractor.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (peekEndOfStreamOrHeader(extractorInput)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if (!headersMatch(sampleHeaderData, synchronizedHeaderData)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example #21
Source File: Mp3Extractor.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate.
 */
private Seeker getConstantBitrateSeeker(ExtractorInput input)
    throws IOException, InterruptedException {
  input.peekFully(scratch.data, 0, 4);
  scratch.setPosition(0);
  MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  return new ConstantBitrateSeeker(input.getLength(), input.getPosition(), synchronizedHeader);
}
 
Example #22
Source File: XingSeeker.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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 inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @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.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static @Nullable XingSeeker create(
    long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;

  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(position, mpegAudioHeader.frameSize, durationUs);
  }

  long dataSize = frame.readUnsignedIntToInt();
  long[] tableOfContents = new long[100];
  for (int i = 0; i < 100; 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();

  if (inputLength != C.LENGTH_UNSET && inputLength != position + dataSize) {
    Log.w(TAG, "XING data size mismatch: " + inputLength + ", " + (position + dataSize));
  }
  return new XingSeeker(
      position, mpegAudioHeader.frameSize, durationUs, dataSize, tableOfContents);
}
 
Example #23
Source File: Mp3Extractor.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
/**
 * @param flags Flags that control the extractor's behavior.
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or
 *     {@link C#TIME_UNSET} if forcing is not required.
 */
public Mp3Extractor(@Flags int flags, long forcedFirstSampleTimestampUs) {
  this.flags = flags;
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(SCRATCH_LENGTH);
  synchronizedHeader = new MpegAudioHeader();
  gaplessInfoHolder = new GaplessInfoHolder();
  basisTimeUs = C.TIME_UNSET;
  id3Peeker = new Id3Peeker();
}
 
Example #24
Source File: MpegAudioReader.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * 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;
    Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
        null, null, 0, language);
    output.format(format);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}
 
Example #25
Source File: Mp3Extractor.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * @param flags Flags that control the extractor's behavior.
 * @param forcedFirstSampleTimestampUs A timestamp to force for the first sample, or
 *     {@link C#TIME_UNSET} if forcing is not required.
 */
public Mp3Extractor(@Flags int flags, long forcedFirstSampleTimestampUs) {
  this.flags = flags;
  this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
  scratch = new ParsableByteArray(SCRATCH_LENGTH);
  synchronizedHeader = new MpegAudioHeader();
  gaplessInfoHolder = new GaplessInfoHolder();
  basisTimeUs = C.TIME_UNSET;
  id3Peeker = new Id3Peeker();
}
 
Example #26
Source File: Mp3Extractor.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (peekEndOfStreamOrHeader(extractorInput)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if (!headersMatch(sampleHeaderData, synchronizedHeaderData)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example #27
Source File: Mp3Extractor.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate.
 */
private Seeker getConstantBitrateSeeker(ExtractorInput input)
    throws IOException, InterruptedException {
  input.peekFully(scratch.data, 0, 4);
  scratch.setPosition(0);
  MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
  return new ConstantBitrateSeeker(input.getLength(), input.getPosition(), synchronizedHeader);
}
 
Example #28
Source File: XingSeeker.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
/**
 * 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 inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
 * @param position The position of the start of this frame in the stream.
 * @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.
 * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required
 *     information is not present.
 */
public static @Nullable XingSeeker create(
    long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) {
  int samplesPerFrame = mpegAudioHeader.samplesPerFrame;
  int sampleRate = mpegAudioHeader.sampleRate;

  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(position, mpegAudioHeader.frameSize, durationUs);
  }

  long dataSize = frame.readUnsignedIntToInt();
  long[] tableOfContents = new long[100];
  for (int i = 0; i < 100; 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();

  if (inputLength != C.LENGTH_UNSET && inputLength != position + dataSize) {
    Log.w(TAG, "XING data size mismatch: " + inputLength + ", " + (position + dataSize));
  }
  return new XingSeeker(
      position, mpegAudioHeader.frameSize, durationUs, dataSize, tableOfContents);
}
 
Example #29
Source File: MpegAudioReader.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
public MpegAudioReader(String language) {
  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();
  this.language = language;
}
 
Example #30
Source File: MpegAudioReader.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
/**
 * 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;
    Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
        Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
        null, null, 0, language);
    output.format(format);
    hasOutputFormat = true;
  }

  headerScratch.setPosition(0);
  output.sampleData(headerScratch, HEADER_SIZE);
  state = STATE_READING_FRAME;
}