/* * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.util; import androidx.annotation.Nullable; import org.mozilla.thirdparty.com.google.android.exoplayer2.C; import org.mozilla.thirdparty.com.google.android.exoplayer2.Format; import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.Metadata; import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.flac.PictureFrame; import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.flac.VorbisComment; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Holder for FLAC metadata. * * @see FLAC format * METADATA_BLOCK_STREAMINFO * @see FLAC format * METADATA_BLOCK_SEEKTABLE * @see FLAC format * METADATA_BLOCK_VORBIS_COMMENT * @see FLAC format * METADATA_BLOCK_PICTURE */ 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; /** Separator between the field name of a Vorbis comment and the corresponding value. */ private static final String SEPARATOR = "="; /** 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. * *
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 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. * *
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 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 {@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 Format.createAudioSampleFormat(
/* id= */ null,
MimeTypes.AUDIO_FLAC,
/* codecs= */ null,
getBitRate(),
maxInputSize,
channels,
sampleRate,
/* pcmEncoding= */ Format.NO_VALUE,
/* encoderDelay= */ 0,
/* encoderPadding= */ 0,
/* initializationData= */ Collections.singletonList(streamMarkerAndInfoBlock),
/* drmInitData= */ null,
/* selectionFlags= */ 0,
/* language= */ null,
metadataWithId3);
}
/** 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