Add a builder to DefaultAudioSink
`DefaultAudioSink` already has 3 telescoping constructors and an other one would be have been needed to add a buffer size tuning option. PiperOrigin-RevId: 414703366
This commit is contained in:
parent
dcc69056bf
commit
341bb57498
@ -66,7 +66,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer<FfmpegAudioD
|
|||||||
this(
|
this(
|
||||||
eventHandler,
|
eventHandler,
|
||||||
eventListener,
|
eventListener,
|
||||||
new DefaultAudioSink(/* audioCapabilities= */ null, audioProcessors));
|
new DefaultAudioSink.Builder().setAudioProcessors(audioProcessors).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +28,6 @@ import androidx.media3.datasource.DefaultDataSource;
|
|||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.Renderer;
|
import androidx.media3.exoplayer.Renderer;
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
import androidx.media3.exoplayer.RenderersFactory;
|
||||||
import androidx.media3.exoplayer.audio.AudioProcessor;
|
|
||||||
import androidx.media3.exoplayer.audio.AudioSink;
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
@ -67,9 +66,7 @@ public class FlacPlaybackTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void playAndAssertAudioSinkInput(String fileName) throws Exception {
|
private static void playAndAssertAudioSinkInput(String fileName) throws Exception {
|
||||||
CapturingAudioSink audioSink =
|
CapturingAudioSink audioSink = new CapturingAudioSink(new DefaultAudioSink.Builder().build());
|
||||||
new CapturingAudioSink(
|
|
||||||
new DefaultAudioSink(/* audioCapabilities= */ null, new AudioProcessor[0]));
|
|
||||||
|
|
||||||
TestPlaybackRunnable testPlaybackRunnable =
|
TestPlaybackRunnable testPlaybackRunnable =
|
||||||
new TestPlaybackRunnable(
|
new TestPlaybackRunnable(
|
||||||
|
@ -47,6 +47,7 @@ dependencies {
|
|||||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||||
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
||||||
|
compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion
|
||||||
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
|
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
|
||||||
androidTestImplementation 'com.linkedin.dexmaker:dexmaker:' + dexmakerVersion
|
androidTestImplementation 'com.linkedin.dexmaker:dexmaker:' + dexmakerVersion
|
||||||
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
||||||
|
@ -28,7 +28,6 @@ import androidx.media3.exoplayer.audio.AudioCapabilities;
|
|||||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||||
import androidx.media3.exoplayer.audio.AudioSink;
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
||||||
import androidx.media3.exoplayer.audio.DefaultAudioSink.DefaultAudioProcessorChain;
|
|
||||||
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
||||||
import androidx.media3.exoplayer.mediacodec.DefaultMediaCodecAdapterFactory;
|
import androidx.media3.exoplayer.mediacodec.DefaultMediaCodecAdapterFactory;
|
||||||
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
|
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
|
||||||
@ -666,14 +665,15 @@ public class DefaultRenderersFactory implements RenderersFactory {
|
|||||||
boolean enableFloatOutput,
|
boolean enableFloatOutput,
|
||||||
boolean enableAudioTrackPlaybackParams,
|
boolean enableAudioTrackPlaybackParams,
|
||||||
boolean enableOffload) {
|
boolean enableOffload) {
|
||||||
return new DefaultAudioSink(
|
return new DefaultAudioSink.Builder()
|
||||||
AudioCapabilities.getCapabilities(context),
|
.setAudioCapabilities(AudioCapabilities.getCapabilities(context))
|
||||||
new DefaultAudioProcessorChain(),
|
.setEnableFloatOutput(enableFloatOutput)
|
||||||
enableFloatOutput,
|
.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
|
||||||
enableAudioTrackPlaybackParams,
|
.setOffloadMode(
|
||||||
enableOffload
|
enableOffload
|
||||||
? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED
|
? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED
|
||||||
: DefaultAudioSink.OFFLOAD_MODE_DISABLED);
|
: DefaultAudioSink.OFFLOAD_MODE_DISABLED)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +47,6 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||||
import androidx.media3.exoplayer.audio.AudioCapabilities;
|
|
||||||
import androidx.media3.exoplayer.audio.AudioSink;
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
||||||
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
||||||
@ -1585,8 +1584,7 @@ public interface ExoPlayer extends Player {
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>Audio offload rendering is enabled in {@link
|
* <li>Audio offload rendering is enabled in {@link
|
||||||
* DefaultRenderersFactory#setEnableAudioOffload} or the equivalent option passed to {@link
|
* DefaultRenderersFactory#setEnableAudioOffload} or the equivalent option passed to {@link
|
||||||
* DefaultAudioSink#DefaultAudioSink(AudioCapabilities,
|
* DefaultAudioSink.Builder#setOffloadMode}.
|
||||||
* DefaultAudioSink.AudioProcessorChain, boolean, boolean, int)}.
|
|
||||||
* <li>An audio track is playing in a format that the device supports offloading (for example,
|
* <li>An audio track is playing in a format that the device supports offloading (for example,
|
||||||
* MP3 or AAC).
|
* MP3 or AAC).
|
||||||
* <li>The {@link AudioSink} is playing with an offload {@link AudioTrack}.
|
* <li>The {@link AudioSink} is playing with an offload {@link AudioTrack}.
|
||||||
|
@ -57,6 +57,7 @@ import androidx.media3.exoplayer.audio.AudioSink.SinkFormatSupport;
|
|||||||
import androidx.media3.exoplayer.drm.DrmSession;
|
import androidx.media3.exoplayer.drm.DrmSession;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession.DrmSessionException;
|
import androidx.media3.exoplayer.drm.DrmSession.DrmSessionException;
|
||||||
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@ -175,7 +176,15 @@ public abstract class DecoderAudioRenderer<
|
|||||||
@Nullable AudioRendererEventListener eventListener,
|
@Nullable AudioRendererEventListener eventListener,
|
||||||
@Nullable AudioCapabilities audioCapabilities,
|
@Nullable AudioCapabilities audioCapabilities,
|
||||||
AudioProcessor... audioProcessors) {
|
AudioProcessor... audioProcessors) {
|
||||||
this(eventHandler, eventListener, new DefaultAudioSink(audioCapabilities, audioProcessors));
|
this(
|
||||||
|
eventHandler,
|
||||||
|
eventListener,
|
||||||
|
new DefaultAudioSink.Builder()
|
||||||
|
.setAudioCapabilities(
|
||||||
|
MoreObjects.firstNonNull(
|
||||||
|
audioCapabilities, AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES))
|
||||||
|
.setAudioProcessors(audioProcessors)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.audio;
|
package androidx.media3.exoplayer.audio;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ import androidx.media3.extractor.Ac3Util;
|
|||||||
import androidx.media3.extractor.Ac4Util;
|
import androidx.media3.extractor.Ac4Util;
|
||||||
import androidx.media3.extractor.DtsUtil;
|
import androidx.media3.extractor.DtsUtil;
|
||||||
import androidx.media3.extractor.MpegAudioUtil;
|
import androidx.media3.extractor.MpegAudioUtil;
|
||||||
|
import com.google.errorprone.annotations.InlineMe;
|
||||||
|
import com.google.errorprone.annotations.InlineMeValidationDisabled;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@ -58,6 +61,7 @@ import java.util.ArrayDeque;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays audio data. The implementation delegates to an {@link AudioTrack} and handles playback
|
* Plays audio data. The implementation delegates to an {@link AudioTrack} and handles playback
|
||||||
@ -212,6 +216,106 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A builder to create {@link DefaultAudioSink} instances. */
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
@Nullable private AudioCapabilities audioCapabilities;
|
||||||
|
@Nullable private AudioProcessorChain audioProcessorChain;
|
||||||
|
private boolean enableFloatOutput;
|
||||||
|
private boolean enableAudioTrackPlaybackParams;
|
||||||
|
private int offloadMode;
|
||||||
|
|
||||||
|
/** Creates a new builder. */
|
||||||
|
public Builder() {
|
||||||
|
offloadMode = OFFLOAD_MODE_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets audio capabilities for playback on this device. May be {@code null} if the default
|
||||||
|
* capabilities (no encoded audio passthrough support) should be assumed.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@code null}.
|
||||||
|
*/
|
||||||
|
public Builder setAudioCapabilities(@Nullable AudioCapabilities audioCapabilities) {
|
||||||
|
this.audioCapabilities = audioCapabilities;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an array of {@link AudioProcessor AudioProcessors}s that will process PCM audio before
|
||||||
|
* output. May be empty. Equivalent of {@code setAudioProcessorChain(new
|
||||||
|
* DefaultAudioProcessorChain(audioProcessors)}.
|
||||||
|
*
|
||||||
|
* <p>The default value is an empty array.
|
||||||
|
*/
|
||||||
|
public Builder setAudioProcessors(AudioProcessor[] audioProcessors) {
|
||||||
|
checkNotNull(audioProcessors);
|
||||||
|
return setAudioProcessorChain(new DefaultAudioProcessorChain(audioProcessors));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AudioProcessorChain} to process audio before playback. The instance passed in
|
||||||
|
* must not be reused in other sinks. Processing chains are only supported for PCM playback (not
|
||||||
|
* passthrough or offload).
|
||||||
|
*
|
||||||
|
* <p>By default, no processing will be applied.
|
||||||
|
*/
|
||||||
|
public Builder setAudioProcessorChain(AudioProcessorChain audioProcessorChain) {
|
||||||
|
checkNotNull(audioProcessorChain);
|
||||||
|
this.audioProcessorChain = audioProcessorChain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float
|
||||||
|
* output will be used if the input is 32-bit float, and also if the input is high resolution
|
||||||
|
* (24-bit or 32-bit) integer PCM. Float output is supported from API level 21. Audio processing
|
||||||
|
* (for example, speed adjustment) will not be available when float output is in use.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@code false}.
|
||||||
|
*/
|
||||||
|
public Builder setEnableFloatOutput(boolean enableFloatOutput) {
|
||||||
|
this.enableFloatOutput = enableFloatOutput;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to control the playback speed using the platform implementation (see {@link
|
||||||
|
* AudioTrack#setPlaybackParams(PlaybackParams)}), if supported. If set to {@code false}, speed
|
||||||
|
* up/down of the audio will be done by ExoPlayer (see {@link SonicAudioProcessor}). Platform
|
||||||
|
* speed adjustment is lower latency, but less reliable.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@code false}.
|
||||||
|
*/
|
||||||
|
public Builder setEnableAudioTrackPlaybackParams(boolean enableAudioTrackPlaybackParams) {
|
||||||
|
this.enableAudioTrackPlaybackParams = enableAudioTrackPlaybackParams;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the offload mode. If an audio format can be both played with offload and encoded audio
|
||||||
|
* passthrough, it will be played in offload. Audio offload is supported from API level 29. Most
|
||||||
|
* Android devices can only support one offload {@link AudioTrack} at a time and can invalidate
|
||||||
|
* it at any time. Thus an app can never be guaranteed that it will be able to play in offload.
|
||||||
|
* Audio processing (for example, speed adjustment) will not be available when offload is in
|
||||||
|
* use.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #OFFLOAD_MODE_DISABLED}.
|
||||||
|
*/
|
||||||
|
public Builder setOffloadMode(@OffloadMode int offloadMode) {
|
||||||
|
this.offloadMode = offloadMode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builds the {@link DefaultAudioSink}. Must only be called once per Builder instance. */
|
||||||
|
public DefaultAudioSink build() {
|
||||||
|
if (audioProcessorChain == null) {
|
||||||
|
audioProcessorChain = new DefaultAudioProcessorChain();
|
||||||
|
}
|
||||||
|
return new DefaultAudioSink(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** The default playback speed. */
|
/** The default playback speed. */
|
||||||
public static final float DEFAULT_PLAYBACK_SPEED = 1f;
|
public static final float DEFAULT_PLAYBACK_SPEED = 1f;
|
||||||
/** The minimum allowed playback speed. Lower values will be constrained to fall in range. */
|
/** The minimum allowed playback speed. Lower values will be constrained to fall in range. */
|
||||||
@ -379,76 +483,78 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
private boolean offloadDisabledUntilNextConfiguration;
|
private boolean offloadDisabledUntilNextConfiguration;
|
||||||
private boolean isWaitingForOffloadEndOfStreamHandled;
|
private boolean isWaitingForOffloadEndOfStreamHandled;
|
||||||
|
|
||||||
/**
|
/** @deprecated Use {@link Builder}. */
|
||||||
* Creates a new default audio sink.
|
@Deprecated
|
||||||
*
|
@InlineMeValidationDisabled("Migrate constructor to Builder")
|
||||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
@InlineMe(
|
||||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
replacement =
|
||||||
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before
|
"new DefaultAudioSink.Builder()"
|
||||||
* output. May be empty.
|
+ ".setAudioCapabilities(audioCapabilities)"
|
||||||
*/
|
+ ".setAudioProcessors(audioProcessors)"
|
||||||
|
+ ".build()",
|
||||||
|
imports = "androidx.media3.exoplayer.audio.DefaultAudioSink")
|
||||||
public DefaultAudioSink(
|
public DefaultAudioSink(
|
||||||
@Nullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors) {
|
@Nullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors) {
|
||||||
this(audioCapabilities, audioProcessors, /* enableFloatOutput= */ false);
|
this(new Builder().setAudioCapabilities(audioCapabilities).setAudioProcessors(audioProcessors));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use {@link Builder}. */
|
||||||
* Creates a new default audio sink, optionally using float output for high resolution PCM.
|
@Deprecated
|
||||||
*
|
@InlineMeValidationDisabled("Migrate constructor to Builder")
|
||||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
@InlineMe(
|
||||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
replacement =
|
||||||
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before
|
"new DefaultAudioSink.Builder()"
|
||||||
* output. May be empty.
|
+ ".setAudioCapabilities(audioCapabilities)"
|
||||||
* @param enableFloatOutput Whether to enable 32-bit float output. Where possible, 32-bit float
|
+ ".setAudioProcessors(audioProcessors)"
|
||||||
* output will be used if the input is 32-bit float, and also if the input is high resolution
|
+ ".setEnableFloatOutput(enableFloatOutput)"
|
||||||
* (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not
|
+ ".build()",
|
||||||
* be available when float output is in use.
|
imports = "androidx.media3.exoplayer.audio.DefaultAudioSink")
|
||||||
*/
|
|
||||||
public DefaultAudioSink(
|
public DefaultAudioSink(
|
||||||
@Nullable AudioCapabilities audioCapabilities,
|
@Nullable AudioCapabilities audioCapabilities,
|
||||||
AudioProcessor[] audioProcessors,
|
AudioProcessor[] audioProcessors,
|
||||||
boolean enableFloatOutput) {
|
boolean enableFloatOutput) {
|
||||||
this(
|
this(
|
||||||
audioCapabilities,
|
new Builder()
|
||||||
new DefaultAudioProcessorChain(audioProcessors),
|
.setAudioCapabilities(audioCapabilities)
|
||||||
enableFloatOutput,
|
.setAudioProcessors(audioProcessors)
|
||||||
/* enableAudioTrackPlaybackParams= */ false,
|
.setEnableFloatOutput(enableFloatOutput));
|
||||||
OFFLOAD_MODE_DISABLED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use {@link Builder}. */
|
||||||
* Creates a new default audio sink, optionally using float output for high resolution PCM and
|
@Deprecated
|
||||||
* with the specified {@code audioProcessorChain}.
|
@InlineMeValidationDisabled("Migrate constructor to Builder")
|
||||||
*
|
@InlineMe(
|
||||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
replacement =
|
||||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
"new DefaultAudioSink.Builder()"
|
||||||
* @param audioProcessorChain An {@link AudioProcessorChain} which is used to apply playback
|
+ ".setAudioCapabilities(audioCapabilities)"
|
||||||
* parameters adjustments. The instance passed in must not be reused in other sinks.
|
+ ".setAudioProcessorChain(audioProcessorChain)"
|
||||||
* @param enableFloatOutput Whether to enable 32-bit float output. Where possible, 32-bit float
|
+ ".setEnableFloatOutput(enableFloatOutput)"
|
||||||
* output will be used if the input is 32-bit float, and also if the input is high resolution
|
+ ".setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)"
|
||||||
* (24-bit or 32-bit) integer PCM. Float output is supported from API level 21. Audio
|
+ ".setOffloadMode(offloadMode)"
|
||||||
* processing (for example, speed adjustment) will not be available when float output is in
|
+ ".build()",
|
||||||
* use.
|
imports = "androidx.media3.exoplayer.audio.DefaultAudioSink")
|
||||||
* @param enableAudioTrackPlaybackParams Whether to enable setting playback speed using {@link
|
|
||||||
* android.media.AudioTrack#setPlaybackParams(PlaybackParams)}, if supported.
|
|
||||||
* @param offloadMode Audio offload configuration. If an audio format can be both played with
|
|
||||||
* offload and encoded audio passthrough, it will be played in offload. Audio offload is
|
|
||||||
* supported from API level 29. Most Android devices can only support one offload {@link
|
|
||||||
* android.media.AudioTrack} at a time and can invalidate it at any time. Thus an app can
|
|
||||||
* never be guaranteed that it will be able to play in offload. Audio processing (for example,
|
|
||||||
* speed adjustment) will not be available when offload is in use.
|
|
||||||
*/
|
|
||||||
public DefaultAudioSink(
|
public DefaultAudioSink(
|
||||||
@Nullable AudioCapabilities audioCapabilities,
|
@Nullable AudioCapabilities audioCapabilities,
|
||||||
AudioProcessorChain audioProcessorChain,
|
AudioProcessorChain audioProcessorChain,
|
||||||
boolean enableFloatOutput,
|
boolean enableFloatOutput,
|
||||||
boolean enableAudioTrackPlaybackParams,
|
boolean enableAudioTrackPlaybackParams,
|
||||||
@OffloadMode int offloadMode) {
|
@OffloadMode int offloadMode) {
|
||||||
this.audioCapabilities = audioCapabilities;
|
this(
|
||||||
this.audioProcessorChain = Assertions.checkNotNull(audioProcessorChain);
|
new Builder()
|
||||||
this.enableFloatOutput = Util.SDK_INT >= 21 && enableFloatOutput;
|
.setAudioCapabilities(audioCapabilities)
|
||||||
this.enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && enableAudioTrackPlaybackParams;
|
.setAudioProcessorChain(audioProcessorChain)
|
||||||
this.offloadMode = Util.SDK_INT >= 29 ? offloadMode : OFFLOAD_MODE_DISABLED;
|
.setEnableFloatOutput(enableFloatOutput)
|
||||||
|
.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
|
||||||
|
.setOffloadMode(offloadMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull("#1.audioProcessorChain")
|
||||||
|
private DefaultAudioSink(Builder builder) {
|
||||||
|
audioCapabilities = builder.audioCapabilities;
|
||||||
|
audioProcessorChain = builder.audioProcessorChain;
|
||||||
|
enableFloatOutput = Util.SDK_INT >= 21 && builder.enableFloatOutput;
|
||||||
|
enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
||||||
|
offloadMode = Util.SDK_INT >= 29 ? builder.offloadMode : OFFLOAD_MODE_DISABLED;
|
||||||
releasingConditionVariable = new ConditionVariable(true);
|
releasingConditionVariable = new ConditionVariable(true);
|
||||||
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
|
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
|
||||||
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
|
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
|
||||||
@ -596,8 +702,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (useOffloadedPlayback(inputFormat, audioAttributes)) {
|
if (useOffloadedPlayback(inputFormat, audioAttributes)) {
|
||||||
outputMode = OUTPUT_MODE_OFFLOAD;
|
outputMode = OUTPUT_MODE_OFFLOAD;
|
||||||
outputEncoding =
|
outputEncoding =
|
||||||
MimeTypes.getEncoding(
|
MimeTypes.getEncoding(checkNotNull(inputFormat.sampleMimeType), inputFormat.codecs);
|
||||||
Assertions.checkNotNull(inputFormat.sampleMimeType), inputFormat.codecs);
|
|
||||||
outputChannelConfig = Util.getAudioTrackChannelConfig(inputFormat.channelCount);
|
outputChannelConfig = Util.getAudioTrackChannelConfig(inputFormat.channelCount);
|
||||||
} else {
|
} else {
|
||||||
outputMode = OUTPUT_MODE_PASSTHROUGH;
|
outputMode = OUTPUT_MODE_PASSTHROUGH;
|
||||||
@ -871,7 +976,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
|
|
||||||
private AudioTrack buildAudioTrack() throws InitializationException {
|
private AudioTrack buildAudioTrack() throws InitializationException {
|
||||||
try {
|
try {
|
||||||
return Assertions.checkNotNull(configuration)
|
return checkNotNull(configuration)
|
||||||
.buildAudioTrack(tunneling, audioAttributes, audioSessionId);
|
.buildAudioTrack(tunneling, audioAttributes, audioSessionId);
|
||||||
} catch (InitializationException e) {
|
} catch (InitializationException e) {
|
||||||
maybeDisableOffload();
|
maybeDisableOffload();
|
||||||
@ -1216,7 +1321,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
audioTrack.pause();
|
audioTrack.pause();
|
||||||
}
|
}
|
||||||
if (isOffloadedPlayback(audioTrack)) {
|
if (isOffloadedPlayback(audioTrack)) {
|
||||||
Assertions.checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
||||||
}
|
}
|
||||||
// AudioTrack.release can take some time, so we call it on a background thread.
|
// AudioTrack.release can take some time, so we call it on a background thread.
|
||||||
final AudioTrack toRelease = audioTrack;
|
final AudioTrack toRelease = audioTrack;
|
||||||
@ -1518,8 +1623,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@C.Encoding
|
@C.Encoding
|
||||||
int encoding =
|
int encoding = MimeTypes.getEncoding(checkNotNull(format.sampleMimeType), format.codecs);
|
||||||
MimeTypes.getEncoding(Assertions.checkNotNull(format.sampleMimeType), format.codecs);
|
|
||||||
// Check for encodings that are known to work for passthrough with the implementation in this
|
// Check for encodings that are known to work for passthrough with the implementation in this
|
||||||
// class. This avoids trying to use passthrough with an encoding where the device/app reports
|
// class. This avoids trying to use passthrough with an encoding where the device/app reports
|
||||||
// it's capable but it is untested or known to be broken (for example AAC-LC).
|
// it's capable but it is untested or known to be broken (for example AAC-LC).
|
||||||
@ -1630,8 +1734,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@C.Encoding
|
@C.Encoding
|
||||||
int encoding =
|
int encoding = MimeTypes.getEncoding(checkNotNull(format.sampleMimeType), format.codecs);
|
||||||
MimeTypes.getEncoding(Assertions.checkNotNull(format.sampleMimeType), format.codecs);
|
|
||||||
if (encoding == C.ENCODING_INVALID) {
|
if (encoding == C.ENCODING_INVALID) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
mediaCodecSelector,
|
mediaCodecSelector,
|
||||||
eventHandler,
|
eventHandler,
|
||||||
eventListener,
|
eventListener,
|
||||||
new DefaultAudioSink(audioCapabilities, audioProcessors));
|
new DefaultAudioSink.Builder()
|
||||||
|
.setAudioCapabilities(audioCapabilities)
|
||||||
|
.setAudioProcessors(audioProcessors)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.audio;
|
package androidx.media3.exoplayer.audio;
|
||||||
|
|
||||||
|
import static androidx.media3.exoplayer.audio.AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES;
|
||||||
import static androidx.media3.exoplayer.audio.AudioSink.CURRENT_POSITION_NOT_SET;
|
import static androidx.media3.exoplayer.audio.AudioSink.CURRENT_POSITION_NOT_SET;
|
||||||
import static androidx.media3.exoplayer.audio.AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY;
|
import static androidx.media3.exoplayer.audio.AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY;
|
||||||
import static androidx.media3.exoplayer.audio.AudioSink.SINK_FORMAT_SUPPORTED_WITH_TRANSCODING;
|
import static androidx.media3.exoplayer.audio.AudioSink.SINK_FORMAT_SUPPORTED_WITH_TRANSCODING;
|
||||||
@ -24,6 +25,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
|
import androidx.media3.exoplayer.audio.DefaultAudioSink.DefaultAudioProcessorChain;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@ -37,7 +39,6 @@ import org.robolectric.annotation.Config;
|
|||||||
/** Unit tests for {@link DefaultAudioSink}. */
|
/** Unit tests for {@link DefaultAudioSink}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class DefaultAudioSinkTest {
|
public final class DefaultAudioSinkTest {
|
||||||
|
|
||||||
private static final int CHANNEL_COUNT_MONO = 1;
|
private static final int CHANNEL_COUNT_MONO = 1;
|
||||||
private static final int CHANNEL_COUNT_STEREO = 2;
|
private static final int CHANNEL_COUNT_STEREO = 2;
|
||||||
private static final int BYTES_PER_FRAME_16_BIT = 2;
|
private static final int BYTES_PER_FRAME_16_BIT = 2;
|
||||||
@ -59,19 +60,20 @@ public final class DefaultAudioSinkTest {
|
|||||||
arrayAudioBufferSink = new ArrayAudioBufferSink();
|
arrayAudioBufferSink = new ArrayAudioBufferSink();
|
||||||
TeeAudioProcessor teeAudioProcessor = new TeeAudioProcessor(arrayAudioBufferSink);
|
TeeAudioProcessor teeAudioProcessor = new TeeAudioProcessor(arrayAudioBufferSink);
|
||||||
defaultAudioSink =
|
defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder()
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
.setAudioCapabilities(DEFAULT_AUDIO_CAPABILITIES)
|
||||||
new DefaultAudioSink.DefaultAudioProcessorChain(teeAudioProcessor),
|
.setAudioProcessorChain(new DefaultAudioProcessorChain(teeAudioProcessor))
|
||||||
/* enableFloatOutput= */ false,
|
.setOffloadMode(DefaultAudioSink.OFFLOAD_MODE_DISABLED)
|
||||||
/* enableAudioTrackPlaybackParams= */ false,
|
.build();
|
||||||
DefaultAudioSink.OFFLOAD_MODE_DISABLED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void handlesSpecializedAudioProcessorArray() {
|
public void handlesSpecializedAudioProcessorArray() {
|
||||||
defaultAudioSink =
|
defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder()
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES, new TeeAudioProcessor[0]);
|
.setAudioCapabilities(DEFAULT_AUDIO_CAPABILITIES)
|
||||||
|
.setAudioProcessors(new TeeAudioProcessor[0])
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -203,10 +205,7 @@ public final class DefaultAudioSinkTest {
|
|||||||
@Test
|
@Test
|
||||||
public void floatPcmNeedsTranscodingIfFloatOutputDisabled() {
|
public void floatPcmNeedsTranscodingIfFloatOutputDisabled() {
|
||||||
defaultAudioSink =
|
defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder().setAudioCapabilities(DEFAULT_AUDIO_CAPABILITIES).build();
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
|
||||||
new AudioProcessor[0],
|
|
||||||
/* enableFloatOutput= */ false);
|
|
||||||
Format floatFormat =
|
Format floatFormat =
|
||||||
STEREO_44_1_FORMAT
|
STEREO_44_1_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
@ -221,10 +220,10 @@ public final class DefaultAudioSinkTest {
|
|||||||
@Test
|
@Test
|
||||||
public void floatPcmNeedsTranscodingIfFloatOutputEnabledBeforeApi21() {
|
public void floatPcmNeedsTranscodingIfFloatOutputEnabledBeforeApi21() {
|
||||||
defaultAudioSink =
|
defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder()
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
.setAudioCapabilities(DEFAULT_AUDIO_CAPABILITIES)
|
||||||
new AudioProcessor[0],
|
.setEnableFloatOutput(true)
|
||||||
/* enableFloatOutput= */ true);
|
.build();
|
||||||
Format floatFormat =
|
Format floatFormat =
|
||||||
STEREO_44_1_FORMAT
|
STEREO_44_1_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
@ -239,10 +238,10 @@ public final class DefaultAudioSinkTest {
|
|||||||
@Test
|
@Test
|
||||||
public void floatOutputSupportedIfFloatOutputEnabledFromApi21() {
|
public void floatOutputSupportedIfFloatOutputEnabledFromApi21() {
|
||||||
defaultAudioSink =
|
defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder()
|
||||||
AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
|
.setAudioCapabilities(DEFAULT_AUDIO_CAPABILITIES)
|
||||||
new AudioProcessor[0],
|
.setEnableFloatOutput(true)
|
||||||
/* enableFloatOutput= */ true);
|
.build();
|
||||||
Format floatFormat =
|
Format floatFormat =
|
||||||
STEREO_44_1_FORMAT
|
STEREO_44_1_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
@ -267,8 +266,9 @@ public final class DefaultAudioSinkTest {
|
|||||||
@Test
|
@Test
|
||||||
public void audioSinkWithAacAudioCapabilitiesWithoutOffload_doesNotSupportAac() {
|
public void audioSinkWithAacAudioCapabilitiesWithoutOffload_doesNotSupportAac() {
|
||||||
DefaultAudioSink defaultAudioSink =
|
DefaultAudioSink defaultAudioSink =
|
||||||
new DefaultAudioSink(
|
new DefaultAudioSink.Builder()
|
||||||
new AudioCapabilities(new int[] {C.ENCODING_AAC_LC}, 2), new AudioProcessor[0]);
|
.setAudioCapabilities(new AudioCapabilities(new int[] {C.ENCODING_AAC_LC}, 2))
|
||||||
|
.build();
|
||||||
Format aacLcFormat =
|
Format aacLcFormat =
|
||||||
STEREO_44_1_FORMAT
|
STEREO_44_1_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
|
@ -33,7 +33,6 @@ import androidx.media3.exoplayer.DefaultRenderersFactory;
|
|||||||
import androidx.media3.exoplayer.Renderer;
|
import androidx.media3.exoplayer.Renderer;
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
import androidx.media3.exoplayer.RenderersFactory;
|
||||||
import androidx.media3.exoplayer.audio.AudioCapabilities;
|
import androidx.media3.exoplayer.audio.AudioCapabilities;
|
||||||
import androidx.media3.exoplayer.audio.AudioProcessor;
|
|
||||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||||
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
||||||
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
||||||
@ -96,7 +95,9 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
/* enableDecoderFallback= */ false,
|
/* enableDecoderFallback= */ false,
|
||||||
eventHandler,
|
eventHandler,
|
||||||
audioRendererEventListener,
|
audioRendererEventListener,
|
||||||
new DefaultAudioSink(AudioCapabilities.getCapabilities(context), new AudioProcessor[0])),
|
new DefaultAudioSink.Builder()
|
||||||
|
.setAudioCapabilities(AudioCapabilities.getCapabilities(context))
|
||||||
|
.build()),
|
||||||
new TextRenderer(textRendererOutput, eventHandler.getLooper()),
|
new TextRenderer(textRendererOutput, eventHandler.getLooper()),
|
||||||
new MetadataRenderer(metadataRendererOutput, eventHandler.getLooper())
|
new MetadataRenderer(metadataRendererOutput, eventHandler.getLooper())
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user