summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java250
1 files changed, 250 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java
new file mode 100644
index 0000000000..a921346e90
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/Ac4Util.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2019 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.audio;
+
+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.drm.DrmInitData;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.MimeTypes;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableBitArray;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+import java.nio.ByteBuffer;
+
+/** Utility methods for parsing AC-4 frames, which are access units in AC-4 bitstreams. */
+public final class Ac4Util {
+
+ /** Holds sample format information as presented by a syncframe header. */
+ public static final class SyncFrameInfo {
+
+ /** The bitstream version. */
+ public final int bitstreamVersion;
+ /** The audio sampling rate in Hz. */
+ public final int sampleRate;
+ /** The number of audio channels */
+ public final int channelCount;
+ /** The size of the frame. */
+ public final int frameSize;
+ /** Number of audio samples in the frame. */
+ public final int sampleCount;
+
+ private SyncFrameInfo(
+ int bitstreamVersion, int channelCount, int sampleRate, int frameSize, int sampleCount) {
+ this.bitstreamVersion = bitstreamVersion;
+ this.channelCount = channelCount;
+ this.sampleRate = sampleRate;
+ this.frameSize = frameSize;
+ this.sampleCount = sampleCount;
+ }
+ }
+
+ public static final int AC40_SYNCWORD = 0xAC40;
+ public static final int AC41_SYNCWORD = 0xAC41;
+
+ /** The channel count of AC-4 stream. */
+ // TODO: Parse AC-4 stream channel count.
+ private static final int CHANNEL_COUNT_2 = 2;
+ /**
+ * The AC-4 sync frame header size for extractor. The seven bytes are 0xAC, 0x40, 0xFF, 0xFF,
+ * sizeByte1, sizeByte2, sizeByte3. See ETSI TS 103 190-1 V1.3.1, Annex G
+ */
+ public static final int SAMPLE_HEADER_SIZE = 7;
+ /**
+ * The header size for AC-4 parser. Only needs to be as big as we need to read, not the full
+ * header size.
+ */
+ public static final int HEADER_SIZE_FOR_PARSER = 16;
+ /**
+ * Number of audio samples in the frame. Defined in IEC61937-14:2017 table 5 and 6. This table
+ * provides the number of samples per frame at the playback sampling frequency of 48 kHz. For 44.1
+ * kHz, only frame_rate_index(13) is valid and corresponding sample count is 2048.
+ */
+ private static final int[] SAMPLE_COUNT =
+ new int[] {
+ /* [ 0] 23.976 fps */ 2002,
+ /* [ 1] 24 fps */ 2000,
+ /* [ 2] 25 fps */ 1920,
+ /* [ 3] 29.97 fps */ 1601, // 1601 | 1602 | 1601 | 1602 | 1602
+ /* [ 4] 30 fps */ 1600,
+ /* [ 5] 47.95 fps */ 1001,
+ /* [ 6] 48 fps */ 1000,
+ /* [ 7] 50 fps */ 960,
+ /* [ 8] 59.94 fps */ 800, // 800 | 801 | 801 | 801 | 801
+ /* [ 9] 60 fps */ 800,
+ /* [10] 100 fps */ 480,
+ /* [11] 119.88 fps */ 400, // 400 | 400 | 401 | 400 | 401
+ /* [12] 120 fps */ 400,
+ /* [13] 23.438 fps */ 2048
+ };
+
+ /**
+ * Returns the AC-4 format given {@code data} containing the AC4SpecificBox according to ETSI TS
+ * 103 190-1 Annex E. The reading position of {@code data} will be modified.
+ *
+ * @param data The AC4SpecificBox to parse.
+ * @param trackId The track identifier to set on the format.
+ * @param language The language to set on the format.
+ * @param drmInitData {@link DrmInitData} to be included in the format.
+ * @return The AC-4 format parsed from data in the header.
+ */
+ public static Format parseAc4AnnexEFormat(
+ ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) {
+ data.skipBytes(1); // ac4_dsi_version, bitstream_version[0:5]
+ int sampleRate = ((data.readUnsignedByte() & 0x20) >> 5 == 1) ? 48000 : 44100;
+ return Format.createAudioSampleFormat(
+ trackId,
+ MimeTypes.AUDIO_AC4,
+ /* codecs= */ null,
+ /* bitrate= */ Format.NO_VALUE,
+ /* maxInputSize= */ Format.NO_VALUE,
+ CHANNEL_COUNT_2,
+ sampleRate,
+ /* initializationData= */ null,
+ drmInitData,
+ /* selectionFlags= */ 0,
+ language);
+ }
+
+ /**
+ * Returns AC-4 format information given {@code data} containing a syncframe. The reading position
+ * of {@code data} will be modified.
+ *
+ * @param data The data to parse, positioned at the start of the syncframe.
+ * @return The AC-4 format data parsed from the header.
+ */
+ public static SyncFrameInfo parseAc4SyncframeInfo(ParsableBitArray data) {
+ int headerSize = 0;
+ int syncWord = data.readBits(16);
+ headerSize += 2;
+ int frameSize = data.readBits(16);
+ headerSize += 2;
+ if (frameSize == 0xFFFF) {
+ frameSize = data.readBits(24);
+ headerSize += 3; // Extended frame_size
+ }
+ frameSize += headerSize;
+ if (syncWord == AC41_SYNCWORD) {
+ frameSize += 2; // crc_word
+ }
+ int bitstreamVersion = data.readBits(2);
+ if (bitstreamVersion == 3) {
+ bitstreamVersion += readVariableBits(data, /* bitsPerRead= */ 2);
+ }
+ int sequenceCounter = data.readBits(10);
+ if (data.readBit()) { // b_wait_frames
+ if (data.readBits(3) > 0) { // wait_frames
+ data.skipBits(2); // reserved
+ }
+ }
+ int sampleRate = data.readBit() ? 48000 : 44100;
+ int frameRateIndex = data.readBits(4);
+ int sampleCount = 0;
+ if (sampleRate == 44100 && frameRateIndex == 13) {
+ sampleCount = SAMPLE_COUNT[frameRateIndex];
+ } else if (sampleRate == 48000 && frameRateIndex < SAMPLE_COUNT.length) {
+ sampleCount = SAMPLE_COUNT[frameRateIndex];
+ switch (sequenceCounter % 5) {
+ case 1: // fall through
+ case 3:
+ if (frameRateIndex == 3 || frameRateIndex == 8) {
+ sampleCount++;
+ }
+ break;
+ case 2:
+ if (frameRateIndex == 8 || frameRateIndex == 11) {
+ sampleCount++;
+ }
+ break;
+ case 4:
+ if (frameRateIndex == 3 || frameRateIndex == 8 || frameRateIndex == 11) {
+ sampleCount++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return new SyncFrameInfo(bitstreamVersion, CHANNEL_COUNT_2, sampleRate, frameSize, sampleCount);
+ }
+
+ /**
+ * Returns the size in bytes of the given AC-4 syncframe.
+ *
+ * @param data The syncframe to parse.
+ * @param syncword The syncword value for the syncframe.
+ * @return The syncframe size in bytes, or {@link C#LENGTH_UNSET} if the input is invalid.
+ */
+ public static int parseAc4SyncframeSize(byte[] data, int syncword) {
+ if (data.length < 7) {
+ return C.LENGTH_UNSET;
+ }
+ int headerSize = 2; // syncword
+ int frameSize = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
+ headerSize += 2;
+ if (frameSize == 0xFFFF) {
+ frameSize = ((data[4] & 0xFF) << 16) | ((data[5] & 0xFF) << 8) | (data[6] & 0xFF);
+ headerSize += 3;
+ }
+ if (syncword == AC41_SYNCWORD) {
+ headerSize += 2;
+ }
+ frameSize += headerSize;
+ return frameSize;
+ }
+
+ /**
+ * Reads the number of audio samples represented by the given AC-4 syncframe. The buffer's
+ * position is not modified.
+ *
+ * @param buffer The {@link ByteBuffer} from which to read the syncframe.
+ * @return The number of audio samples represented by the syncframe.
+ */
+ public static int parseAc4SyncframeAudioSampleCount(ByteBuffer buffer) {
+ byte[] bufferBytes = new byte[HEADER_SIZE_FOR_PARSER];
+ int position = buffer.position();
+ buffer.get(bufferBytes);
+ buffer.position(position);
+ return parseAc4SyncframeInfo(new ParsableBitArray(bufferBytes)).sampleCount;
+ }
+
+ /** Populates {@code buffer} with an AC-4 sample header for a sample of the specified size. */
+ public static void getAc4SampleHeader(int size, ParsableByteArray buffer) {
+ // See ETSI TS 103 190-1 V1.3.1, Annex G.
+ buffer.reset(SAMPLE_HEADER_SIZE);
+ buffer.data[0] = (byte) 0xAC;
+ buffer.data[1] = 0x40;
+ buffer.data[2] = (byte) 0xFF;
+ buffer.data[3] = (byte) 0xFF;
+ buffer.data[4] = (byte) ((size >> 16) & 0xFF);
+ buffer.data[5] = (byte) ((size >> 8) & 0xFF);
+ buffer.data[6] = (byte) (size & 0xFF);
+ }
+
+ private static int readVariableBits(ParsableBitArray data, int bitsPerRead) {
+ int value = 0;
+ while (true) {
+ value += data.readBits(bitsPerRead);
+ if (!data.readBit()) {
+ break;
+ }
+ value++;
+ value <<= bitsPerRead;
+ }
+ return value;
+ }
+
+ private Ac4Util() {}
+}