Commit 9db0dbc0 by borrelli Committed by Oliver Woodman

Allow SimpleExoPlayer to handle audio focus

Add automatic audio focus handling to SimpleExoPlayer. Audio focus
handling is an opt-in feature that can be requested by passing
the system's AudioManager and an AudioFocusConfiguration to
SimpleExoPlayer.setAudioFocusConfiguration.

When audio focus is being managed by SimpleExoPlayer, the player
will transparently handle pausing playback during
AUDIOFOCUS_LOSS_TRANSIENT, as well as lowering playback volume
during AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=208045732
parent c1998da4
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Add a flag to opt-in to automatic audio focus handling via
`SimpleExoPlayer.setAudioAttributes`.
* Distribute Cronet extension via jCenter. * Distribute Cronet extension via jCenter.
* Add `AudioListener` for listening to changes in audio configuration during * Add `AudioListener` for listening to changes in audio configuration during
playback ([#3994](https://github.com/google/ExoPlayer/issues/3994)). playback ([#3994](https://github.com/google/ExoPlayer/issues/3994)).
......
...@@ -283,24 +283,32 @@ public final class C { ...@@ -283,24 +283,32 @@ public final class C {
public static final int FLAG_AUDIBILITY_ENFORCED = public static final int FLAG_AUDIBILITY_ENFORCED =
android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED; android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED;
/** /** Usage types for {@link com.google.android.exoplayer2.audio.AudioAttributes}. */
* Usage types for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
*/
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({USAGE_ALARM, USAGE_ASSISTANCE_ACCESSIBILITY, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, @IntDef({
USAGE_ASSISTANCE_SONIFICATION, USAGE_GAME, USAGE_MEDIA, USAGE_NOTIFICATION, USAGE_ALARM,
USAGE_NOTIFICATION_COMMUNICATION_DELAYED, USAGE_NOTIFICATION_COMMUNICATION_INSTANT, USAGE_ASSISTANCE_ACCESSIBILITY,
USAGE_NOTIFICATION_COMMUNICATION_REQUEST, USAGE_NOTIFICATION_EVENT, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
USAGE_NOTIFICATION_RINGTONE, USAGE_UNKNOWN, USAGE_VOICE_COMMUNICATION, USAGE_ASSISTANCE_SONIFICATION,
USAGE_VOICE_COMMUNICATION_SIGNALLING}) USAGE_ASSISTANT,
USAGE_GAME,
USAGE_MEDIA,
USAGE_NOTIFICATION,
USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
USAGE_NOTIFICATION_EVENT,
USAGE_NOTIFICATION_RINGTONE,
USAGE_UNKNOWN,
USAGE_VOICE_COMMUNICATION,
USAGE_VOICE_COMMUNICATION_SIGNALLING
})
public @interface AudioUsage {} public @interface AudioUsage {}
/** /**
* @see android.media.AudioAttributes#USAGE_ALARM * @see android.media.AudioAttributes#USAGE_ALARM
*/ */
public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM; public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM;
/** /** @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY */
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY
*/
public static final int USAGE_ASSISTANCE_ACCESSIBILITY = public static final int USAGE_ASSISTANCE_ACCESSIBILITY =
android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
/** /**
...@@ -313,6 +321,8 @@ public final class C { ...@@ -313,6 +321,8 @@ public final class C {
*/ */
public static final int USAGE_ASSISTANCE_SONIFICATION = public static final int USAGE_ASSISTANCE_SONIFICATION =
android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
/** @see android.media.AudioAttributes#USAGE_ASSISTANT */
public static final int USAGE_ASSISTANT = android.media.AudioAttributes.USAGE_ASSISTANT;
/** /**
* @see android.media.AudioAttributes#USAGE_GAME * @see android.media.AudioAttributes#USAGE_GAME
*/ */
...@@ -365,6 +375,29 @@ public final class C { ...@@ -365,6 +375,29 @@ public final class C {
public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING =
android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
/** Audio focus types for {@link AudioFocusConfiguration}. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
AUDIOFOCUS_NONE,
AUDIOFOCUS_GAIN,
AUDIOFOCUS_GAIN_TRANSIENT,
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
})
public @interface AudioFocusGain {}
/** @see AudioManager#AUDIOFOCUS_NONE */
public static final int AUDIOFOCUS_NONE = AudioManager.AUDIOFOCUS_NONE;
/** @see AudioManager#AUDIOFOCUS_GAIN */
public static final int AUDIOFOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT */
public static final int AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK */
public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK =
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE */
public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE =
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
/** /**
* Flags which can apply to a buffer containing a media sample. * Flags which can apply to a buffer containing a media sample.
*/ */
......
...@@ -19,6 +19,7 @@ import android.content.Context; ...@@ -19,6 +19,7 @@ import android.content.Context;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.analytics.AnalyticsCollector; import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
...@@ -145,7 +146,9 @@ public final class ExoPlayerFactory { ...@@ -145,7 +146,9 @@ public final class ExoPlayerFactory {
* *
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance. * @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @deprecated Use {@link #newSimpleInstance(Context, RenderersFactory, TrackSelector)}. * @deprecated Use {@link #newSimpleInstance(Context, RenderersFactory, TrackSelector)}. The use
* of {@link SimpleExoPlayer#setAudioAttributes(AudioAttributes, boolean)} to manage audio
* focus will be unavailable for the {@link SimpleExoPlayer} returned by this method.
*/ */
@Deprecated @Deprecated
@SuppressWarnings("nullness:argument.type.incompatible") @SuppressWarnings("nullness:argument.type.incompatible")
......
...@@ -66,6 +66,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -66,6 +66,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final ArrayDeque<PlaybackInfoUpdate> pendingPlaybackInfoUpdates; private final ArrayDeque<PlaybackInfoUpdate> pendingPlaybackInfoUpdates;
private boolean playWhenReady; private boolean playWhenReady;
private boolean internalPlayWhenReady;
private @RepeatMode int repeatMode; private @RepeatMode int repeatMode;
private boolean shuffleModeEnabled; private boolean shuffleModeEnabled;
private int pendingOperationAcks; private int pendingOperationAcks;
...@@ -219,9 +220,17 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -219,9 +220,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override @Override
public void setPlayWhenReady(boolean playWhenReady) { public void setPlayWhenReady(boolean playWhenReady) {
setPlayWhenReady(playWhenReady, /* suppressPlayback= */ false);
}
public void setPlayWhenReady(boolean playWhenReady, boolean suppressPlayback) {
boolean internalPlayWhenReady = playWhenReady && !suppressPlayback;
if (this.internalPlayWhenReady != internalPlayWhenReady) {
this.internalPlayWhenReady = internalPlayWhenReady;
internalPlayer.setPlayWhenReady(internalPlayWhenReady);
}
if (this.playWhenReady != playWhenReady) { if (this.playWhenReady != playWhenReady) {
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
internalPlayer.setPlayWhenReady(playWhenReady);
updatePlaybackInfo( updatePlaybackInfo(
playbackInfo, playbackInfo,
/* positionDiscontinuity= */ false, /* positionDiscontinuity= */ false,
......
...@@ -91,7 +91,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -91,7 +91,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
/* manifest= */ null, /* manifest= */ null,
DUMMY_MEDIA_PERIOD_ID, DUMMY_MEDIA_PERIOD_ID,
startPositionUs, startPositionUs,
/* contentPositionUs =*/ C.TIME_UNSET, /* contentPositionUs= */ C.TIME_UNSET,
Player.STATE_IDLE, Player.STATE_IDLE,
/* isLoading= */ false, /* isLoading= */ false,
TrackGroupArray.EMPTY, TrackGroupArray.EMPTY,
......
...@@ -85,9 +85,34 @@ public interface Player { ...@@ -85,9 +85,34 @@ public interface Player {
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}. * equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
* *
* @param audioAttributes The attributes to use for audio playback. * @param audioAttributes The attributes to use for audio playback.
* @deprecated Use {@link AudioComponent#setAudioAttributes(AudioAttributes, boolean)}.
*/ */
@Deprecated
void setAudioAttributes(AudioAttributes audioAttributes); void setAudioAttributes(AudioAttributes audioAttributes);
/**
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
* default audio attributes will be used. They are suitable for general media playback.
*
* <p>Setting the audio attributes during playback may introduce a short gap in audio output as
* the audio track is recreated. A new audio session id will also be generated.
*
* <p>If tunneling is enabled by the track selector, the specified audio attributes will be
* ignored, but they will take effect if audio is later played without tunneling.
*
* <p>If the device is running a build before platform API version 21, audio attributes cannot
* be set directly on the underlying audio track. In this case, the usage will be mapped onto an
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
*
* <p>If audio focus should be handled, the {@link AudioAttributes#usage} must be {@link
* C#USAGE_MEDIA} or {@link C#USAGE_GAME}. Other usages will throw an {@link
* IllegalArgumentException}.
*
* @param audioAttributes The attributes to use for audio playback.
* @param handleAudioFocus True if the player should handle audio focus, false otherwise.
*/
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
/** Returns the attributes for audio playback. */ /** Returns the attributes for audio playback. */
AudioAttributes getAudioAttributes(); AudioAttributes getAudioAttributes();
......
...@@ -19,7 +19,6 @@ import android.annotation.TargetApi; ...@@ -19,7 +19,6 @@ import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.PlaybackParams; import android.media.PlaybackParams;
import android.os.Handler; import android.os.Handler;
...@@ -33,6 +32,7 @@ import android.view.TextureView; ...@@ -33,6 +32,7 @@ import android.view.TextureView;
import com.google.android.exoplayer2.analytics.AnalyticsCollector; import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioFocusManager;
import com.google.android.exoplayer2.audio.AudioListener; import com.google.android.exoplayer2.audio.AudioListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
...@@ -72,7 +72,7 @@ public class SimpleExoPlayer ...@@ -72,7 +72,7 @@ public class SimpleExoPlayer
protected final Renderer[] renderers; protected final Renderer[] renderers;
private final ExoPlayer player; private final ExoPlayerImpl player;
private final Handler eventHandler; private final Handler eventHandler;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener> private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener>
...@@ -85,8 +85,7 @@ public class SimpleExoPlayer ...@@ -85,8 +85,7 @@ public class SimpleExoPlayer
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final AnalyticsCollector analyticsCollector; private final AnalyticsCollector analyticsCollector;
@SuppressWarnings({"unused", "FieldCanBeLocal"}) private final AudioFocusManager audioFocusManager;
private final @Nullable AudioManager audioManager;
private Format videoFormat; private Format videoFormat;
private Format audioFormat; private Format audioFormat;
...@@ -116,7 +115,9 @@ public class SimpleExoPlayer ...@@ -116,7 +115,9 @@ public class SimpleExoPlayer
* @param looper The {@link Looper} which must be used for all calls to the player and which is * @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on. * used to call listeners on.
* @deprecated Use {@link #SimpleExoPlayer(Context, RenderersFactory, TrackSelector, LoadControl, * @deprecated Use {@link #SimpleExoPlayer(Context, RenderersFactory, TrackSelector, LoadControl,
* BandwidthMeter, DrmSessionManager, Looper)}. * BandwidthMeter, DrmSessionManager, Looper)}. The use of {@link
* SimpleExoPlayer#setAudioAttributes(AudioAttributes, boolean)} to manage audio focus will be
* unavailable for a player created with this constructor.
*/ */
@Deprecated @Deprecated
protected SimpleExoPlayer( protected SimpleExoPlayer(
...@@ -253,7 +254,7 @@ public class SimpleExoPlayer ...@@ -253,7 +254,7 @@ public class SimpleExoPlayer
// Build the player and associated objects. // Build the player and associated objects.
player = player =
createExoPlayerImpl(renderers, trackSelector, loadControl, bandwidthMeter, clock, looper); new ExoPlayerImpl(renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
analyticsCollector = analyticsCollectorFactory.createAnalyticsCollector(player, clock); analyticsCollector = analyticsCollectorFactory.createAnalyticsCollector(player, clock);
addListener(analyticsCollector); addListener(analyticsCollector);
videoDebugListeners.add(analyticsCollector); videoDebugListeners.add(analyticsCollector);
...@@ -265,13 +266,7 @@ public class SimpleExoPlayer ...@@ -265,13 +266,7 @@ public class SimpleExoPlayer
if (drmSessionManager instanceof DefaultDrmSessionManager) { if (drmSessionManager instanceof DefaultDrmSessionManager) {
((DefaultDrmSessionManager) drmSessionManager).addListener(eventHandler, analyticsCollector); ((DefaultDrmSessionManager) drmSessionManager).addListener(eventHandler, analyticsCollector);
} }
// TODO: Remove null check once the deprecated factory method and constructor that don't take audioFocusManager = new AudioFocusManager(context, componentListener);
// Contexts have been removed.
audioManager =
context == null
? null
: (AudioManager)
context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
} }
@Override @Override
...@@ -417,22 +412,32 @@ public class SimpleExoPlayer ...@@ -417,22 +412,32 @@ public class SimpleExoPlayer
@Override @Override
public void setAudioAttributes(AudioAttributes audioAttributes) { public void setAudioAttributes(AudioAttributes audioAttributes) {
if (Util.areEqual(this.audioAttributes, audioAttributes)) { setAudioAttributes(audioAttributes, /* handleAudioFocus= */ false);
return; }
}
this.audioAttributes = audioAttributes; @Override
for (Renderer renderer : renderers) { public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { if (!Util.areEqual(this.audioAttributes, audioAttributes)) {
player this.audioAttributes = audioAttributes;
.createMessage(renderer) for (Renderer renderer : renderers) {
.setType(C.MSG_SET_AUDIO_ATTRIBUTES) if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
.setPayload(audioAttributes) player
.send(); .createMessage(renderer)
.setType(C.MSG_SET_AUDIO_ATTRIBUTES)
.setPayload(audioAttributes)
.send();
}
}
for (AudioListener audioListener : audioListeners) {
audioListener.onAudioAttributesChanged(audioAttributes);
} }
} }
for (AudioListener audioListener : audioListeners) {
audioListener.onAudioAttributesChanged(audioAttributes); @AudioFocusManager.PlayerCommand
} int playerCommand =
audioFocusManager.setAudioAttributes(
handleAudioFocus ? audioAttributes : null, getPlayWhenReady(), getPlaybackState());
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
} }
@Override @Override
...@@ -452,11 +457,7 @@ public class SimpleExoPlayer ...@@ -452,11 +457,7 @@ public class SimpleExoPlayer
return; return;
} }
this.audioVolume = audioVolume; this.audioVolume = audioVolume;
for (Renderer renderer : renderers) { sendVolumeToRenderers();
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setPayload(audioVolume).send();
}
}
for (AudioListener audioListener : audioListeners) { for (AudioListener audioListener : audioListeners) {
audioListener.onVolumeChanged(audioVolume); audioListener.onVolumeChanged(audioVolume);
} }
...@@ -792,12 +793,17 @@ public class SimpleExoPlayer ...@@ -792,12 +793,17 @@ public class SimpleExoPlayer
mediaSource.addEventListener(eventHandler, analyticsCollector); mediaSource.addEventListener(eventHandler, analyticsCollector);
this.mediaSource = mediaSource; this.mediaSource = mediaSource;
} }
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.handlePrepare(getPlayWhenReady());
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
player.prepare(mediaSource, resetPosition, resetState); player.prepare(mediaSource, resetPosition, resetState);
} }
@Override @Override
public void setPlayWhenReady(boolean playWhenReady) { public void setPlayWhenReady(boolean playWhenReady) {
player.setPlayWhenReady(playWhenReady); @AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.handleSetPlayWhenReady(playWhenReady, getPlaybackState());
updatePlayWhenReady(playWhenReady, playerCommand);
} }
@Override @Override
...@@ -892,11 +898,13 @@ public class SimpleExoPlayer ...@@ -892,11 +898,13 @@ public class SimpleExoPlayer
mediaSource = null; mediaSource = null;
analyticsCollector.resetForNewMediaSource(); analyticsCollector.resetForNewMediaSource();
} }
audioFocusManager.handleStop();
currentCues = Collections.emptyList(); currentCues = Collections.emptyList();
} }
@Override @Override
public void release() { public void release() {
audioFocusManager.handleStop();
player.release(); player.release();
removeSurfaceCallbacks(); removeSurfaceCallbacks();
if (surface != null) { if (surface != null) {
...@@ -1039,28 +1047,6 @@ public class SimpleExoPlayer ...@@ -1039,28 +1047,6 @@ public class SimpleExoPlayer
// Internal methods. // Internal methods.
/**
* Creates the {@link ExoPlayer} implementation used by this instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param bandwidthMeter The {@link BandwidthMeter} that will be used by the instance.
* @param clock The {@link Clock} that will be used by this instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
* @return A new {@link ExoPlayer} instance.
*/
protected ExoPlayer createExoPlayerImpl(
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
BandwidthMeter bandwidthMeter,
Clock clock,
Looper looper) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, bandwidthMeter, clock, looper);
}
private void removeSurfaceCallbacks() { private void removeSurfaceCallbacks() {
if (textureView != null) { if (textureView != null) {
if (textureView.getSurfaceTextureListener() != componentListener) { if (textureView.getSurfaceTextureListener() != componentListener) {
...@@ -1114,9 +1100,31 @@ public class SimpleExoPlayer ...@@ -1114,9 +1100,31 @@ public class SimpleExoPlayer
} }
} }
private final class ComponentListener implements VideoRendererEventListener, private void sendVolumeToRenderers() {
AudioRendererEventListener, TextOutput, MetadataOutput, SurfaceHolder.Callback, float scaledVolume = audioVolume * audioFocusManager.getVolumeMultiplier();
TextureView.SurfaceTextureListener { for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setPayload(scaledVolume).send();
}
}
}
private void updatePlayWhenReady(
boolean playWhenReady, @AudioFocusManager.PlayerCommand int playerCommand) {
player.setPlayWhenReady(
playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY,
playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY);
}
private final class ComponentListener
implements VideoRendererEventListener,
AudioRendererEventListener,
TextOutput,
MetadataOutput,
SurfaceHolder.Callback,
TextureView.SurfaceTextureListener,
AudioFocusManager.PlayerControl {
// VideoRendererEventListener implementation // VideoRendererEventListener implementation
...@@ -1314,6 +1322,17 @@ public class SimpleExoPlayer ...@@ -1314,6 +1322,17 @@ public class SimpleExoPlayer
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
// Do nothing. // Do nothing.
} }
}
// AudioFocusManager.PlayerControl implementation
@Override
public void setVolumeMultiplier(float volumeMultiplier) {
sendVolumeToRenderers();
}
@Override
public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) {
updatePlayWhenReady(getPlayWhenReady(), playerCommand);
}
}
} }
...@@ -102,7 +102,7 @@ public final class AudioAttributes { ...@@ -102,7 +102,7 @@ public final class AudioAttributes {
} }
@TargetApi(21) @TargetApi(21)
/* package */ android.media.AudioAttributes getAudioAttributesV21() { public android.media.AudioAttributes getAudioAttributesV21() {
if (audioAttributesV21 == null) { if (audioAttributesV21 == null) {
audioAttributesV21 = new android.media.AudioAttributes.Builder() audioAttributesV21 = new android.media.AudioAttributes.Builder()
.setContentType(contentType) .setContentType(contentType)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment