diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java index 75d23dfa2e..1a70310a8d 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java @@ -20,7 +20,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioRendererEventListener; -import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer; import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.util.MimeTypes; @@ -54,11 +53,10 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer { * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, - AudioCapabilities audioCapabilities, int streamType) { - super(eventHandler, eventListener, audioCapabilities, streamType); + AudioCapabilities audioCapabilities) { + super(eventHandler, eventListener, audioCapabilities); } @Override diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java index 0562851d3e..954a090ee9 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java @@ -19,7 +19,6 @@ import android.os.Handler; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioRendererEventListener; -import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer; import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.util.MimeTypes; @@ -50,11 +49,10 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer { * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, - AudioCapabilities audioCapabilities, int streamType) { - super(eventHandler, eventListener, audioCapabilities, streamType); + AudioCapabilities audioCapabilities) { + super(eventHandler, eventListener, audioCapabilities); } @Override diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java index 60e5ff34b4..2dd2697aab 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java @@ -19,7 +19,6 @@ import android.os.Handler; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioRendererEventListener; -import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.ExoMediaCrypto; @@ -52,11 +51,10 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer { * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, - AudioCapabilities audioCapabilities, int streamType) { - super(eventHandler, eventListener, audioCapabilities, streamType); + AudioCapabilities audioCapabilities) { + super(eventHandler, eventListener, audioCapabilities); } /** @@ -65,12 +63,11 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer { * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, - AudioCapabilities audioCapabilities, int streamType, - DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) { - super(eventHandler, eventListener, audioCapabilities, streamType, drmSessionManager, + AudioCapabilities audioCapabilities, DrmSessionManager drmSessionManager, + boolean playClearSamplesWithoutKeys) { + super(eventHandler, eventListener, audioCapabilities, drmSessionManager, playClearSamplesWithoutKeys); } diff --git a/library/src/main/java/com/google/android/exoplayer2/C.java b/library/src/main/java/com/google/android/exoplayer2/C.java index 8c69524e95..3392aa64c6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/src/main/java/com/google/android/exoplayer2/C.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2; import android.media.AudioFormat; +import android.media.AudioManager; import android.media.MediaCodec; import android.support.annotation.IntDef; import android.view.Surface; @@ -159,6 +160,42 @@ public final class C { public static final int CHANNEL_OUT_7POINT1_SURROUND = Util.SDK_INT < 23 ? AudioFormat.CHANNEL_OUT_7POINT1 : AudioFormat.CHANNEL_OUT_7POINT1_SURROUND; + /** + * Stream types for an {@link android.media.AudioTrack}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({STREAM_TYPE_ALARM, STREAM_TYPE_MUSIC, STREAM_TYPE_NOTIFICATION, STREAM_TYPE_RING, + STREAM_TYPE_SYSTEM, STREAM_TYPE_VOICE_CALL}) + public @interface StreamType {} + /** + * @see AudioManager#STREAM_ALARM + */ + public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM; + /** + * @see AudioManager#STREAM_MUSIC + */ + public static final int STREAM_TYPE_MUSIC = AudioManager.STREAM_MUSIC; + /** + * @see AudioManager#STREAM_NOTIFICATION + */ + public static final int STREAM_TYPE_NOTIFICATION = AudioManager.STREAM_NOTIFICATION; + /** + * @see AudioManager#STREAM_RING + */ + public static final int STREAM_TYPE_RING = AudioManager.STREAM_RING; + /** + * @see AudioManager#STREAM_SYSTEM + */ + public static final int STREAM_TYPE_SYSTEM = AudioManager.STREAM_SYSTEM; + /** + * @see AudioManager#STREAM_VOICE_CALL + */ + public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL; + /** + * The default stream type used by audio renderers. + */ + public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC; + /** * Flags which can apply to a buffer containing a media sample. */ @@ -397,21 +434,35 @@ public final class C { public static final int MSG_SET_SURFACE = 1; /** - * The type of a message that can be passed to an audio {@link Renderer} via + * A type of a message that can be passed to an audio {@link Renderer} via * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object * should be a {@link Float} with 0 being silence and 1 being unity gain. */ public static final int MSG_SET_VOLUME = 2; /** - * The type of a message that can be passed to an audio {@link Renderer} via + * A type of a message that can be passed to an audio {@link Renderer} via * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object - * should be a {@link android.media.PlaybackParams}, which will be used to configure the + * should be a {@link android.media.PlaybackParams}, or null, which will be used to configure the * underlying {@link android.media.AudioTrack}. The message object should not be modified by the * caller after it has been passed */ public static final int MSG_SET_PLAYBACK_PARAMS = 3; + /** + * A type of a message that can be passed to an audio {@link Renderer} via + * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object + * should be one of the integer stream types in {@link C.StreamType}, and will specify the stream + * type of the underlying {@link android.media.AudioTrack}. See also + * {@link android.media.AudioTrack#AudioTrack(int, int, int, int, int, int)}. If the stream type + * is not set, audio renderers use {@link #STREAM_TYPE_DEFAULT}. + *

+ * Note that when the stream type changes, the AudioTrack must be reinitialized, which can + * introduce a brief gap in audio output. Note also that tracks in the same audio session must + * share the same routing, so a new audio session id will be generated. + */ + public static final int MSG_SET_STREAM_TYPE = 4; + /** * Applications or extensions may define custom {@code MSG_*} constants greater than or equal to * this value. diff --git a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index ad12002b17..bb5f3d42ed 100644 --- a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2; import android.annotation.TargetApi; import android.content.Context; import android.graphics.SurfaceTexture; -import android.media.AudioManager; import android.media.MediaCodec; import android.media.PlaybackParams; import android.os.Handler; @@ -114,6 +113,8 @@ public final class SimpleExoPlayer implements ExoPlayer { private DecoderCounters videoDecoderCounters; private DecoderCounters audioDecoderCounters; private int audioSessionId; + @C.StreamType + private int audioStreamType; private float volume; private PlaybackParamsHolder playbackParamsHolder; @@ -152,6 +153,7 @@ public final class SimpleExoPlayer implements ExoPlayer { // Set initial values. audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + audioStreamType = C.STREAM_TYPE_DEFAULT; volume = 1; // Build the player and associated objects. @@ -232,6 +234,36 @@ public final class SimpleExoPlayer implements ExoPlayer { } } + /** + * Sets the stream type for audio playback (see {@link C.StreamType} and + * {@link android.media.AudioTrack#AudioTrack(int, int, int, int, int, int)}). If the stream type + * is not set, audio renderers use {@link C#STREAM_TYPE_DEFAULT}. + *

+ * Note that when the stream type changes, the AudioTrack must be reinitialized, which can + * introduce a brief gap in audio output. Note also that tracks in the same audio session must + * share the same routing, so a new audio session id will be generated. + * + * @param audioStreamType The stream type for audio playback. + */ + public void setAudioStreamType(@C.StreamType int audioStreamType) { + this.audioStreamType = audioStreamType; + ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount]; + int count = 0; + for (Renderer renderer : renderers) { + if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { + messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_STREAM_TYPE, audioStreamType); + } + } + player.sendMessages(messages); + } + + /** + * Returns the stream type for audio playback. + */ + public @C.StreamType int getAudioStreamType() { + return audioStreamType; + } + /** * Sets the audio volume, with 0 being silence and 1 being unity gain. * @@ -543,7 +575,7 @@ public final class SimpleExoPlayer implements ExoPlayer { Renderer audioRenderer = new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, componentListener, - AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); + AudioCapabilities.getCapabilities(context)); renderersList.add(audioRenderer); Renderer textRenderer = new TextRenderer(componentListener, mainHandler.getLooper()); diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java index 41b0397b01..8e6cf68dc8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java @@ -249,7 +249,6 @@ public final class AudioTrack { public static boolean failOnSpuriousAudioTimestamp = false; private final AudioCapabilities audioCapabilities; - private final int streamType; private final Listener listener; private final ConditionVariable releasingConditionVariable; private final long[] playheadOffsets; @@ -263,6 +262,8 @@ public final class AudioTrack { private android.media.AudioTrack audioTrack; private int sampleRate; private int channelConfig; + @C.StreamType + private int streamType; @C.Encoding private int sourceEncoding; @C.Encoding @@ -301,12 +302,10 @@ public final class AudioTrack { /** * @param audioCapabilities The current audio capabilities. - * @param streamType The type of audio stream for the underlying {@link android.media.AudioTrack}. * @param listener Listener for audio track events. */ - public AudioTrack(AudioCapabilities audioCapabilities, int streamType, Listener listener) { + public AudioTrack(AudioCapabilities audioCapabilities, Listener listener) { this.audioCapabilities = audioCapabilities; - this.streamType = streamType; this.listener = listener; releasingConditionVariable = new ConditionVariable(true); if (Util.SDK_INT >= 18) { @@ -327,6 +326,7 @@ public final class AudioTrack { playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT]; volume = 1.0f; startMediaTimeState = START_NOT_SET; + streamType = C.STREAM_TYPE_DEFAULT; } /** @@ -742,6 +742,24 @@ public final class AudioTrack { audioTrackUtil.setPlaybackParams(playbackParams); } + /** + * Sets the stream type for audio track. If the stream type has changed, {@link #isInitialized()} + * will return {@code false} and the caller must re-{@link #initialize(int)} the audio track + * before writing more data. The caller must not reuse the audio session identifier when + * re-initializing with a new stream type. + * + * @param streamType The {@link C.StreamType} to use for audio output. + * @return Whether the stream type changed. + */ + public boolean setStreamType(@C.StreamType int streamType) { + if (this.streamType == streamType) { + return false; + } + this.streamType = streamType; + reset(); + return true; + } + /** * Sets the playback volume. * diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index fb793c6a60..9d9601103e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.audio; import android.annotation.TargetApi; -import android.media.AudioManager; import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; @@ -107,7 +106,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media boolean playClearSamplesWithoutKeys, Handler eventHandler, AudioRendererEventListener eventListener) { this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, - eventListener, null, AudioManager.STREAM_MUSIC); + eventListener, null); } /** @@ -124,16 +123,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, Handler eventHandler, - AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities, - int streamType) { + AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) { super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); audioSessionId = AudioTrack.SESSION_ID_NOT_SET; - audioTrack = new AudioTrack(audioCapabilities, streamType, this); + audioTrack = new AudioTrack(audioCapabilities, this); eventDispatcher = new EventDispatcher(eventHandler, eventListener); } @@ -387,6 +384,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media case C.MSG_SET_PLAYBACK_PARAMS: audioTrack.setPlaybackParams((PlaybackParams) message); break; + case C.MSG_SET_STREAM_TYPE: + @C.StreamType int streamType = (Integer) message; + if (audioTrack.setStreamType(streamType)) { + audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + } + break; default: super.handleMessage(messageType, message); break; diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java index c8bc9066f6..f8ba3c6ad8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.audio; -import android.media.AudioManager; import android.media.PlaybackParams; import android.os.Handler; import android.os.Looper; @@ -47,8 +46,9 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements private final boolean playClearSamplesWithoutKeys; private final EventDispatcher eventDispatcher; - private final FormatHolder formatHolder; + private final AudioTrack audioTrack; private final DrmSessionManager drmSessionManager; + private final FormatHolder formatHolder; private DecoderCounters decoderCounters; private Format inputFormat; @@ -65,7 +65,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements private boolean outputStreamEnded; private boolean waitingForKeys; - private final AudioTrack audioTrack; private int audioSessionId; public SimpleDecoderAudioRenderer() { @@ -79,7 +78,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements */ public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener) { - this(eventHandler, eventListener, null, AudioManager.STREAM_MUSIC); + this(eventHandler, eventListener, null); } /** @@ -88,12 +87,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. */ public SimpleDecoderAudioRenderer(Handler eventHandler, - AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities, - int streamType) { - this(eventHandler, eventListener, audioCapabilities, streamType, null, false); + AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) { + this(eventHandler, eventListener, audioCapabilities, null, false); } /** @@ -102,7 +99,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements * @param eventListener A listener of events. May be null if delivery of events is not required. * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. - * @param streamType The type of audio stream for the {@link AudioTrack}. * @param drmSessionManager For use with encrypted media. May be null if support for encrypted * media is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. @@ -113,15 +109,14 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements */ public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities, - int streamType, DrmSessionManager drmSessionManager, - boolean playClearSamplesWithoutKeys) { + DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) { super(C.TRACK_TYPE_AUDIO); - this.drmSessionManager = drmSessionManager; - this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; eventDispatcher = new EventDispatcher(eventHandler, eventListener); - audioSessionId = AudioTrack.SESSION_ID_NOT_SET; - audioTrack = new AudioTrack(audioCapabilities, streamType, this); + audioTrack = new AudioTrack(audioCapabilities, this); + this.drmSessionManager = drmSessionManager; formatHolder = new FormatHolder(); + this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; + audioSessionId = AudioTrack.SESSION_ID_NOT_SET; } @Override @@ -473,6 +468,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements case C.MSG_SET_PLAYBACK_PARAMS: audioTrack.setPlaybackParams((PlaybackParams) message); break; + case C.MSG_SET_STREAM_TYPE: + @C.StreamType int streamType = (Integer) message; + if (audioTrack.setStreamType(streamType)) { + audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + } + break; default: super.handleMessage(messageType, message); break;