• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.audio;
17 
18 import androidx.annotation.Nullable;
19 import com.google.android.exoplayer2.C;
20 import com.google.android.exoplayer2.Format;
21 import com.google.android.exoplayer2.drm.DrmInitData;
22 import com.google.android.exoplayer2.util.MimeTypes;
23 import com.google.android.exoplayer2.util.ParsableBitArray;
24 import com.google.android.exoplayer2.util.ParsableByteArray;
25 import java.nio.ByteBuffer;
26 
27 /** Utility methods for parsing AC-4 frames, which are access units in AC-4 bitstreams. */
28 public final class Ac4Util {
29 
30   /** Holds sample format information as presented by a syncframe header. */
31   public static final class SyncFrameInfo {
32 
33     /** The bitstream version. */
34     public final int bitstreamVersion;
35     /** The audio sampling rate in Hz. */
36     public final int sampleRate;
37     /** The number of audio channels */
38     public final int channelCount;
39     /** The size of the frame. */
40     public final int frameSize;
41     /** Number of audio samples in the frame. */
42     public final int sampleCount;
43 
SyncFrameInfo( int bitstreamVersion, int channelCount, int sampleRate, int frameSize, int sampleCount)44     private SyncFrameInfo(
45         int bitstreamVersion, int channelCount, int sampleRate, int frameSize, int sampleCount) {
46       this.bitstreamVersion = bitstreamVersion;
47       this.channelCount = channelCount;
48       this.sampleRate = sampleRate;
49       this.frameSize = frameSize;
50       this.sampleCount = sampleCount;
51     }
52   }
53 
54   public static final int AC40_SYNCWORD = 0xAC40;
55   public static final int AC41_SYNCWORD = 0xAC41;
56 
57   /** Maximum rate for an AC-4 audio stream, in bytes per second. */
58   public static final int MAX_RATE_BYTES_PER_SECOND = 2688 * 1000 / 8;
59 
60   /** The channel count of AC-4 stream. */
61   // TODO: Parse AC-4 stream channel count.
62   private static final int CHANNEL_COUNT_2 = 2;
63   /**
64    * The AC-4 sync frame header size for extractor. The seven bytes are 0xAC, 0x40, 0xFF, 0xFF,
65    * sizeByte1, sizeByte2, sizeByte3. See ETSI TS 103 190-1 V1.3.1, Annex G
66    */
67   public static final int SAMPLE_HEADER_SIZE = 7;
68   /**
69    * The header size for AC-4 parser. Only needs to be as big as we need to read, not the full
70    * header size.
71    */
72   public static final int HEADER_SIZE_FOR_PARSER = 16;
73   /**
74    * Number of audio samples in the frame. Defined in IEC61937-14:2017 table 5 and 6. This table
75    * provides the number of samples per frame at the playback sampling frequency of 48 kHz. For 44.1
76    * kHz, only frame_rate_index(13) is valid and corresponding sample count is 2048.
77    */
78   private static final int[] SAMPLE_COUNT =
79       new int[] {
80         /* [ 0]  23.976 fps */ 2002,
81         /* [ 1]  24     fps */ 2000,
82         /* [ 2]  25     fps */ 1920,
83         /* [ 3]  29.97  fps */ 1601, // 1601 | 1602 | 1601 | 1602 | 1602
84         /* [ 4]  30     fps */ 1600,
85         /* [ 5]  47.95  fps */ 1001,
86         /* [ 6]  48     fps */ 1000,
87         /* [ 7]  50     fps */ 960,
88         /* [ 8]  59.94  fps */ 800, //  800 |  801 |  801 |  801 |  801
89         /* [ 9]  60     fps */ 800,
90         /* [10] 100     fps */ 480,
91         /* [11] 119.88  fps */ 400, //  400 |  400 |  401 |  400 |  401
92         /* [12] 120     fps */ 400,
93         /* [13]  23.438 fps */ 2048
94       };
95 
96   /**
97    * Returns the AC-4 format given {@code data} containing the AC4SpecificBox according to ETSI TS
98    * 103 190-1 Annex E. The reading position of {@code data} will be modified.
99    *
100    * @param data The AC4SpecificBox to parse.
101    * @param trackId The track identifier to set on the format.
102    * @param language The language to set on the format.
103    * @param drmInitData {@link DrmInitData} to be included in the format.
104    * @return The AC-4 format parsed from data in the header.
105    */
parseAc4AnnexEFormat( ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData)106   public static Format parseAc4AnnexEFormat(
107       ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) {
108     data.skipBytes(1); // ac4_dsi_version, bitstream_version[0:5]
109     int sampleRate = ((data.readUnsignedByte() & 0x20) >> 5 == 1) ? 48000 : 44100;
110     return new Format.Builder()
111         .setId(trackId)
112         .setSampleMimeType(MimeTypes.AUDIO_AC4)
113         .setChannelCount(CHANNEL_COUNT_2)
114         .setSampleRate(sampleRate)
115         .setDrmInitData(drmInitData)
116         .setLanguage(language)
117         .build();
118   }
119 
120   /**
121    * Returns AC-4 format information given {@code data} containing a syncframe. The reading position
122    * of {@code data} will be modified.
123    *
124    * @param data The data to parse, positioned at the start of the syncframe.
125    * @return The AC-4 format data parsed from the header.
126    */
parseAc4SyncframeInfo(ParsableBitArray data)127   public static SyncFrameInfo parseAc4SyncframeInfo(ParsableBitArray data) {
128     int headerSize = 0;
129     int syncWord = data.readBits(16);
130     headerSize += 2;
131     int frameSize = data.readBits(16);
132     headerSize += 2;
133     if (frameSize == 0xFFFF) {
134       frameSize = data.readBits(24);
135       headerSize += 3; // Extended frame_size
136     }
137     frameSize += headerSize;
138     if (syncWord == AC41_SYNCWORD) {
139       frameSize += 2; // crc_word
140     }
141     int bitstreamVersion = data.readBits(2);
142     if (bitstreamVersion == 3) {
143       bitstreamVersion += readVariableBits(data, /* bitsPerRead= */ 2);
144     }
145     int sequenceCounter = data.readBits(10);
146     if (data.readBit()) { // b_wait_frames
147       if (data.readBits(3) > 0) { // wait_frames
148         data.skipBits(2); // reserved
149       }
150     }
151     int sampleRate = data.readBit() ? 48000 : 44100;
152     int frameRateIndex = data.readBits(4);
153     int sampleCount = 0;
154     if (sampleRate == 44100 && frameRateIndex == 13) {
155       sampleCount = SAMPLE_COUNT[frameRateIndex];
156     } else if (sampleRate == 48000 && frameRateIndex < SAMPLE_COUNT.length) {
157       sampleCount = SAMPLE_COUNT[frameRateIndex];
158       switch (sequenceCounter % 5) {
159         case 1: // fall through
160         case 3:
161           if (frameRateIndex == 3 || frameRateIndex == 8) {
162             sampleCount++;
163           }
164           break;
165         case 2:
166           if (frameRateIndex == 8 || frameRateIndex == 11) {
167             sampleCount++;
168           }
169           break;
170         case 4:
171           if (frameRateIndex == 3 || frameRateIndex == 8 || frameRateIndex == 11) {
172             sampleCount++;
173           }
174           break;
175         default:
176           break;
177       }
178     }
179     return new SyncFrameInfo(bitstreamVersion, CHANNEL_COUNT_2, sampleRate, frameSize, sampleCount);
180   }
181 
182   /**
183    * Returns the size in bytes of the given AC-4 syncframe.
184    *
185    * @param data The syncframe to parse.
186    * @param syncword The syncword value for the syncframe.
187    * @return The syncframe size in bytes, or {@link C#LENGTH_UNSET} if the input is invalid.
188    */
parseAc4SyncframeSize(byte[] data, int syncword)189   public static int parseAc4SyncframeSize(byte[] data, int syncword) {
190     if (data.length < 7) {
191       return C.LENGTH_UNSET;
192     }
193     int headerSize = 2; // syncword
194     int frameSize = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
195     headerSize += 2;
196     if (frameSize == 0xFFFF) {
197       frameSize = ((data[4] & 0xFF) << 16) | ((data[5] & 0xFF) << 8) | (data[6] & 0xFF);
198       headerSize += 3;
199     }
200     if (syncword == AC41_SYNCWORD) {
201       headerSize += 2;
202     }
203     frameSize += headerSize;
204     return frameSize;
205   }
206 
207   /**
208    * Reads the number of audio samples represented by the given AC-4 syncframe. The buffer's
209    * position is not modified.
210    *
211    * @param buffer The {@link ByteBuffer} from which to read the syncframe.
212    * @return The number of audio samples represented by the syncframe.
213    */
parseAc4SyncframeAudioSampleCount(ByteBuffer buffer)214   public static int parseAc4SyncframeAudioSampleCount(ByteBuffer buffer) {
215     byte[] bufferBytes = new byte[HEADER_SIZE_FOR_PARSER];
216     int position = buffer.position();
217     buffer.get(bufferBytes);
218     buffer.position(position);
219     return parseAc4SyncframeInfo(new ParsableBitArray(bufferBytes)).sampleCount;
220   }
221 
222   /** Populates {@code buffer} with an AC-4 sample header for a sample of the specified size. */
getAc4SampleHeader(int size, ParsableByteArray buffer)223   public static void getAc4SampleHeader(int size, ParsableByteArray buffer) {
224     // See ETSI TS 103 190-1 V1.3.1, Annex G.
225     buffer.reset(SAMPLE_HEADER_SIZE);
226     buffer.data[0] = (byte) 0xAC;
227     buffer.data[1] = 0x40;
228     buffer.data[2] = (byte) 0xFF;
229     buffer.data[3] = (byte) 0xFF;
230     buffer.data[4] = (byte) ((size >> 16) & 0xFF);
231     buffer.data[5] = (byte) ((size >> 8) & 0xFF);
232     buffer.data[6] = (byte) (size & 0xFF);
233   }
234 
readVariableBits(ParsableBitArray data, int bitsPerRead)235   private static int readVariableBits(ParsableBitArray data, int bitsPerRead) {
236     int value = 0;
237     while (true) {
238       value += data.readBits(bitsPerRead);
239       if (!data.readBit()) {
240         break;
241       }
242       value++;
243       value <<= bitsPerRead;
244     }
245     return value;
246   }
247 
Ac4Util()248   private Ac4Util() {}
249 }
250