Skip to content

Commit 77bccf6

Browse files
committed
Better support for PCM audio formats around DefaultAudioSink
This change: - abstracts away the fact that SonicAudioProcessor does not support float PCM from the audio sink to the audio processor chain (easier to replace) - makes the audio sink allow custom audio processors with any output format - adds DSD and double PCM format definitions to allow passing these around for internal signal processing - adds a new mode to DefaultAudioSink that allows any supported linear PCM format to be output into the AudioTrack Issue: google/ExoPlayer#10516 Issue: #1931
1 parent 6536929 commit 77bccf6

File tree

13 files changed

+318
-53
lines changed

13 files changed

+318
-53
lines changed

libraries/common/src/main/java/androidx/media3/common/C.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ private C() {}
168168
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
169169
* #ENCODING_PCM_16BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_24BIT}, {@link
170170
* #ENCODING_PCM_24BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_32BIT}, {@link
171-
* #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_MP3}, {@link
172-
* #ENCODING_AC3}, {@link #ENCODING_E_AC3}, {@link #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4},
173-
* {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD}, {@link #ENCODING_DOLBY_TRUEHD} or {@link
174-
* #ENCODING_OPUS}.
171+
* #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_PCM_DOUBLE},
172+
* {@link #ENCODING_MP3}, {@link #ENCODING_AC3}, {@link #ENCODING_E_AC3}, {@link
173+
* #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4}, {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD},
174+
* {@link #ENCODING_DOLBY_TRUEHD}, {@link #ENCODING_OPUS} or {@link #ENCODING_DSD}.
175175
*/
176176
@UnstableApi
177177
@Documented
@@ -188,6 +188,7 @@ private C() {}
188188
ENCODING_PCM_32BIT,
189189
ENCODING_PCM_32BIT_BIG_ENDIAN,
190190
ENCODING_PCM_FLOAT,
191+
ENCODING_PCM_DOUBLE,
191192
ENCODING_MP3,
192193
ENCODING_AAC_LC,
193194
ENCODING_AAC_HE_V1,
@@ -204,6 +205,7 @@ private C() {}
204205
ENCODING_DOLBY_TRUEHD,
205206
ENCODING_OPUS,
206207
ENCODING_DTS_UHD_P2,
208+
ENCODING_DSD,
207209
})
208210
public @interface Encoding {}
209211

@@ -212,7 +214,7 @@ private C() {}
212214
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
213215
* #ENCODING_PCM_16BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_24BIT}, {@link
214216
* #ENCODING_PCM_24BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_32BIT}, {@link
215-
* #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}.
217+
* #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_PCM_DOUBLE}.
216218
*/
217219
@UnstableApi
218220
@Documented
@@ -228,7 +230,8 @@ private C() {}
228230
ENCODING_PCM_24BIT_BIG_ENDIAN,
229231
ENCODING_PCM_32BIT,
230232
ENCODING_PCM_32BIT_BIG_ENDIAN,
231-
ENCODING_PCM_FLOAT
233+
ENCODING_PCM_FLOAT,
234+
ENCODING_PCM_DOUBLE
232235
})
233236
public @interface PcmEncoding {}
234237

@@ -259,6 +262,9 @@ private C() {}
259262
/** See {@link AudioFormat#ENCODING_PCM_FLOAT}. */
260263
@UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
261264

265+
/** PCM encoding with double-precision floating point samples. */
266+
@UnstableApi public static final int ENCODING_PCM_DOUBLE = 0x70000000;
267+
262268
/** See {@link AudioFormat#ENCODING_MP3}. */
263269
@UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3;
264270

@@ -307,6 +313,9 @@ private C() {}
307313
/** See {@link AudioFormat#ENCODING_OPUS}. */
308314
@UnstableApi public static final int ENCODING_OPUS = AudioFormat.ENCODING_OPUS;
309315

316+
/** See {@link AudioFormat#ENCODING_DSD}. */
317+
@UnstableApi public static final int ENCODING_DSD = AudioFormat.ENCODING_DSD;
318+
310319
/**
311320
* Represents the behavior affecting whether spatialization will be used. One of {@link
312321
* #SPATIALIZATION_BEHAVIOR_AUTO} or {@link #SPATIALIZATION_BEHAVIOR_NEVER}.

libraries/common/src/main/java/androidx/media3/common/MimeTypes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public final class MimeTypes {
8686
public static final String AUDIO_E_AC3_JOC = BASE_TYPE_AUDIO + "/eac3-joc";
8787
public static final String AUDIO_AC4 = BASE_TYPE_AUDIO + "/ac4";
8888
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
89+
public static final String AUDIO_DSD = BASE_TYPE_AUDIO + "/x-dsd";
8990
public static final String AUDIO_DTS = BASE_TYPE_AUDIO + "/vnd.dts";
9091
public static final String AUDIO_DTS_HD = BASE_TYPE_AUDIO + "/vnd.dts.hd";
9192
public static final String AUDIO_DTS_EXPRESS = BASE_TYPE_AUDIO + "/vnd.dts.hd;profile=lbr";
@@ -686,6 +687,8 @@ public static boolean isDolbyVisionCodec(
686687
return C.ENCODING_DOLBY_TRUEHD;
687688
case MimeTypes.AUDIO_OPUS:
688689
return C.ENCODING_OPUS;
690+
case MimeTypes.AUDIO_DSD:
691+
return C.ENCODING_DSD;
689692
default:
690693
return C.ENCODING_INVALID;
691694
}

libraries/common/src/main/java/androidx/media3/common/audio/AudioProcessorChain.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package androidx.media3.common.audio;
1717

18+
import androidx.media3.common.Format;
1819
import androidx.media3.common.PlaybackParameters;
1920
import androidx.media3.common.util.UnstableApi;
2021

@@ -33,7 +34,7 @@ public interface AudioProcessorChain {
3334
* during initialization, but audio processors may change state to become active/inactive during
3435
* playback.
3536
*/
36-
AudioProcessor[] getAudioProcessors();
37+
AudioProcessor[] getAudioProcessors(Format inputFormat);
3738

3839
/**
3940
* Configures audio processors to apply the specified playback parameters immediately, returning
@@ -50,7 +51,7 @@ public interface AudioProcessorChain {
5051
* value. Only called when processors have no input pending.
5152
*
5253
* @param skipSilenceEnabled Whether silences should be skipped in the audio stream.
53-
* @return The new value.
54+
* @return The value that was actually applied.
5455
*/
5556
boolean applySkipSilenceEnabled(boolean skipSilenceEnabled);
5657

libraries/common/src/main/java/androidx/media3/common/audio/ToInt16PcmAudioProcessor.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* <li>{@link C#ENCODING_PCM_32BIT}
3535
* <li>{@link C#ENCODING_PCM_32BIT_BIG_ENDIAN}
3636
* <li>{@link C#ENCODING_PCM_FLOAT}
37+
* <li>{@link C#ENCODING_PCM_DOUBLE}
3738
* </ul>
3839
*/
3940
@UnstableApi
@@ -50,7 +51,8 @@ public AudioFormat onConfigure(AudioFormat inputAudioFormat)
5051
&& encoding != C.ENCODING_PCM_24BIT_BIG_ENDIAN
5152
&& encoding != C.ENCODING_PCM_32BIT
5253
&& encoding != C.ENCODING_PCM_32BIT_BIG_ENDIAN
53-
&& encoding != C.ENCODING_PCM_FLOAT) {
54+
&& encoding != C.ENCODING_PCM_FLOAT
55+
&& encoding != C.ENCODING_PCM_DOUBLE) {
5456
throw new UnhandledAudioFormatException(inputAudioFormat);
5557
}
5658
return encoding != C.ENCODING_PCM_16BIT
@@ -82,6 +84,9 @@ public void queueInput(ByteBuffer inputBuffer) {
8284
case C.ENCODING_PCM_FLOAT:
8385
resampledSize = size / 2;
8486
break;
87+
case C.ENCODING_PCM_DOUBLE:
88+
resampledSize = size / 4;
89+
break;
8590
case C.ENCODING_PCM_16BIT:
8691
case C.ENCODING_INVALID:
8792
case Format.NO_VALUE:
@@ -147,6 +152,19 @@ public void queueInput(ByteBuffer inputBuffer) {
147152
buffer.put((byte) ((shortValue >> 8) & 0xFF));
148153
}
149154
break;
155+
case C.ENCODING_PCM_DOUBLE:
156+
// 64 bit floating point -> 16 bit resampling. Floating point values are in the range
157+
// [-1.0, 1.0], so need to be scaled by Short.MAX_VALUE.
158+
for (int i = position; i < limit; i += 8) {
159+
// Clamp to avoid integer overflow if the floating point values exceed their nominal range
160+
// [Internal ref: b/161204847].
161+
double doubleValue =
162+
Util.constrainValue(inputBuffer.getDouble(i), /* min= */ -1, /* max= */ 1);
163+
short shortValue = (short) (doubleValue * Short.MAX_VALUE);
164+
buffer.put((byte) (shortValue & 0xFF));
165+
buffer.put((byte) ((shortValue >> 8) & 0xFF));
166+
}
167+
break;
150168
case C.ENCODING_PCM_16BIT:
151169
case C.ENCODING_INVALID:
152170
case Format.NO_VALUE:

libraries/common/src/main/java/androidx/media3/common/util/MediaFormatUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ private static void maybeSetPcmEncoding(
482482
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
483483
case C.ENCODING_PCM_24BIT_BIG_ENDIAN:
484484
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
485+
case C.ENCODING_PCM_DOUBLE:
485486
default:
486487
// No matching value. Do nothing.
487488
return;

libraries/common/src/main/java/androidx/media3/common/util/Util.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,19 @@ public static float constrainValue(float value, float min, float max) {
11881188
return max(min, min(value, max));
11891189
}
11901190

1191+
/**
1192+
* Constrains a value to the specified bounds.
1193+
*
1194+
* @param value The value to constrain.
1195+
* @param min The lower bound.
1196+
* @param max The upper bound.
1197+
* @return The constrained value {@code Math.max(min, Math.min(value, max))}.
1198+
*/
1199+
@UnstableApi
1200+
public static double constrainValue(double value, double min, double max) {
1201+
return max(min, min(value, max));
1202+
}
1203+
11911204
/**
11921205
* Returns the sum of two arguments, or a third argument if the result overflows.
11931206
*
@@ -2258,7 +2271,8 @@ public static boolean isEncodingLinearPcm(@C.Encoding int encoding) {
22582271
|| encoding == C.ENCODING_PCM_24BIT_BIG_ENDIAN
22592272
|| encoding == C.ENCODING_PCM_32BIT
22602273
|| encoding == C.ENCODING_PCM_32BIT_BIG_ENDIAN
2261-
|| encoding == C.ENCODING_PCM_FLOAT;
2274+
|| encoding == C.ENCODING_PCM_FLOAT
2275+
|| encoding == C.ENCODING_PCM_DOUBLE;
22622276
}
22632277

22642278
/**
@@ -2273,7 +2287,8 @@ public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
22732287
|| encoding == C.ENCODING_PCM_24BIT_BIG_ENDIAN
22742288
|| encoding == C.ENCODING_PCM_32BIT
22752289
|| encoding == C.ENCODING_PCM_32BIT_BIG_ENDIAN
2276-
|| encoding == C.ENCODING_PCM_FLOAT;
2290+
|| encoding == C.ENCODING_PCM_FLOAT
2291+
|| encoding == C.ENCODING_PCM_DOUBLE;
22772292
}
22782293

22792294
/**
@@ -2380,9 +2395,11 @@ public static int getApiLevelThatAudioFormatIntroducedAudioEncoding(int encoding
23802395
return 28;
23812396
case C.ENCODING_OPUS:
23822397
return 30;
2398+
case C.ENCODING_PCM_24BIT:
23832399
case C.ENCODING_PCM_32BIT:
23842400
return 31;
23852401
case C.ENCODING_DTS_UHD_P2:
2402+
case C.ENCODING_DSD:
23862403
return 34;
23872404
default:
23882405
return Integer.MAX_VALUE;
@@ -2422,6 +2439,8 @@ public static int getByteDepth(@C.PcmEncoding int pcmEncoding) {
24222439
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
24232440
case C.ENCODING_PCM_FLOAT:
24242441
return 4;
2442+
case C.ENCODING_PCM_DOUBLE:
2443+
return 8;
24252444
case C.ENCODING_INVALID:
24262445
case Format.NO_VALUE:
24272446
default:

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
108108
private long allowedVideoJoiningTimeMs;
109109
private boolean enableDecoderFallback;
110110
private MediaCodecSelector mediaCodecSelector;
111+
private boolean pcmEncodingRestrictionLifted;
111112
private boolean enableFloatOutput;
112113
private boolean enableAudioTrackPlaybackParams;
113114
private boolean enableMediaCodecVideoRendererPrewarming;
@@ -223,15 +224,38 @@ public final DefaultRenderersFactory setMediaCodecSelector(
223224
*
224225
* <p>The default value is {@code false}.
225226
*
227+
* @deprecated Use {@link #setPcmEncodingRestrictionLifted} instead to allow any encoding, not
228+
* just 32-bit float.
226229
* @param enableFloatOutput Whether to enable use of floating point audio output, if available.
227230
* @return This factory, for convenience.
228231
*/
229232
@CanIgnoreReturnValue
233+
@Deprecated
230234
public final DefaultRenderersFactory setEnableAudioFloatOutput(boolean enableFloatOutput) {
231235
this.enableFloatOutput = enableFloatOutput;
232236
return this;
233237
}
234238

239+
/**
240+
* Sets whether to enable outputting samples in any platform-supported format (such as 32-bit
241+
* float, 32-bit integer, 24-bit integer, 16-bit integer or 8-bit integer) instead of restricting
242+
* output to 16-bit integers. Where possible, the input sample format will be used, otherwise
243+
* high-resolution formats will be output as 32-bit float. Parts of the default audio processing
244+
* chain (for example, speed adjustment) will not be available when output formats other than
245+
* 16-bit integer are in use.
246+
*
247+
* <p>The default value is {@code false}.
248+
*
249+
* @param pcmEncodingRestrictionLifted Whether to lift any restriction of output sample format.
250+
* @return This factory, for convenience.
251+
*/
252+
@CanIgnoreReturnValue
253+
public final DefaultRenderersFactory setPcmEncodingRestrictionLifted(
254+
boolean pcmEncodingRestrictionLifted) {
255+
this.pcmEncodingRestrictionLifted = pcmEncodingRestrictionLifted;
256+
return this;
257+
}
258+
235259
/**
236260
* Sets whether to enable setting playback speed using {@link
237261
* android.media.AudioTrack#setPlaybackParams(PlaybackParams)}, which is supported from API level
@@ -375,7 +399,11 @@ public Renderer[] createRenderers(
375399
renderersList);
376400
@Nullable
377401
AudioSink audioSink =
378-
buildAudioSink(context, enableFloatOutput, enableAudioTrackPlaybackParams);
402+
buildAudioSink(
403+
context,
404+
pcmEncodingRestrictionLifted,
405+
enableFloatOutput,
406+
enableAudioTrackPlaybackParams);
379407
if (audioSink != null) {
380408
buildAudioRenderers(
381409
context,
@@ -814,11 +842,17 @@ protected void buildMiscellaneousRenderers(
814842
*/
815843
@Nullable
816844
protected AudioSink buildAudioSink(
817-
Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams) {
818-
return new DefaultAudioSink.Builder(context)
819-
.setEnableFloatOutput(enableFloatOutput)
820-
.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
821-
.build();
845+
Context context,
846+
boolean pcmEncodingRestrictionLifted,
847+
boolean enableFloatOutput,
848+
boolean enableAudioTrackPlaybackParams) {
849+
DefaultAudioSink.Builder builder = new DefaultAudioSink.Builder(context);
850+
if (pcmEncodingRestrictionLifted || !enableFloatOutput) {
851+
builder.setPcmEncodingRestrictionLifted(pcmEncodingRestrictionLifted);
852+
} else {
853+
builder.setEnableFloatOutput(true);
854+
}
855+
return builder.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams).build();
822856
}
823857

824858
@Override

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ public void queueInput(ByteBuffer inputBuffer) {
113113
case C.ENCODING_PCM_FLOAT:
114114
buffer.putFloat(inputBuffer.getFloat(inputIndex));
115115
break;
116+
case C.ENCODING_PCM_DOUBLE:
117+
buffer.putDouble(inputBuffer.getDouble(inputIndex));
118+
break;
116119
default:
117120
throw new IllegalStateException("Unexpected encoding: " + inputAudioFormat.encoding);
118121
}

0 commit comments

Comments
 (0)