Skip to content

Commit c7186c9

Browse files
copybara-githubrohitjoins
authored andcommitted
Merge pull request #650 from cedricxperi:dts-lbr-buffer-underflow-fix
PiperOrigin-RevId: 572864175 (cherry picked from commit 2421ba4)
1 parent 24c39b6 commit c7186c9

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

RELEASENOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
stays at its current behavior of `true`.
1212
* Extractors:
1313
* Audio:
14+
* Fix DTS Express audio buffer underflow issue
15+
([#650](https://github.com/androidx/media/pull/650)).
1416
* Video:
1517
* Text:
1618
* Remove `ExoplayerCuesDecoder`. Text tracks with `sampleMimeType =

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProvider.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class DefaultAudioTrackBufferSizeProvider
6363
*/
6464
private static final int AC3_BUFFER_MULTIPLICATION_FACTOR = 2;
6565

66+
/**
67+
* Default multiplication factor to apply to DTS Express passthrough buffer to avoid underruns.
68+
*/
69+
private static final int DTSHD_BUFFER_MULTIPLICATION_FACTOR = 4;
70+
6671
/** A builder to create {@link DefaultAudioTrackBufferSizeProvider} instances. */
6772
public static class Builder {
6873

@@ -72,6 +77,7 @@ public static class Builder {
7277
private int passthroughBufferDurationUs;
7378
private int offloadBufferDurationUs;
7479
private int ac3BufferMultiplicationFactor;
80+
private int dtshdBufferMultiplicationFactor;
7581

7682
/** Creates a new builder. */
7783
public Builder() {
@@ -81,6 +87,7 @@ public Builder() {
8187
passthroughBufferDurationUs = PASSTHROUGH_BUFFER_DURATION_US;
8288
offloadBufferDurationUs = OFFLOAD_BUFFER_DURATION_US;
8389
ac3BufferMultiplicationFactor = AC3_BUFFER_MULTIPLICATION_FACTOR;
90+
dtshdBufferMultiplicationFactor = DTSHD_BUFFER_MULTIPLICATION_FACTOR;
8491
}
8592

8693
/**
@@ -143,6 +150,16 @@ public Builder setAc3BufferMultiplicationFactor(int ac3BufferMultiplicationFacto
143150
return this;
144151
}
145152

153+
/**
154+
* Sets the multiplication factor to apply to the passthrough buffer for DTS-HD (DTS Express) to
155+
* avoid underruns. Default is {@link #DTSHD_BUFFER_MULTIPLICATION_FACTOR}.
156+
*/
157+
@CanIgnoreReturnValue
158+
public Builder setDtshdBufferMultiplicationFactor(int dtshdBufferMultiplicationFactor) {
159+
this.dtshdBufferMultiplicationFactor = dtshdBufferMultiplicationFactor;
160+
return this;
161+
}
162+
146163
/** Build the {@link DefaultAudioTrackBufferSizeProvider}. */
147164
public DefaultAudioTrackBufferSizeProvider build() {
148165
return new DefaultAudioTrackBufferSizeProvider(this);
@@ -170,13 +187,20 @@ public DefaultAudioTrackBufferSizeProvider build() {
170187
*/
171188
public final int ac3BufferMultiplicationFactor;
172189

190+
/**
191+
* The multiplication factor to apply to DTS-HD (DTS Express) passthrough buffer to avoid
192+
* underruns.
193+
*/
194+
public final int dtshdBufferMultiplicationFactor;
195+
173196
protected DefaultAudioTrackBufferSizeProvider(Builder builder) {
174197
minPcmBufferDurationUs = builder.minPcmBufferDurationUs;
175198
maxPcmBufferDurationUs = builder.maxPcmBufferDurationUs;
176199
pcmBufferMultiplicationFactor = builder.pcmBufferMultiplicationFactor;
177200
passthroughBufferDurationUs = builder.passthroughBufferDurationUs;
178201
offloadBufferDurationUs = builder.offloadBufferDurationUs;
179202
ac3BufferMultiplicationFactor = builder.ac3BufferMultiplicationFactor;
203+
dtshdBufferMultiplicationFactor = builder.dtshdBufferMultiplicationFactor;
180204
}
181205

182206
@Override
@@ -232,7 +256,13 @@ protected int getPassthroughBufferSizeInBytes(@C.Encoding int encoding, int bitr
232256
int bufferSizeUs = passthroughBufferDurationUs;
233257
if (encoding == C.ENCODING_AC3) {
234258
bufferSizeUs *= ac3BufferMultiplicationFactor;
259+
} else if (encoding == C.ENCODING_DTS_HD) {
260+
// DTS-HD (DTS Express) for streaming uses a frame size (number of audio samples per channel
261+
// per frame) of 4096. This requires a higher multiple for the buffersize computation.
262+
// Otherwise, there will be buffer underflow during DASH playback.
263+
bufferSizeUs *= dtshdBufferMultiplicationFactor;
235264
}
265+
236266
int byteRate =
237267
bitrate != Format.NO_VALUE
238268
? divide(bitrate, 8, RoundingMode.CEILING)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (C) 2023 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 androidx.media3.exoplayer.audio;
17+
18+
import static androidx.media3.common.C.MICROS_PER_SECOND;
19+
import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH;
20+
import static androidx.media3.exoplayer.audio.DefaultAudioTrackBufferSizeProvider.getMaximumEncodedRateBytesPerSecond;
21+
import static com.google.common.truth.Truth.assertThat;
22+
23+
import androidx.media3.common.C;
24+
import androidx.media3.common.Format;
25+
import androidx.test.ext.junit.runners.AndroidJUnit4;
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
29+
/** Tests for {@link DefaultAudioTrackBufferSizeProvider} DTS-HD (DTS Express) audio. */
30+
@RunWith(AndroidJUnit4.class)
31+
public class DefaultAudioTrackBufferSizeProviderDTSHDTest {
32+
33+
private static final DefaultAudioTrackBufferSizeProvider DEFAULT =
34+
new DefaultAudioTrackBufferSizeProvider.Builder().build();
35+
36+
@Test
37+
public void
38+
getBufferSizeInBytes_passthroughDtshdAndNoBitrate_assumesMaxByteRateTimesMultiplicationFactor() {
39+
int bufferSize =
40+
DEFAULT.getBufferSizeInBytes(
41+
/* minBufferSizeInBytes= */ 0,
42+
/* encoding= */ C.ENCODING_DTS_HD,
43+
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
44+
/* pcmFrameSize= */ 1,
45+
/* sampleRate= */ 0,
46+
/* bitrate= */ Format.NO_VALUE,
47+
/* maxAudioTrackPlaybackSpeed= */ 1);
48+
49+
assertThat(bufferSize)
50+
.isEqualTo(
51+
durationUsToDtshdMaxBytes(DEFAULT.passthroughBufferDurationUs)
52+
* DEFAULT.dtshdBufferMultiplicationFactor);
53+
}
54+
55+
@Test
56+
public void
57+
getBufferSizeInBytes_passthroughDtshdAt384Kbits_isPassthroughBufferSizeTimesMultiplicationFactor() {
58+
int bufferSize =
59+
DEFAULT.getBufferSizeInBytes(
60+
/* minBufferSizeInBytes= */ 0,
61+
/* encoding= */ C.ENCODING_DTS_HD,
62+
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
63+
/* pcmFrameSize= */ 1,
64+
/* sampleRate= */ 0,
65+
/* bitrate= */ 384_000,
66+
/* maxAudioTrackPlaybackSpeed= */ 1);
67+
68+
// Default buffer duration 0.25s => 0.25 * 384000 / 8 = 12000
69+
assertThat(bufferSize).isEqualTo(12000 * DEFAULT.dtshdBufferMultiplicationFactor);
70+
}
71+
72+
private static int durationUsToDtshdMaxBytes(long durationUs) {
73+
return (int)
74+
(durationUs * getMaximumEncodedRateBytesPerSecond(C.ENCODING_DTS_HD) / MICROS_PER_SECOND);
75+
}
76+
}

0 commit comments

Comments
 (0)