summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java384
1 files changed, 384 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java
new file mode 100644
index 0000000000..9c821c47c8
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/CodecSpecificDataUtil.java
@@ -0,0 +1,384 @@
+/*
+ * 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 android.util.Pair;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.ParserException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides static utility methods for manipulating various types of codec specific data.
+ */
+public final class CodecSpecificDataUtil {
+
+ private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
+
+ private static final int AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY = 0xF;
+
+ private static final int[] AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE = new int[] {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
+ };
+
+ private static final int AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID = -1;
+ /**
+ * In the channel configurations below, <A> indicates a single channel element; (A, B) indicates a
+ * channel pair element; and [A] indicates a low-frequency effects element.
+ * The speaker mapping short forms used are:
+ * - FC: front center
+ * - BC: back center
+ * - FL/FR: front left/right
+ * - FCL/FCR: front center left/right
+ * - FTL/FTR: front top left/right
+ * - SL/SR: back surround left/right
+ * - BL/BR: back left/right
+ * - LFE: low frequency effects
+ */
+ private static final int[] AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE =
+ new int[] {
+ 0,
+ 1, /* mono: <FC> */
+ 2, /* stereo: (FL, FR) */
+ 3, /* 3.0: <FC>, (FL, FR) */
+ 4, /* 4.0: <FC>, (FL, FR), <BC> */
+ 5, /* 5.0 back: <FC>, (FL, FR), (SL, SR) */
+ 6, /* 5.1 back: <FC>, (FL, FR), (SL, SR), <BC>, [LFE] */
+ 8, /* 7.1 wide back: <FC>, (FCL, FCR), (FL, FR), (SL, SR), [LFE] */
+ AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID,
+ AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID,
+ AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID,
+ 7, /* 6.1: <FC>, (FL, FR), (SL, SR), <RC>, [LFE] */
+ 8, /* 7.1: <FC>, (FL, FR), (SL, SR), (BL, BR), [LFE] */
+ AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID,
+ 8, /* 7.1 top: <FC>, (FL, FR), (SL, SR), [LFE], (FTL, FTR) */
+ AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID
+ };
+
+ // Advanced Audio Coding Low-Complexity profile.
+ private static final int AUDIO_OBJECT_TYPE_AAC_LC = 2;
+ // Spectral Band Replication.
+ private static final int AUDIO_OBJECT_TYPE_SBR = 5;
+ // Error Resilient Bit-Sliced Arithmetic Coding.
+ private static final int AUDIO_OBJECT_TYPE_ER_BSAC = 22;
+ // Parametric Stereo.
+ private static final int AUDIO_OBJECT_TYPE_PS = 29;
+ // Escape code for extended audio object types.
+ private static final int AUDIO_OBJECT_TYPE_ESCAPE = 31;
+
+ private CodecSpecificDataUtil() {}
+
+ /**
+ * Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
+ *
+ * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
+ * @return A pair consisting of the sample rate in Hz and the channel count.
+ * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported.
+ */
+ public static Pair<Integer, Integer> parseAacAudioSpecificConfig(byte[] audioSpecificConfig)
+ throws ParserException {
+ return parseAacAudioSpecificConfig(new ParsableBitArray(audioSpecificConfig), false);
+ }
+
+ /**
+ * Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
+ *
+ * @param bitArray A {@link ParsableBitArray} containing the AudioSpecificConfig to parse. The
+ * position is advanced to the end of the AudioSpecificConfig.
+ * @param forceReadToEnd Whether the entire AudioSpecificConfig should be read. Required for
+ * knowing the length of the configuration payload.
+ * @return A pair consisting of the sample rate in Hz and the channel count.
+ * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported.
+ */
+ public static Pair<Integer, Integer> parseAacAudioSpecificConfig(
+ ParsableBitArray bitArray, boolean forceReadToEnd) throws ParserException {
+ int audioObjectType = getAacAudioObjectType(bitArray);
+ int sampleRate = getAacSamplingFrequency(bitArray);
+ int channelConfiguration = bitArray.readBits(4);
+ if (audioObjectType == AUDIO_OBJECT_TYPE_SBR || audioObjectType == AUDIO_OBJECT_TYPE_PS) {
+ // For an AAC bitstream using spectral band replication (SBR) or parametric stereo (PS) with
+ // explicit signaling, we return the extension sampling frequency as the sample rate of the
+ // content; this is identical to the sample rate of the decoded output but may differ from
+ // the sample rate set above.
+ // Use the extensionSamplingFrequencyIndex.
+ sampleRate = getAacSamplingFrequency(bitArray);
+ audioObjectType = getAacAudioObjectType(bitArray);
+ if (audioObjectType == AUDIO_OBJECT_TYPE_ER_BSAC) {
+ // Use the extensionChannelConfiguration.
+ channelConfiguration = bitArray.readBits(4);
+ }
+ }
+
+ if (forceReadToEnd) {
+ switch (audioObjectType) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 7:
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ parseGaSpecificConfig(bitArray, audioObjectType, channelConfiguration);
+ break;
+ default:
+ throw new ParserException("Unsupported audio object type: " + audioObjectType);
+ }
+ switch (audioObjectType) {
+ case 17:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ int epConfig = bitArray.readBits(2);
+ if (epConfig == 2 || epConfig == 3) {
+ throw new ParserException("Unsupported epConfig: " + epConfig);
+ }
+ break;
+ }
+ }
+ // For supported containers, bits_to_decode() is always 0.
+ int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration];
+ Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID);
+ return Pair.create(sampleRate, channelCount);
+ }
+
+ /**
+ * Builds a simple HE-AAC LC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
+ *
+ * @param sampleRate The sample rate in Hz.
+ * @param channelCount The channel count.
+ * @return The AudioSpecificConfig.
+ */
+ public static byte[] buildAacLcAudioSpecificConfig(int sampleRate, int channelCount) {
+ int sampleRateIndex = C.INDEX_UNSET;
+ for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) {
+ if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) {
+ sampleRateIndex = i;
+ }
+ }
+ int channelConfig = C.INDEX_UNSET;
+ for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE.length; ++i) {
+ if (channelCount == AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[i]) {
+ channelConfig = i;
+ }
+ }
+ if (sampleRate == C.INDEX_UNSET || channelConfig == C.INDEX_UNSET) {
+ throw new IllegalArgumentException(
+ "Invalid sample rate or number of channels: " + sampleRate + ", " + channelCount);
+ }
+ return buildAacAudioSpecificConfig(AUDIO_OBJECT_TYPE_AAC_LC, sampleRateIndex, channelConfig);
+ }
+
+ /**
+ * Builds a simple AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
+ *
+ * @param audioObjectType The audio object type.
+ * @param sampleRateIndex The sample rate index.
+ * @param channelConfig The channel configuration.
+ * @return The AudioSpecificConfig.
+ */
+ public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
+ int channelConfig) {
+ byte[] specificConfig = new byte[2];
+ specificConfig[0] = (byte) (((audioObjectType << 3) & 0xF8) | ((sampleRateIndex >> 1) & 0x07));
+ specificConfig[1] = (byte) (((sampleRateIndex << 7) & 0x80) | ((channelConfig << 3) & 0x78));
+ return specificConfig;
+ }
+
+ /**
+ * Parses an ALAC AudioSpecificConfig (i.e. an <a
+ * href="https://github.com/macosforge/alac/blob/master/ALACMagicCookieDescription.txt">ALACSpecificConfig</a>).
+ *
+ * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
+ * @return A pair consisting of the sample rate in Hz and the channel count.
+ */
+ public static Pair<Integer, Integer> parseAlacAudioSpecificConfig(byte[] audioSpecificConfig) {
+ ParsableByteArray byteArray = new ParsableByteArray(audioSpecificConfig);
+ byteArray.setPosition(9);
+ int channelCount = byteArray.readUnsignedByte();
+ byteArray.setPosition(20);
+ int sampleRate = byteArray.readUnsignedIntToInt();
+ return Pair.create(sampleRate, channelCount);
+ }
+
+ /**
+ * Builds an RFC 6381 AVC codec string using the provided parameters.
+ *
+ * @param profileIdc The encoding profile.
+ * @param constraintsFlagsAndReservedZero2Bits The constraint flags followed by the reserved zero
+ * 2 bits, all contained in the least significant byte of the integer.
+ * @param levelIdc The encoding level.
+ * @return An RFC 6381 AVC codec string built using the provided parameters.
+ */
+ public static String buildAvcCodecString(
+ int profileIdc, int constraintsFlagsAndReservedZero2Bits, int levelIdc) {
+ return String.format(
+ "avc1.%02X%02X%02X", profileIdc, constraintsFlagsAndReservedZero2Bits, levelIdc);
+ }
+
+ /**
+ * Constructs a NAL unit consisting of the NAL start code followed by the specified data.
+ *
+ * @param data An array containing the data that should follow the NAL start code.
+ * @param offset The start offset into {@code data}.
+ * @param length The number of bytes to copy from {@code data}
+ * @return The constructed NAL unit.
+ */
+ public static byte[] buildNalUnit(byte[] data, int offset, int length) {
+ byte[] nalUnit = new byte[length + NAL_START_CODE.length];
+ System.arraycopy(NAL_START_CODE, 0, nalUnit, 0, NAL_START_CODE.length);
+ System.arraycopy(data, offset, nalUnit, NAL_START_CODE.length, length);
+ return nalUnit;
+ }
+
+ /**
+ * Splits an array of NAL units.
+ *
+ * <p>If the input consists of NAL start code delimited units, then the returned array consists of
+ * the split NAL units, each of which is still prefixed with the NAL start code. For any other
+ * input, null is returned.
+ *
+ * @param data An array of data.
+ * @return The individual NAL units, or null if the input did not consist of NAL start code
+ * delimited units.
+ */
+ public static @Nullable byte[][] splitNalUnits(byte[] data) {
+ if (!isNalStartCode(data, 0)) {
+ // data does not consist of NAL start code delimited units.
+ return null;
+ }
+ List<Integer> starts = new ArrayList<>();
+ int nalUnitIndex = 0;
+ do {
+ starts.add(nalUnitIndex);
+ nalUnitIndex = findNalStartCode(data, nalUnitIndex + NAL_START_CODE.length);
+ } while (nalUnitIndex != C.INDEX_UNSET);
+ byte[][] split = new byte[starts.size()][];
+ for (int i = 0; i < starts.size(); i++) {
+ int startIndex = starts.get(i);
+ int endIndex = i < starts.size() - 1 ? starts.get(i + 1) : data.length;
+ byte[] nal = new byte[endIndex - startIndex];
+ System.arraycopy(data, startIndex, nal, 0, nal.length);
+ split[i] = nal;
+ }
+ return split;
+ }
+
+ /**
+ * Finds the next occurrence of the NAL start code from a given index.
+ *
+ * @param data The data in which to search.
+ * @param index The first index to test.
+ * @return The index of the first byte of the found start code, or {@link C#INDEX_UNSET}.
+ */
+ private static int findNalStartCode(byte[] data, int index) {
+ int endIndex = data.length - NAL_START_CODE.length;
+ for (int i = index; i <= endIndex; i++) {
+ if (isNalStartCode(data, i)) {
+ return i;
+ }
+ }
+ return C.INDEX_UNSET;
+ }
+
+ /**
+ * Tests whether there exists a NAL start code at a given index.
+ *
+ * @param data The data.
+ * @param index The index to test.
+ * @return Whether there exists a start code that begins at {@code index}.
+ */
+ private static boolean isNalStartCode(byte[] data, int index) {
+ if (data.length - index <= NAL_START_CODE.length) {
+ return false;
+ }
+ for (int j = 0; j < NAL_START_CODE.length; j++) {
+ if (data[index + j] != NAL_START_CODE[j]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the AAC audio object type as specified in 14496-3 (2005) Table 1.14.
+ *
+ * @param bitArray The bit array containing the audio specific configuration.
+ * @return The audio object type.
+ */
+ private static int getAacAudioObjectType(ParsableBitArray bitArray) {
+ int audioObjectType = bitArray.readBits(5);
+ if (audioObjectType == AUDIO_OBJECT_TYPE_ESCAPE) {
+ audioObjectType = 32 + bitArray.readBits(6);
+ }
+ return audioObjectType;
+ }
+
+ /**
+ * Returns the AAC sampling frequency (or extension sampling frequency) as specified in 14496-3
+ * (2005) Table 1.13.
+ *
+ * @param bitArray The bit array containing the audio specific configuration.
+ * @return The sampling frequency.
+ */
+ private static int getAacSamplingFrequency(ParsableBitArray bitArray) {
+ int samplingFrequency;
+ int frequencyIndex = bitArray.readBits(4);
+ if (frequencyIndex == AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY) {
+ samplingFrequency = bitArray.readBits(24);
+ } else {
+ Assertions.checkArgument(frequencyIndex < 13);
+ samplingFrequency = AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[frequencyIndex];
+ }
+ return samplingFrequency;
+ }
+
+ private static void parseGaSpecificConfig(ParsableBitArray bitArray, int audioObjectType,
+ int channelConfiguration) {
+ bitArray.skipBits(1); // frameLengthFlag.
+ boolean dependsOnCoreDecoder = bitArray.readBit();
+ if (dependsOnCoreDecoder) {
+ bitArray.skipBits(14); // coreCoderDelay.
+ }
+ boolean extensionFlag = bitArray.readBit();
+ if (channelConfiguration == 0) {
+ throw new UnsupportedOperationException(); // TODO: Implement programConfigElement();
+ }
+ if (audioObjectType == 6 || audioObjectType == 20) {
+ bitArray.skipBits(3); // layerNr.
+ }
+ if (extensionFlag) {
+ if (audioObjectType == 22) {
+ bitArray.skipBits(16); // numOfSubFrame (5), layer_length(11).
+ }
+ if (audioObjectType == 17 || audioObjectType == 19 || audioObjectType == 20
+ || audioObjectType == 23) {
+ // aacSectionDataResilienceFlag, aacScalefactorDataResilienceFlag,
+ // aacSpectralDataResilienceFlag.
+ bitArray.skipBits(3);
+ }
+ bitArray.skipBits(1); // extensionFlag3.
+ }
+ }
+
+}