@@ -142,6 +142,21 @@ default int getAudioTrackChannelConfig(int channelCount) {
142
142
/** The time it takes to ramp AudioTrack's volume up or down when pausing or starting to play. */
143
143
private static final int AUDIO_TRACK_VOLUME_RAMP_TIME_MS = 20 ;
144
144
145
+ /**
146
+ * @see Builder#setPcmEncodingRestrictionLifted(boolean)
147
+ */
148
+ private static final int PCM_ENCODING_ANY = 0 ;
149
+
150
+ /**
151
+ * @see Builder#setPcmEncodingRestrictionLifted(boolean)
152
+ */
153
+ private static final int PCM_ENCODING_INT16_ONLY = 1 ;
154
+
155
+ /**
156
+ * @see Builder#setEnableFloatOutput(boolean)
157
+ */
158
+ private static final int PCM_ENCODING_INT16_FLOAT32_ONLY = 2 ;
159
+
145
160
/**
146
161
* Thrown when the audio track has provided a spurious timestamp, if {@link
147
162
* #failOnSpuriousAudioTimestamp} is set.
@@ -167,10 +182,12 @@ public interface AudioProcessorChain extends androidx.media3.common.audio.AudioP
167
182
/**
168
183
* The default audio processor chain, which applies a (possibly empty) chain of user-defined audio
169
184
* processors followed by {@link SilenceSkippingAudioProcessor} and {@link SonicAudioProcessor}.
185
+ * No audio processors will be applied for PCM encodings other than 16-bit integer.
170
186
*/
171
187
@ SuppressWarnings ("deprecation" )
172
188
public static class DefaultAudioProcessorChain implements AudioProcessorChain {
173
189
190
+ private boolean formatSupported = false ;
174
191
private final AudioProcessor [] audioProcessors ;
175
192
private final SilenceSkippingAudioProcessor silenceSkippingAudioProcessor ;
176
193
private final SonicAudioProcessor sonicAudioProcessor ;
@@ -207,33 +224,44 @@ public DefaultAudioProcessorChain(
207
224
}
208
225
209
226
@ Override
210
- public AudioProcessor [] getAudioProcessors () {
227
+ public AudioProcessor [] getAudioProcessors (Format inputFormat ) {
228
+ if (inputFormat .pcmEncoding != C .ENCODING_PCM_16BIT ) {
229
+ formatSupported = false ;
230
+ return new AudioProcessor [0 ];
231
+ }
232
+ formatSupported = true ;
211
233
return audioProcessors ;
212
234
}
213
235
214
236
@ Override
215
237
public PlaybackParameters applyPlaybackParameters (PlaybackParameters playbackParameters ) {
238
+ if (!formatSupported ) {
239
+ return PlaybackParameters .DEFAULT ;
240
+ }
216
241
sonicAudioProcessor .setSpeed (playbackParameters .speed );
217
242
sonicAudioProcessor .setPitch (playbackParameters .pitch );
218
243
return playbackParameters ;
219
244
}
220
245
221
246
@ Override
222
247
public boolean applySkipSilenceEnabled (boolean skipSilenceEnabled ) {
248
+ if (!formatSupported ) {
249
+ return false ;
250
+ }
223
251
silenceSkippingAudioProcessor .setEnabled (skipSilenceEnabled );
224
252
return skipSilenceEnabled ;
225
253
}
226
254
227
255
@ Override
228
256
public long getMediaDuration (long playoutDuration ) {
229
- return sonicAudioProcessor .isActive ()
257
+ return formatSupported && sonicAudioProcessor .isActive ()
230
258
? sonicAudioProcessor .getMediaDuration (playoutDuration )
231
259
: playoutDuration ;
232
260
}
233
261
234
262
@ Override
235
263
public long getSkippedOutputFrameCount () {
236
- return silenceSkippingAudioProcessor .getSkippedFrames ();
264
+ return formatSupported ? silenceSkippingAudioProcessor .getSkippedFrames () : 0 ;
237
265
}
238
266
}
239
267
@@ -297,7 +325,7 @@ public static final class Builder {
297
325
@ Nullable private final Context context ;
298
326
private AudioCapabilities audioCapabilities ;
299
327
@ Nullable private androidx .media3 .common .audio .AudioProcessorChain audioProcessorChain ;
300
- private boolean enableFloatOutput ;
328
+ private int pcmEncodingRestrictionMode ;
301
329
private boolean enableAudioTrackPlaybackParams ;
302
330
303
331
private boolean buildCalled ;
@@ -316,6 +344,7 @@ public Builder() {
316
344
audioCapabilities = DEFAULT_AUDIO_CAPABILITIES ;
317
345
audioTrackBufferSizeProvider = AudioTrackBufferSizeProvider .DEFAULT ;
318
346
audioTrackProvider = AudioTrackProvider .DEFAULT ;
347
+ pcmEncodingRestrictionMode = PCM_ENCODING_INT16_ONLY ;
319
348
}
320
349
321
350
/**
@@ -328,6 +357,7 @@ public Builder(Context context) {
328
357
audioCapabilities = DEFAULT_AUDIO_CAPABILITIES ;
329
358
audioTrackBufferSizeProvider = AudioTrackBufferSizeProvider .DEFAULT ;
330
359
audioTrackProvider = AudioTrackProvider .DEFAULT ;
360
+ pcmEncodingRestrictionMode = PCM_ENCODING_INT16_ONLY ;
331
361
}
332
362
333
363
/**
@@ -375,14 +405,37 @@ public Builder setAudioProcessorChain(
375
405
/**
376
406
* Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float
377
407
* output will be used if the input is 32-bit float, and also if the input is high resolution
378
- * (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not be
379
- * available when float output is in use.
408
+ * (24-bit or 32-bit) integer PCM. Parts of the default audio processing chain (for example,
409
+ * speed adjustment) will not be available when output formats other than 16-bit integer are in
410
+ * use.
380
411
*
381
412
* <p>The default value is {@code false}.
413
+ *
414
+ * @deprecated Use {@link #setPcmEncodingRestrictionLifted} instead to allow any encoding, not
415
+ * just 32-bit float.
382
416
*/
417
+ @ Deprecated
383
418
@ CanIgnoreReturnValue
384
419
public Builder setEnableFloatOutput (boolean enableFloatOutput ) {
385
- this .enableFloatOutput = enableFloatOutput ;
420
+ this .pcmEncodingRestrictionMode =
421
+ enableFloatOutput ? PCM_ENCODING_INT16_FLOAT32_ONLY : PCM_ENCODING_INT16_ONLY ;
422
+ return this ;
423
+ }
424
+
425
+ /**
426
+ * Sets whether to enable outputting samples in any platform-supported format (such as 32-bit
427
+ * float, 32-bit integer, 24-bit integer, 16-bit integer or 8-bit integer) instead of
428
+ * restricting output to 16-bit integers. Where possible, the input sample format will be used,
429
+ * otherwise high-resolution formats will be output as 32-bit float. Parts of the default audio
430
+ * processing chain (for example, speed adjustment) will not be available when output formats
431
+ * other than 16-bit integer are in use.
432
+ *
433
+ * <p>The default value is {@code false}.
434
+ */
435
+ @ CanIgnoreReturnValue
436
+ public Builder setPcmEncodingRestrictionLifted (boolean pcmEncodingRestrictionLifted ) {
437
+ this .pcmEncodingRestrictionMode =
438
+ pcmEncodingRestrictionLifted ? PCM_ENCODING_ANY : PCM_ENCODING_INT16_ONLY ;
386
439
return this ;
387
440
}
388
441
@@ -549,7 +602,7 @@ public DefaultAudioSink build() {
549
602
550
603
@ Nullable private final Context context ;
551
604
private final androidx .media3 .common .audio .AudioProcessorChain audioProcessorChain ;
552
- private final boolean enableFloatOutput ;
605
+ private final int pcmEncodingRestrictionMode ;
553
606
private final ChannelMappingAudioProcessor channelMappingAudioProcessor ;
554
607
private final TrimmingAudioProcessor trimmingAudioProcessor ;
555
608
private final ToInt16PcmAudioProcessor toInt16PcmAudioProcessor ;
@@ -628,7 +681,7 @@ private DefaultAudioSink(Builder builder) {
628
681
audioAttributes = AudioAttributes .DEFAULT ;
629
682
audioCapabilities = context != null ? null : builder .audioCapabilities ;
630
683
audioProcessorChain = builder .audioProcessorChain ;
631
- enableFloatOutput = builder .enableFloatOutput ;
684
+ pcmEncodingRestrictionMode = builder .pcmEncodingRestrictionMode ;
632
685
preferAudioTrackPlaybackParams = SDK_INT >= 23 && builder .enableAudioTrackPlaybackParams ;
633
686
offloadMode = OFFLOAD_MODE_DISABLED ;
634
687
audioTrackBufferSizeProvider = builder .audioTrackBufferSizeProvider ;
@@ -690,13 +743,24 @@ public boolean supportsFormat(Format format) {
690
743
Log .w (TAG , "Invalid PCM encoding: " + format .pcmEncoding );
691
744
return SINK_FORMAT_UNSUPPORTED ;
692
745
}
693
- if (format .pcmEncoding == C .ENCODING_PCM_16BIT
694
- || (enableFloatOutput && format .pcmEncoding == C .ENCODING_PCM_FLOAT )) {
695
- return SINK_FORMAT_SUPPORTED_DIRECTLY ;
746
+ if (format .pcmEncoding != C .ENCODING_PCM_16BIT ) {
747
+ if (pcmEncodingRestrictionMode == PCM_ENCODING_INT16_FLOAT32_ONLY
748
+ && format .pcmEncoding != C .ENCODING_PCM_FLOAT ) {
749
+ // PCM_ENCODING_INT16_FLOAT32_ONLY is deprecated and kept for backwards compatibility.
750
+ return SINK_FORMAT_SUPPORTED_WITH_TRANSCODING ;
751
+ }
752
+ if (pcmEncodingRestrictionMode == PCM_ENCODING_INT16_ONLY ) {
753
+ // We will forcibly transcode to 16-bit PCM to allow the full audio processor chain to
754
+ // operate.
755
+ return SINK_FORMAT_SUPPORTED_WITH_TRANSCODING ;
756
+ }
757
+ }
758
+ if (SDK_INT < Util .getApiLevelThatAudioFormatIntroducedAudioEncoding (format .pcmEncoding )) {
759
+ // We can resample all linear PCM encodings to 16-bit integer PCM, which AudioTrack is
760
+ // guaranteed to support.
761
+ return SINK_FORMAT_SUPPORTED_WITH_TRANSCODING ;
696
762
}
697
- // We can resample all linear PCM encodings to 16-bit integer PCM, which AudioTrack is
698
- // guaranteed to support.
699
- return SINK_FORMAT_SUPPORTED_WITH_TRANSCODING ;
763
+ return SINK_FORMAT_SUPPORTED_DIRECTLY ;
700
764
}
701
765
if (audioCapabilities .isPassthroughPlaybackSupported (format , audioAttributes )) {
702
766
return SINK_FORMAT_SUPPORTED_DIRECTLY ;
@@ -743,12 +807,32 @@ public void configure(Format inputFormat, int specifiedBufferSize, @Nullable int
743
807
744
808
ImmutableList .Builder <AudioProcessor > pipelineProcessors = new ImmutableList .Builder <>();
745
809
pipelineProcessors .addAll (availableAudioProcessors );
746
- if (shouldUseFloatOutput (inputFormat .pcmEncoding )) {
747
- pipelineProcessors .add (toFloatPcmAudioProcessor );
810
+ // We need to convert sample formats either if we don't support it, or if:
811
+ // - there is some pcm encoding restriction, and
812
+ // - the format isn't 16-bit integer (which is always allowed), and
813
+ // - the format isn't 32-bit float OR 32-bit float isn't allowed
814
+ Format afterConversionFormat ;
815
+ if (SDK_INT < Util .getApiLevelThatAudioFormatIntroducedAudioEncoding (inputFormat .pcmEncoding )
816
+ || (pcmEncodingRestrictionMode != PCM_ENCODING_ANY
817
+ && inputFormat .pcmEncoding != C .ENCODING_PCM_16BIT
818
+ && (inputFormat .pcmEncoding != C .ENCODING_PCM_FLOAT
819
+ || pcmEncodingRestrictionMode == PCM_ENCODING_INT16_ONLY ))) {
820
+ if (Util .isEncodingHighResolutionPcm (inputFormat .pcmEncoding )
821
+ && pcmEncodingRestrictionMode != PCM_ENCODING_INT16_ONLY ) {
822
+ pipelineProcessors .add (toFloatPcmAudioProcessor );
823
+ afterConversionFormat =
824
+ Util .getPcmFormat (
825
+ C .ENCODING_PCM_FLOAT , inputFormat .channelCount , inputFormat .sampleRate );
826
+ } else {
827
+ pipelineProcessors .add (toInt16PcmAudioProcessor );
828
+ afterConversionFormat =
829
+ Util .getPcmFormat (
830
+ C .ENCODING_PCM_16BIT , inputFormat .channelCount , inputFormat .sampleRate );
831
+ }
748
832
} else {
749
- pipelineProcessors .add (toInt16PcmAudioProcessor );
750
- pipelineProcessors .add (audioProcessorChain .getAudioProcessors ());
833
+ afterConversionFormat = inputFormat ;
751
834
}
835
+ pipelineProcessors .add (audioProcessorChain .getAudioProcessors (afterConversionFormat ));
752
836
audioProcessingPipeline = new AudioProcessingPipeline (pipelineProcessors .build ());
753
837
754
838
// If the underlying processors of the new pipeline are the same as the existing pipeline,
@@ -1779,25 +1863,14 @@ private boolean shouldApplyAudioProcessorPlaybackParameters() {
1779
1863
// frame presentation times are currently not modified (see also
1780
1864
// https://github.com/google/ExoPlayer/issues/4803);
1781
1865
// - when playing encoded audio via passthrough/offload, because modifying the audio stream
1782
- // would require decoding/re-encoding; and
1783
- // - when outputting float PCM audio, because SonicAudioProcessor outputs 16-bit integer PCM.
1784
- return !tunneling
1785
- && configuration .outputMode == OUTPUT_MODE_PCM
1786
- && !shouldUseFloatOutput (configuration .inputFormat .pcmEncoding );
1866
+ // would require decoding/re-encoding.
1867
+ return !tunneling && configuration .outputMode == OUTPUT_MODE_PCM ;
1787
1868
}
1788
1869
1789
1870
private boolean useAudioTrackPlaybackParams () {
1790
1871
return configuration != null && configuration .enableAudioTrackPlaybackParams && SDK_INT >= 23 ;
1791
1872
}
1792
1873
1793
- /**
1794
- * Returns whether audio in the specified PCM encoding should be written to the audio track as
1795
- * float PCM.
1796
- */
1797
- private boolean shouldUseFloatOutput (@ C .PcmEncoding int pcmEncoding ) {
1798
- return enableFloatOutput && Util .isEncodingHighResolutionPcm (pcmEncoding );
1799
- }
1800
-
1801
1874
/**
1802
1875
* Applies and updates media position parameters.
1803
1876
*
0 commit comments