public final class

FlacStreamMetadata

extends java.lang.Object

 java.lang.Object

↳androidx.media3.extractor.FlacStreamMetadata

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-extractor', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-extractor
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-extractor:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

Holder for FLAC metadata.

Summary

Fields
public final intbitsPerSample

Number of bits per sample.

public final intbitsPerSampleLookupKey

Lookup key corresponding to the number of bits per sample of the stream, or FlacStreamMetadata.NOT_IN_LOOKUP_TABLE if it is not in the lookup table.

public final intchannels

Number of audio channels.

public final intmaxBlockSizeSamples

Maximum number of samples per block.

public final intmaxFrameSize

Maximum frame size in bytes, or 0 if the value is unknown.

public final intminBlockSizeSamples

Minimum number of samples per block.

public final intminFrameSize

Minimum frame size in bytes, or 0 if the value is unknown.

public static final intNOT_IN_LOOKUP_TABLE

Indicates that a value is not in the corresponding lookup table.

public final intsampleRate

Sample rate in Hertz.

public final intsampleRateLookupKey

Lookup key corresponding to the stream sample rate, or FlacStreamMetadata.NOT_IN_LOOKUP_TABLE if it is not in the lookup table.

public final FlacStreamMetadata.SeekTableseekTable

Seek table, or null if it is not provided.

public final longtotalSamples

Total number of samples, or 0 if the value is unknown.

Constructors
publicFlacStreamMetadata(byte[] data[], int offset)

Parses binary FLAC stream info metadata.

publicFlacStreamMetadata(int minBlockSizeSamples, int maxBlockSizeSamples, int minFrameSize, int maxFrameSize, int sampleRate, int channels, int bitsPerSample, long totalSamples, java.util.ArrayList<java.lang.String> vorbisComments, java.util.ArrayList<PictureFrame> pictureFrames)

Methods
public FlacStreamMetadatacopyWithPictureFrames(java.util.List<PictureFrame> pictureFrames)

Returns a copy of this with the given picture frames added to the metadata.

public FlacStreamMetadatacopyWithSeekTable(FlacStreamMetadata.SeekTable seekTable)

Returns a copy of this with the seek table replaced by the one given.

public FlacStreamMetadatacopyWithVorbisComments(java.util.List<java.lang.String> vorbisComments)

Returns a copy of this with the given Vorbis comments added to the metadata.

public longgetApproxBytesPerFrame()

Returns the approximate number of bytes per frame for the current FLAC stream.

public intgetDecodedBitrate()

Returns the bitrate of the stream after it's decoded into PCM.

public longgetDurationUs()

Returns the duration of the FLAC stream in microseconds, or C.TIME_UNSET if the total number of samples if unknown.

public FormatgetFormat(byte[] streamMarkerAndInfoBlock[], Metadata id3Metadata)

Returns a Format extracted from the FLAC stream metadata.

public intgetMaxDecodedFrameSize()

Returns the maximum size for a decoded frame from the FLAC stream.

public MetadatagetMetadataCopyWithAppendedEntriesFrom(Metadata other)

Returns a copy of the content metadata with entries from other appended.

public longgetSampleNumber(long timeUs)

Returns the sample number of the sample at a given time.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int NOT_IN_LOOKUP_TABLE

Indicates that a value is not in the corresponding lookup table.

public final int minBlockSizeSamples

Minimum number of samples per block.

public final int maxBlockSizeSamples

Maximum number of samples per block.

public final int minFrameSize

Minimum frame size in bytes, or 0 if the value is unknown.

public final int maxFrameSize

Maximum frame size in bytes, or 0 if the value is unknown.

public final int sampleRate

Sample rate in Hertz.

public final int sampleRateLookupKey

Lookup key corresponding to the stream sample rate, or FlacStreamMetadata.NOT_IN_LOOKUP_TABLE if it is not in the lookup table.

This key is used to indicate the sample rate in the frame header for the most common values.

The sample rate lookup table is described in https://xiph.org/flac/format.html#frame_header.

public final int channels

Number of audio channels.

public final int bitsPerSample

Number of bits per sample.

public final int bitsPerSampleLookupKey

Lookup key corresponding to the number of bits per sample of the stream, or FlacStreamMetadata.NOT_IN_LOOKUP_TABLE if it is not in the lookup table.

This key is used to indicate the number of bits per sample in the frame header for the most common values.

The sample size lookup table is described in https://xiph.org/flac/format.html#frame_header.

public final long totalSamples

Total number of samples, or 0 if the value is unknown.

public final FlacStreamMetadata.SeekTable seekTable

Seek table, or null if it is not provided.

Constructors

public FlacStreamMetadata(byte[] data[], int offset)

Parses binary FLAC stream info metadata.

Parameters:

data: An array containing binary FLAC stream info block.
offset: The offset of the stream info block in data, excluding the header (i.e. the offset points to the first byte of the minimum block size).

public FlacStreamMetadata(int minBlockSizeSamples, int maxBlockSizeSamples, int minFrameSize, int maxFrameSize, int sampleRate, int channels, int bitsPerSample, long totalSamples, java.util.ArrayList<java.lang.String> vorbisComments, java.util.ArrayList<PictureFrame> pictureFrames)

Methods

public int getMaxDecodedFrameSize()

Returns the maximum size for a decoded frame from the FLAC stream.

public int getDecodedBitrate()

Returns the bitrate of the stream after it's decoded into PCM.

public long getDurationUs()

Returns the duration of the FLAC stream in microseconds, or C.TIME_UNSET if the total number of samples if unknown.

public long getSampleNumber(long timeUs)

Returns the sample number of the sample at a given time.

Parameters:

timeUs: Time position in microseconds in the FLAC stream.

Returns:

The sample number corresponding to the time position.

public long getApproxBytesPerFrame()

Returns the approximate number of bytes per frame for the current FLAC stream.

public Format getFormat(byte[] streamMarkerAndInfoBlock[], Metadata id3Metadata)

Returns a Format extracted from the FLAC stream metadata.

streamMarkerAndInfoBlock is updated to set the bit corresponding to the stream info last metadata block flag to true.

Parameters:

streamMarkerAndInfoBlock: An array containing the FLAC stream marker followed by the stream info block.
id3Metadata: The ID3 metadata of the stream, or null if there is no such data.

Returns:

The extracted Format.

public Metadata getMetadataCopyWithAppendedEntriesFrom(Metadata other)

Returns a copy of the content metadata with entries from other appended.

public FlacStreamMetadata copyWithSeekTable(FlacStreamMetadata.SeekTable seekTable)

Returns a copy of this with the seek table replaced by the one given.

public FlacStreamMetadata copyWithVorbisComments(java.util.List<java.lang.String> vorbisComments)

Returns a copy of this with the given Vorbis comments added to the metadata.

public FlacStreamMetadata copyWithPictureFrames(java.util.List<PictureFrame> pictureFrames)

Returns a copy of this with the given picture frames added to the metadata.

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.extractor;

import static androidx.media3.extractor.VorbisUtil.parseVorbisComments;

import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Metadata;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.ParsableBitArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.extractor.metadata.flac.PictureFrame;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Holder for FLAC metadata.
 *
 * @see <a href="https://xiph.org/flac/format.html#metadata_block_streaminfo">FLAC format
 *     METADATA_BLOCK_STREAMINFO</a>
 * @see <a href="https://xiph.org/flac/format.html#metadata_block_seektable">FLAC format
 *     METADATA_BLOCK_SEEKTABLE</a>
 * @see <a href="https://xiph.org/flac/format.html#metadata_block_vorbis_comment">FLAC format
 *     METADATA_BLOCK_VORBIS_COMMENT</a>
 * @see <a href="https://xiph.org/flac/format.html#metadata_block_picture">FLAC format
 *     METADATA_BLOCK_PICTURE</a>
 */
@UnstableApi
public final class FlacStreamMetadata {

  /** A FLAC seek table. */
  public static class SeekTable {
    /** Seek points sample numbers. */
    public final long[] pointSampleNumbers;
    /** Seek points byte offsets from the first frame. */
    public final long[] pointOffsets;

    public SeekTable(long[] pointSampleNumbers, long[] pointOffsets) {
      this.pointSampleNumbers = pointSampleNumbers;
      this.pointOffsets = pointOffsets;
    }
  }

  private static final String TAG = "FlacStreamMetadata";

  /** Indicates that a value is not in the corresponding lookup table. */
  public static final int NOT_IN_LOOKUP_TABLE = -1;

  /** Minimum number of samples per block. */
  public final int minBlockSizeSamples;
  /** Maximum number of samples per block. */
  public final int maxBlockSizeSamples;
  /** Minimum frame size in bytes, or 0 if the value is unknown. */
  public final int minFrameSize;
  /** Maximum frame size in bytes, or 0 if the value is unknown. */
  public final int maxFrameSize;
  /** Sample rate in Hertz. */
  public final int sampleRate;
  /**
   * Lookup key corresponding to the stream sample rate, or {@link #NOT_IN_LOOKUP_TABLE} if it is
   * not in the lookup table.
   *
   * <p>This key is used to indicate the sample rate in the frame header for the most common values.
   *
   * <p>The sample rate lookup table is described in https://xiph.org/flac/format.html#frame_header.
   */
  public final int sampleRateLookupKey;
  /** Number of audio channels. */
  public final int channels;
  /** Number of bits per sample. */
  public final int bitsPerSample;
  /**
   * Lookup key corresponding to the number of bits per sample of the stream, or {@link
   * #NOT_IN_LOOKUP_TABLE} if it is not in the lookup table.
   *
   * <p>This key is used to indicate the number of bits per sample in the frame header for the most
   * common values.
   *
   * <p>The sample size lookup table is described in https://xiph.org/flac/format.html#frame_header.
   */
  public final int bitsPerSampleLookupKey;
  /** Total number of samples, or 0 if the value is unknown. */
  public final long totalSamples;
  /** Seek table, or {@code null} if it is not provided. */
  @Nullable public final SeekTable seekTable;
  /** Content metadata, or {@code null} if it is not provided. */
  @Nullable private final Metadata metadata;

  /**
   * Parses binary FLAC stream info metadata.
   *
   * @param data An array containing binary FLAC stream info block.
   * @param offset The offset of the stream info block in {@code data}, excluding the header (i.e.
   *     the offset points to the first byte of the minimum block size).
   */
  public FlacStreamMetadata(byte[] data, int offset) {
    ParsableBitArray scratch = new ParsableBitArray(data);
    scratch.setPosition(offset * 8);
    minBlockSizeSamples = scratch.readBits(16);
    maxBlockSizeSamples = scratch.readBits(16);
    minFrameSize = scratch.readBits(24);
    maxFrameSize = scratch.readBits(24);
    sampleRate = scratch.readBits(20);
    sampleRateLookupKey = getSampleRateLookupKey(sampleRate);
    channels = scratch.readBits(3) + 1;
    bitsPerSample = scratch.readBits(5) + 1;
    bitsPerSampleLookupKey = getBitsPerSampleLookupKey(bitsPerSample);
    totalSamples = scratch.readBitsToLong(36);
    seekTable = null;
    metadata = null;
  }

  // Used in native code.
  public FlacStreamMetadata(
      int minBlockSizeSamples,
      int maxBlockSizeSamples,
      int minFrameSize,
      int maxFrameSize,
      int sampleRate,
      int channels,
      int bitsPerSample,
      long totalSamples,
      ArrayList<String> vorbisComments,
      ArrayList<PictureFrame> pictureFrames) {
    this(
        minBlockSizeSamples,
        maxBlockSizeSamples,
        minFrameSize,
        maxFrameSize,
        sampleRate,
        channels,
        bitsPerSample,
        totalSamples,
        /* seekTable= */ null,
        concatenateVorbisMetadata(vorbisComments, pictureFrames));
  }

  private FlacStreamMetadata(
      int minBlockSizeSamples,
      int maxBlockSizeSamples,
      int minFrameSize,
      int maxFrameSize,
      int sampleRate,
      int channels,
      int bitsPerSample,
      long totalSamples,
      @Nullable SeekTable seekTable,
      @Nullable Metadata metadata) {
    this.minBlockSizeSamples = minBlockSizeSamples;
    this.maxBlockSizeSamples = maxBlockSizeSamples;
    this.minFrameSize = minFrameSize;
    this.maxFrameSize = maxFrameSize;
    this.sampleRate = sampleRate;
    this.sampleRateLookupKey = getSampleRateLookupKey(sampleRate);
    this.channels = channels;
    this.bitsPerSample = bitsPerSample;
    this.bitsPerSampleLookupKey = getBitsPerSampleLookupKey(bitsPerSample);
    this.totalSamples = totalSamples;
    this.seekTable = seekTable;
    this.metadata = metadata;
  }

  /** Returns the maximum size for a decoded frame from the FLAC stream. */
  public int getMaxDecodedFrameSize() {
    return maxBlockSizeSamples * channels * (bitsPerSample / 8);
  }

  /** Returns the bitrate of the stream after it's decoded into PCM. */
  public int getDecodedBitrate() {
    return bitsPerSample * sampleRate * channels;
  }

  /**
   * Returns the duration of the FLAC stream in microseconds, or {@link C#TIME_UNSET} if the total
   * number of samples if unknown.
   */
  public long getDurationUs() {
    return totalSamples == 0 ? C.TIME_UNSET : totalSamples * C.MICROS_PER_SECOND / sampleRate;
  }

  /**
   * Returns the sample number of the sample at a given time.
   *
   * @param timeUs Time position in microseconds in the FLAC stream.
   * @return The sample number corresponding to the time position.
   */
  public long getSampleNumber(long timeUs) {
    long sampleNumber = (timeUs * sampleRate) / C.MICROS_PER_SECOND;
    return Util.constrainValue(sampleNumber, /* min= */ 0, totalSamples - 1);
  }

  /** Returns the approximate number of bytes per frame for the current FLAC stream. */
  public long getApproxBytesPerFrame() {
    long approxBytesPerFrame;
    if (maxFrameSize > 0) {
      approxBytesPerFrame = ((long) maxFrameSize + minFrameSize) / 2 + 1;
    } else {
      // Uses the stream's block-size if it's a known fixed block-size stream, otherwise uses the
      // default value for FLAC block-size, which is 4096.
      long blockSizeSamples =
          (minBlockSizeSamples == maxBlockSizeSamples && minBlockSizeSamples > 0)
              ? minBlockSizeSamples
              : 4096;
      approxBytesPerFrame = (blockSizeSamples * channels * bitsPerSample) / 8 + 64;
    }
    return approxBytesPerFrame;
  }

  /**
   * Returns a {@link Format} extracted from the FLAC stream metadata.
   *
   * <p>{@code streamMarkerAndInfoBlock} is updated to set the bit corresponding to the stream info
   * last metadata block flag to true.
   *
   * @param streamMarkerAndInfoBlock An array containing the FLAC stream marker followed by the
   *     stream info block.
   * @param id3Metadata The ID3 metadata of the stream, or {@code null} if there is no such data.
   * @return The extracted {@link Format}.
   */
  public Format getFormat(byte[] streamMarkerAndInfoBlock, @Nullable Metadata id3Metadata) {
    // Set the last metadata block flag, ignore the other blocks.
    streamMarkerAndInfoBlock[4] = (byte) 0x80;
    int maxInputSize = maxFrameSize > 0 ? maxFrameSize : Format.NO_VALUE;
    @Nullable Metadata metadataWithId3 = getMetadataCopyWithAppendedEntriesFrom(id3Metadata);
    return new Format.Builder()
        .setSampleMimeType(MimeTypes.AUDIO_FLAC)
        .setMaxInputSize(maxInputSize)
        .setChannelCount(channels)
        .setSampleRate(sampleRate)
        .setInitializationData(Collections.singletonList(streamMarkerAndInfoBlock))
        .setMetadata(metadataWithId3)
        .build();
  }

  /** Returns a copy of the content metadata with entries from {@code other} appended. */
  @Nullable
  public Metadata getMetadataCopyWithAppendedEntriesFrom(@Nullable Metadata other) {
    return metadata == null ? other : metadata.copyWithAppendedEntriesFrom(other);
  }

  /** Returns a copy of {@code this} with the seek table replaced by the one given. */
  public FlacStreamMetadata copyWithSeekTable(@Nullable SeekTable seekTable) {
    return new FlacStreamMetadata(
        minBlockSizeSamples,
        maxBlockSizeSamples,
        minFrameSize,
        maxFrameSize,
        sampleRate,
        channels,
        bitsPerSample,
        totalSamples,
        seekTable,
        metadata);
  }

  /** Returns a copy of {@code this} with the given Vorbis comments added to the metadata. */
  public FlacStreamMetadata copyWithVorbisComments(List<String> vorbisComments) {
    @Nullable
    Metadata appendedMetadata =
        getMetadataCopyWithAppendedEntriesFrom(parseVorbisComments(vorbisComments));
    return new FlacStreamMetadata(
        minBlockSizeSamples,
        maxBlockSizeSamples,
        minFrameSize,
        maxFrameSize,
        sampleRate,
        channels,
        bitsPerSample,
        totalSamples,
        seekTable,
        appendedMetadata);
  }

  /** Returns a copy of {@code this} with the given picture frames added to the metadata. */
  public FlacStreamMetadata copyWithPictureFrames(List<PictureFrame> pictureFrames) {
    @Nullable
    Metadata appendedMetadata = getMetadataCopyWithAppendedEntriesFrom(new Metadata(pictureFrames));
    return new FlacStreamMetadata(
        minBlockSizeSamples,
        maxBlockSizeSamples,
        minFrameSize,
        maxFrameSize,
        sampleRate,
        channels,
        bitsPerSample,
        totalSamples,
        seekTable,
        appendedMetadata);
  }

  /**
   * Returns a new {@link Metadata} instance created from {@code vorbisComments} and {@code
   * pictureFrames}.
   */
  @Nullable
  private static Metadata concatenateVorbisMetadata(
      List<String> vorbisComments, List<PictureFrame> pictureFrames) {
    @Nullable Metadata parsedVorbisComments = parseVorbisComments(vorbisComments);
    if (parsedVorbisComments == null && pictureFrames.isEmpty()) {
      return null;
    }
    return new Metadata(pictureFrames).copyWithAppendedEntriesFrom(parsedVorbisComments);
  }

  private static int getSampleRateLookupKey(int sampleRate) {
    switch (sampleRate) {
      case 88200:
        return 1;
      case 176400:
        return 2;
      case 192000:
        return 3;
      case 8000:
        return 4;
      case 16000:
        return 5;
      case 22050:
        return 6;
      case 24000:
        return 7;
      case 32000:
        return 8;
      case 44100:
        return 9;
      case 48000:
        return 10;
      case 96000:
        return 11;
      default:
        return NOT_IN_LOOKUP_TABLE;
    }
  }

  private static int getBitsPerSampleLookupKey(int bitsPerSample) {
    switch (bitsPerSample) {
      case 8:
        return 1;
      case 12:
        return 2;
      case 16:
        return 4;
      case 20:
        return 5;
      case 24:
        return 6;
      default:
        return NOT_IN_LOOKUP_TABLE;
    }
  }
}