diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 18d4553af2..39f1655ab5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -109,6 +109,7 @@ public class SimpleExoPlayer private List currentCues; private VideoFrameMetadataListener videoFrameMetadataListener; private CameraMotionListener cameraMotionListener; + private boolean hasNotifiedFullWrongThreadWarning; /** * @param context A {@link Context}. @@ -266,6 +267,7 @@ public class SimpleExoPlayer */ @Override public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { + verifyApplicationThread(); this.videoScalingMode = videoScalingMode; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { @@ -285,11 +287,13 @@ public class SimpleExoPlayer @Override public void clearVideoSurface() { + verifyApplicationThread(); setVideoSurface(null); } @Override public void clearVideoSurface(Surface surface) { + verifyApplicationThread(); if (surface != null && surface == this.surface) { setVideoSurface(null); } @@ -297,6 +301,7 @@ public class SimpleExoPlayer @Override public void setVideoSurface(@Nullable Surface surface) { + verifyApplicationThread(); removeSurfaceCallbacks(); setVideoSurfaceInternal(surface, false); int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; @@ -305,6 +310,7 @@ public class SimpleExoPlayer @Override public void setVideoSurfaceHolder(SurfaceHolder surfaceHolder) { + verifyApplicationThread(); removeSurfaceCallbacks(); this.surfaceHolder = surfaceHolder; if (surfaceHolder == null) { @@ -326,6 +332,7 @@ public class SimpleExoPlayer @Override public void clearVideoSurfaceHolder(SurfaceHolder surfaceHolder) { + verifyApplicationThread(); if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { setVideoSurfaceHolder(null); } @@ -343,6 +350,7 @@ public class SimpleExoPlayer @Override public void setVideoTextureView(TextureView textureView) { + verifyApplicationThread(); removeSurfaceCallbacks(); this.textureView = textureView; if (textureView == null) { @@ -367,6 +375,7 @@ public class SimpleExoPlayer @Override public void clearVideoTextureView(TextureView textureView) { + verifyApplicationThread(); if (textureView != null && textureView == this.textureView) { setVideoTextureView(null); } @@ -389,6 +398,7 @@ public class SimpleExoPlayer @Override public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { + verifyApplicationThread(); if (!Util.areEqual(this.audioAttributes, audioAttributes)) { this.audioAttributes = audioAttributes; for (Renderer renderer : renderers) { @@ -424,6 +434,7 @@ public class SimpleExoPlayer @Override public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { + verifyApplicationThread(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { player @@ -442,6 +453,7 @@ public class SimpleExoPlayer @Override public void setVolume(float audioVolume) { + verifyApplicationThread(); audioVolume = Util.constrainValue(audioVolume, /* min= */ 0, /* max= */ 1); if (this.audioVolume == audioVolume) { return; @@ -500,6 +512,7 @@ public class SimpleExoPlayer * @param listener The listener to be added. */ public void addAnalyticsListener(AnalyticsListener listener) { + verifyApplicationThread(); analyticsCollector.addListener(listener); } @@ -509,6 +522,7 @@ public class SimpleExoPlayer * @param listener The listener to be removed. */ public void removeAnalyticsListener(AnalyticsListener listener) { + verifyApplicationThread(); analyticsCollector.removeListener(listener); } @@ -571,6 +585,7 @@ public class SimpleExoPlayer @Override public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + verifyApplicationThread(); videoFrameMetadataListener = listener; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { @@ -585,6 +600,7 @@ public class SimpleExoPlayer @Override public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + verifyApplicationThread(); if (videoFrameMetadataListener != listener) { return; } @@ -601,6 +617,7 @@ public class SimpleExoPlayer @Override public void setCameraMotionListener(CameraMotionListener listener) { + verifyApplicationThread(); cameraMotionListener = listener; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_CAMERA_MOTION) { @@ -615,6 +632,7 @@ public class SimpleExoPlayer @Override public void clearCameraMotionListener(CameraMotionListener listener) { + verifyApplicationThread(); if (cameraMotionListener != listener) { return; } @@ -814,26 +832,31 @@ public class SimpleExoPlayer @Override public void addListener(Player.EventListener listener) { + verifyApplicationThread(); player.addListener(listener); } @Override public void removeListener(Player.EventListener listener) { + verifyApplicationThread(); player.removeListener(listener); } @Override public int getPlaybackState() { + verifyApplicationThread(); return player.getPlaybackState(); } @Override public @Nullable ExoPlaybackException getPlaybackError() { + verifyApplicationThread(); return player.getPlaybackError(); } @Override public void retry() { + verifyApplicationThread(); if (mediaSource != null && (getPlaybackError() != null || getPlaybackState() == Player.STATE_IDLE)) { prepare(mediaSource, /* resetPosition= */ false, /* resetState= */ false); @@ -847,6 +870,7 @@ public class SimpleExoPlayer @Override public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + verifyApplicationThread(); if (this.mediaSource != null) { this.mediaSource.removeEventListener(analyticsCollector); analyticsCollector.resetForNewMediaSource(); @@ -861,6 +885,7 @@ public class SimpleExoPlayer @Override public void setPlayWhenReady(boolean playWhenReady) { + verifyApplicationThread(); @AudioFocusManager.PlayerCommand int playerCommand = audioFocusManager.handleSetPlayWhenReady(playWhenReady, getPlaybackState()); updatePlayWhenReady(playWhenReady, playerCommand); @@ -868,80 +893,95 @@ public class SimpleExoPlayer @Override public boolean getPlayWhenReady() { + verifyApplicationThread(); return player.getPlayWhenReady(); } @Override public @RepeatMode int getRepeatMode() { + verifyApplicationThread(); return player.getRepeatMode(); } @Override public void setRepeatMode(@RepeatMode int repeatMode) { + verifyApplicationThread(); player.setRepeatMode(repeatMode); } @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { + verifyApplicationThread(); player.setShuffleModeEnabled(shuffleModeEnabled); } @Override public boolean getShuffleModeEnabled() { + verifyApplicationThread(); return player.getShuffleModeEnabled(); } @Override public boolean isLoading() { + verifyApplicationThread(); return player.isLoading(); } @Override public void seekToDefaultPosition() { + verifyApplicationThread(); analyticsCollector.notifySeekStarted(); player.seekToDefaultPosition(); } @Override public void seekToDefaultPosition(int windowIndex) { + verifyApplicationThread(); analyticsCollector.notifySeekStarted(); player.seekToDefaultPosition(windowIndex); } @Override public void seekTo(long positionMs) { + verifyApplicationThread(); analyticsCollector.notifySeekStarted(); player.seekTo(positionMs); } @Override public void seekTo(int windowIndex, long positionMs) { + verifyApplicationThread(); analyticsCollector.notifySeekStarted(); player.seekTo(windowIndex, positionMs); } @Override public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) { + verifyApplicationThread(); player.setPlaybackParameters(playbackParameters); } @Override public PlaybackParameters getPlaybackParameters() { + verifyApplicationThread(); return player.getPlaybackParameters(); } @Override public void setSeekParameters(@Nullable SeekParameters seekParameters) { + verifyApplicationThread(); player.setSeekParameters(seekParameters); } @Override public SeekParameters getSeekParameters() { + verifyApplicationThread(); return player.getSeekParameters(); } @Override public @Nullable Object getCurrentTag() { + verifyApplicationThread(); return player.getCurrentTag(); } @@ -952,6 +992,7 @@ public class SimpleExoPlayer @Override public void stop(boolean reset) { + verifyApplicationThread(); player.stop(reset); if (mediaSource != null) { mediaSource.removeEventListener(analyticsCollector); @@ -992,6 +1033,7 @@ public class SimpleExoPlayer @Override public PlayerMessage createMessage(PlayerMessage.Target target) { + verifyApplicationThread(); return player.createMessage(target); } @@ -1004,116 +1046,139 @@ public class SimpleExoPlayer @Override public int getRendererCount() { + verifyApplicationThread(); return player.getRendererCount(); } @Override public int getRendererType(int index) { + verifyApplicationThread(); return player.getRendererType(index); } @Override public TrackGroupArray getCurrentTrackGroups() { + verifyApplicationThread(); return player.getCurrentTrackGroups(); } @Override public TrackSelectionArray getCurrentTrackSelections() { + verifyApplicationThread(); return player.getCurrentTrackSelections(); } @Override public Timeline getCurrentTimeline() { + verifyApplicationThread(); return player.getCurrentTimeline(); } @Override public @Nullable Object getCurrentManifest() { + verifyApplicationThread(); return player.getCurrentManifest(); } @Override public int getCurrentPeriodIndex() { + verifyApplicationThread(); return player.getCurrentPeriodIndex(); } @Override public int getCurrentWindowIndex() { + verifyApplicationThread(); return player.getCurrentWindowIndex(); } @Override public int getNextWindowIndex() { + verifyApplicationThread(); return player.getNextWindowIndex(); } @Override public int getPreviousWindowIndex() { + verifyApplicationThread(); return player.getPreviousWindowIndex(); } @Override public long getDuration() { + verifyApplicationThread(); return player.getDuration(); } @Override public long getCurrentPosition() { + verifyApplicationThread(); return player.getCurrentPosition(); } @Override public long getBufferedPosition() { + verifyApplicationThread(); return player.getBufferedPosition(); } @Override public int getBufferedPercentage() { + verifyApplicationThread(); return player.getBufferedPercentage(); } @Override public long getTotalBufferedDuration() { + verifyApplicationThread(); return player.getTotalBufferedDuration(); } @Override public boolean isCurrentWindowDynamic() { + verifyApplicationThread(); return player.isCurrentWindowDynamic(); } @Override public boolean isCurrentWindowSeekable() { + verifyApplicationThread(); return player.isCurrentWindowSeekable(); } @Override public boolean isPlayingAd() { + verifyApplicationThread(); return player.isPlayingAd(); } @Override public int getCurrentAdGroupIndex() { + verifyApplicationThread(); return player.getCurrentAdGroupIndex(); } @Override public int getCurrentAdIndexInAdGroup() { + verifyApplicationThread(); return player.getCurrentAdIndexInAdGroup(); } @Override public long getContentDuration() { + verifyApplicationThread(); return player.getContentDuration(); } @Override public long getContentPosition() { + verifyApplicationThread(); return player.getContentPosition(); } @Override public long getContentBufferedPosition() { + verifyApplicationThread(); return player.getContentBufferedPosition(); } @@ -1183,12 +1248,23 @@ public class SimpleExoPlayer 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 void verifyApplicationThread() { + if (Looper.myLooper() != getApplicationLooper()) { + Log.w( + TAG, + "Player is accessed on the wrong thread. See " + + "https://google.github.io/ExoPlayer/faqs.html#" + + "what-do-player-is-accessed-on-the-wrong-thread-warnings-mean", + hasNotifiedFullWrongThreadWarning ? null : new IllegalStateException()); + hasNotifiedFullWrongThreadWarning = true; + } + } + private final class ComponentListener implements VideoRendererEventListener, AudioRendererEventListener,