Skip to content

Commit 101b94f

Browse files
committed
Remove dependency from opus module to extractor module
PiperOrigin-RevId: 405429757
1 parent 2ab7f28 commit 101b94f

File tree

5 files changed

+150
-84
lines changed

5 files changed

+150
-84
lines changed

‎extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.google.android.exoplayer2.audio.AudioSink;
2525
import com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport;
2626
import com.google.android.exoplayer2.audio.DecoderAudioRenderer;
27-
import com.google.android.exoplayer2.audio.OpusUtil;
2827
import com.google.android.exoplayer2.decoder.CryptoConfig;
2928
import com.google.android.exoplayer2.util.MimeTypes;
3029
import com.google.android.exoplayer2.util.TraceUtil;
@@ -124,6 +123,6 @@ protected OpusDecoder createDecoder(Format format, @Nullable CryptoConfig crypto
124123
protected Format getOutputFormat(OpusDecoder decoder) {
125124
@C.PcmEncoding
126125
int pcmEncoding = decoder.outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT;
127-
return Util.getPcmFormat(pcmEncoding, decoder.channelCount, OpusUtil.SAMPLE_RATE);
126+
return Util.getPcmFormat(pcmEncoding, decoder.channelCount, OpusDecoder.SAMPLE_RATE);
128127
}
129128
}

‎extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import androidx.annotation.Nullable;
2121
import androidx.annotation.VisibleForTesting;
2222
import com.google.android.exoplayer2.C;
23-
import com.google.android.exoplayer2.audio.OpusUtil;
2423
import com.google.android.exoplayer2.decoder.CryptoConfig;
2524
import com.google.android.exoplayer2.decoder.CryptoException;
2625
import com.google.android.exoplayer2.decoder.CryptoInfo;
@@ -30,13 +29,20 @@
3029
import com.google.android.exoplayer2.util.Assertions;
3130
import com.google.android.exoplayer2.util.Util;
3231
import java.nio.ByteBuffer;
32+
import java.nio.ByteOrder;
3333
import java.util.List;
3434

3535
/** Opus decoder. */
3636
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
3737
public final class OpusDecoder
3838
extends SimpleDecoder<DecoderInputBuffer, SimpleDecoderOutputBuffer, OpusDecoderException> {
3939

40+
/** Opus streams are always 48000 Hz. */
41+
/* package */ static final int SAMPLE_RATE = 48_000;
42+
43+
private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840;
44+
private static final int FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT = 3;
45+
4046
private static final int NO_ERROR = 0;
4147
private static final int DECODE_ERROR = -1;
4248
private static final int DRM_ERROR = -2;
@@ -89,14 +95,14 @@ public OpusDecoder(
8995
&& (initializationData.get(1).length != 8 || initializationData.get(2).length != 8)) {
9096
throw new OpusDecoderException("Invalid pre-skip or seek pre-roll");
9197
}
92-
preSkipSamples = OpusUtil.getPreSkipSamples(initializationData);
93-
seekPreRollSamples = OpusUtil.getSeekPreRollSamples(initializationData);
98+
preSkipSamples = getPreSkipSamples(initializationData);
99+
seekPreRollSamples = getSeekPreRollSamples(initializationData);
94100

95101
byte[] headerBytes = initializationData.get(0);
96102
if (headerBytes.length < 19) {
97103
throw new OpusDecoderException("Invalid header length");
98104
}
99-
channelCount = OpusUtil.getChannelCount(headerBytes);
105+
channelCount = getChannelCount(headerBytes);
100106
if (channelCount > 8) {
101107
throw new OpusDecoderException("Invalid channel count: " + channelCount);
102108
}
@@ -124,7 +130,7 @@ public OpusDecoder(
124130
System.arraycopy(headerBytes, 21, streamMap, 0, channelCount);
125131
}
126132
nativeDecoderContext =
127-
opusInit(OpusUtil.SAMPLE_RATE, channelCount, numStreams, numCoupled, gain, streamMap);
133+
opusInit(SAMPLE_RATE, channelCount, numStreams, numCoupled, gain, streamMap);
128134
if (nativeDecoderContext == 0) {
129135
throw new OpusDecoderException("Failed to initialize decoder");
130136
}
@@ -176,7 +182,7 @@ protected OpusDecoderException decode(
176182
inputData,
177183
inputData.limit(),
178184
outputBuffer,
179-
OpusUtil.SAMPLE_RATE,
185+
SAMPLE_RATE,
180186
cryptoConfig,
181187
cryptoInfo.mode,
182188
Assertions.checkNotNull(cryptoInfo.key),
@@ -225,6 +231,53 @@ public void release() {
225231
opusClose(nativeDecoderContext);
226232
}
227233

234+
/**
235+
* Parses the channel count from an Opus Identification Header.
236+
*
237+
* @param header An Opus Identification Header, as defined by RFC 7845.
238+
* @return The parsed channel count.
239+
*/
240+
@VisibleForTesting
241+
/* package */ static int getChannelCount(byte[] header) {
242+
return header[9] & 0xFF;
243+
}
244+
245+
/**
246+
* Returns the number of pre-skip samples specified by the given Opus codec initialization data.
247+
*
248+
* @param initializationData The codec initialization data.
249+
* @return The number of pre-skip samples.
250+
*/
251+
@VisibleForTesting
252+
/* package */ static int getPreSkipSamples(List<byte[]> initializationData) {
253+
if (initializationData.size() == FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT) {
254+
long codecDelayNs =
255+
ByteBuffer.wrap(initializationData.get(1)).order(ByteOrder.nativeOrder()).getLong();
256+
return (int) ((codecDelayNs * SAMPLE_RATE) / C.NANOS_PER_SECOND);
257+
}
258+
// Fall back to parsing directly from the Opus Identification header.
259+
byte[] headerData = initializationData.get(0);
260+
return ((headerData[11] & 0xFF) << 8) | (headerData[10] & 0xFF);
261+
}
262+
263+
/**
264+
* Returns the number of seek per-roll samples specified by the given Opus codec initialization
265+
* data.
266+
*
267+
* @param initializationData The codec initialization data.
268+
* @return The number of seek pre-roll samples.
269+
*/
270+
@VisibleForTesting
271+
/* package */ static int getSeekPreRollSamples(List<byte[]> initializationData) {
272+
if (initializationData.size() == FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT) {
273+
long seekPreRollNs =
274+
ByteBuffer.wrap(initializationData.get(2)).order(ByteOrder.nativeOrder()).getLong();
275+
return (int) ((seekPreRollNs * SAMPLE_RATE) / C.NANOS_PER_SECOND);
276+
}
277+
// Fall back to returning the default seek pre-roll.
278+
return DEFAULT_SEEK_PRE_ROLL_SAMPLES;
279+
}
280+
228281
private static int readSignedLittleEndian16(byte[] input, int offset) {
229282
int value = input[offset] & 0xFF;
230283
value |= (input[offset + 1] & 0xFF) << 8;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (C) 2021 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.ext.opus;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
20+
import androidx.test.ext.junit.runners.AndroidJUnit4;
21+
import com.google.android.exoplayer2.C;
22+
import com.google.common.collect.ImmutableList;
23+
import java.nio.ByteBuffer;
24+
import java.nio.ByteOrder;
25+
import org.junit.Test;
26+
import org.junit.runner.RunWith;
27+
28+
/** Unit tests for {@link OpusDecoder}. */
29+
@RunWith(AndroidJUnit4.class)
30+
public final class OpusDecoderTest {
31+
32+
private static final byte[] HEADER =
33+
new byte[] {79, 112, 117, 115, 72, 101, 97, 100, 0, 2, 1, 56, 0, 0, -69, -128, 0, 0, 0};
34+
35+
private static final int HEADER_PRE_SKIP_SAMPLES = 14337;
36+
37+
private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840;
38+
39+
private static final ImmutableList<byte[]> HEADER_ONLY_INITIALIZATION_DATA =
40+
ImmutableList.of(HEADER);
41+
42+
private static final long CUSTOM_PRE_SKIP_SAMPLES = 28674;
43+
private static final byte[] CUSTOM_PRE_SKIP_BYTES =
44+
buildNativeOrderByteArray(sampleCountToNanoseconds(CUSTOM_PRE_SKIP_SAMPLES));
45+
46+
private static final long CUSTOM_SEEK_PRE_ROLL_SAMPLES = 7680;
47+
private static final byte[] CUSTOM_SEEK_PRE_ROLL_BYTES =
48+
buildNativeOrderByteArray(sampleCountToNanoseconds(CUSTOM_SEEK_PRE_ROLL_SAMPLES));
49+
50+
private static final ImmutableList<byte[]> FULL_INITIALIZATION_DATA =
51+
ImmutableList.of(HEADER, CUSTOM_PRE_SKIP_BYTES, CUSTOM_SEEK_PRE_ROLL_BYTES);
52+
53+
@Test
54+
public void getChannelCount() {
55+
int channelCount = OpusDecoder.getChannelCount(HEADER);
56+
assertThat(channelCount).isEqualTo(2);
57+
}
58+
59+
@Test
60+
public void getPreSkipSamples_fullInitializationData_returnsOverrideValue() {
61+
int preSkipSamples = OpusDecoder.getPreSkipSamples(FULL_INITIALIZATION_DATA);
62+
assertThat(preSkipSamples).isEqualTo(CUSTOM_PRE_SKIP_SAMPLES);
63+
}
64+
65+
@Test
66+
public void getPreSkipSamples_headerOnlyInitializationData_returnsHeaderValue() {
67+
int preSkipSamples = OpusDecoder.getPreSkipSamples(HEADER_ONLY_INITIALIZATION_DATA);
68+
assertThat(preSkipSamples).isEqualTo(HEADER_PRE_SKIP_SAMPLES);
69+
}
70+
71+
@Test
72+
public void getSeekPreRollSamples_fullInitializationData_returnsInitializationDataValue() {
73+
int seekPreRollSamples = OpusDecoder.getSeekPreRollSamples(FULL_INITIALIZATION_DATA);
74+
assertThat(seekPreRollSamples).isEqualTo(CUSTOM_SEEK_PRE_ROLL_SAMPLES);
75+
}
76+
77+
@Test
78+
public void getSeekPreRollSamples_headerOnlyInitializationData_returnsDefaultValue() {
79+
int seekPreRollSamples = OpusDecoder.getSeekPreRollSamples(HEADER_ONLY_INITIALIZATION_DATA);
80+
assertThat(seekPreRollSamples).isEqualTo(DEFAULT_SEEK_PRE_ROLL_SAMPLES);
81+
}
82+
83+
private static long sampleCountToNanoseconds(long sampleCount) {
84+
return (sampleCount * C.NANOS_PER_SECOND) / OpusDecoder.SAMPLE_RATE;
85+
}
86+
87+
private static byte[] buildNativeOrderByteArray(long value) {
88+
return ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()).putLong(value).array();
89+
}
90+
}

‎library/extractor/src/main/java/com/google/android/exoplayer2/audio/OpusUtil.java

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -61,39 +61,6 @@ public static List<byte[]> buildInitializationData(byte[] header) {
6161
return initializationData;
6262
}
6363

64-
/**
65-
* Returns the number of pre-skip samples specified by the given Opus codec initialization data.
66-
*
67-
* @param initializationData The codec initialization data.
68-
* @return The number of pre-skip samples.
69-
*/
70-
public static int getPreSkipSamples(List<byte[]> initializationData) {
71-
if (initializationData.size() == FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT) {
72-
long codecDelayNs =
73-
ByteBuffer.wrap(initializationData.get(1)).order(ByteOrder.nativeOrder()).getLong();
74-
return (int) nanosecondsToSampleCount(codecDelayNs);
75-
}
76-
// Fall back to parsing directly from the Opus Identification header.
77-
return getPreSkipSamples(initializationData.get(0));
78-
}
79-
80-
/**
81-
* Returns the number of seek per-roll samples specified by the given Opus codec initialization
82-
* data.
83-
*
84-
* @param initializationData The codec initialization data.
85-
* @return The number of seek pre-roll samples.
86-
*/
87-
public static int getSeekPreRollSamples(List<byte[]> initializationData) {
88-
if (initializationData.size() == FULL_CODEC_INITIALIZATION_DATA_BUFFER_COUNT) {
89-
long seekPreRollNs =
90-
ByteBuffer.wrap(initializationData.get(2)).order(ByteOrder.nativeOrder()).getLong();
91-
return (int) nanosecondsToSampleCount(seekPreRollNs);
92-
}
93-
// Fall back to returning the default seek pre-roll.
94-
return DEFAULT_SEEK_PRE_ROLL_SAMPLES;
95-
}
96-
9764
private static int getPreSkipSamples(byte[] header) {
9865
return ((header[11] & 0xFF) << 8) | (header[10] & 0xFF);
9966
}
@@ -105,8 +72,4 @@ private static byte[] buildNativeOrderByteArray(long value) {
10572
private static long sampleCountToNanoseconds(long sampleCount) {
10673
return (sampleCount * C.NANOS_PER_SECOND) / SAMPLE_RATE;
10774
}
108-
109-
private static long nanosecondsToSampleCount(long nanoseconds) {
110-
return (nanoseconds * SAMPLE_RATE) / C.NANOS_PER_SECOND;
111-
}
11275
}

‎library/extractor/src/test/java/com/google/android/exoplayer2/audio/OpusUtilTest.java

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import androidx.test.ext.junit.runners.AndroidJUnit4;
2121
import com.google.android.exoplayer2.C;
22-
import com.google.common.collect.ImmutableList;
2322
import java.nio.ByteBuffer;
2423
import java.nio.ByteOrder;
2524
import java.util.List;
@@ -41,20 +40,6 @@ public final class OpusUtilTest {
4140
private static final byte[] DEFAULT_SEEK_PRE_ROLL_BYTES =
4241
buildNativeOrderByteArray(sampleCountToNanoseconds(DEFAULT_SEEK_PRE_ROLL_SAMPLES));
4342

44-
private static final ImmutableList<byte[]> HEADER_ONLY_INITIALIZATION_DATA =
45-
ImmutableList.of(HEADER);
46-
47-
private static final long CUSTOM_PRE_SKIP_SAMPLES = 28674;
48-
private static final byte[] CUSTOM_PRE_SKIP_BYTES =
49-
buildNativeOrderByteArray(sampleCountToNanoseconds(CUSTOM_PRE_SKIP_SAMPLES));
50-
51-
private static final long CUSTOM_SEEK_PRE_ROLL_SAMPLES = 7680;
52-
private static final byte[] CUSTOM_SEEK_PRE_ROLL_BYTES =
53-
buildNativeOrderByteArray(sampleCountToNanoseconds(CUSTOM_SEEK_PRE_ROLL_SAMPLES));
54-
55-
private static final ImmutableList<byte[]> FULL_INITIALIZATION_DATA =
56-
ImmutableList.of(HEADER, CUSTOM_PRE_SKIP_BYTES, CUSTOM_SEEK_PRE_ROLL_BYTES);
57-
5843
@Test
5944
public void buildInitializationData() {
6045
List<byte[]> initializationData = OpusUtil.buildInitializationData(HEADER);
@@ -70,30 +55,6 @@ public void getChannelCount() {
7055
assertThat(channelCount).isEqualTo(2);
7156
}
7257

73-
@Test
74-
public void getPreSkipSamples_fullInitializationData_returnsOverrideValue() {
75-
int preSkipSamples = OpusUtil.getPreSkipSamples(FULL_INITIALIZATION_DATA);
76-
assertThat(preSkipSamples).isEqualTo(CUSTOM_PRE_SKIP_SAMPLES);
77-
}
78-
79-
@Test
80-
public void getPreSkipSamples_headerOnlyInitializationData_returnsHeaderValue() {
81-
int preSkipSamples = OpusUtil.getPreSkipSamples(HEADER_ONLY_INITIALIZATION_DATA);
82-
assertThat(preSkipSamples).isEqualTo(HEADER_PRE_SKIP_SAMPLES);
83-
}
84-
85-
@Test
86-
public void getSeekPreRollSamples_fullInitializationData_returnsInitializationDataValue() {
87-
int seekPreRollSamples = OpusUtil.getSeekPreRollSamples(FULL_INITIALIZATION_DATA);
88-
assertThat(seekPreRollSamples).isEqualTo(CUSTOM_SEEK_PRE_ROLL_SAMPLES);
89-
}
90-
91-
@Test
92-
public void getSeekPreRollSamples_headerOnlyInitializationData_returnsDefaultValue() {
93-
int seekPreRollSamples = OpusUtil.getSeekPreRollSamples(HEADER_ONLY_INITIALIZATION_DATA);
94-
assertThat(seekPreRollSamples).isEqualTo(DEFAULT_SEEK_PRE_ROLL_SAMPLES);
95-
}
96-
9758
private static long sampleCountToNanoseconds(long sampleCount) {
9859
return (sampleCount * C.NANOS_PER_SECOND) / OpusUtil.SAMPLE_RATE;
9960
}

0 commit comments

Comments
 (0)