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 f799df2c5d..173078dad7 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 @@ -239,6 +239,18 @@ public class SimpleExoPlayer implements ExoPlayer { setVideoSurfaceInternal(surface, false); } + /** + * Clears the {@link Surface} onto which video is being rendered if it matches the one passed. + * Else does nothing. + * + * @param surface The surface to clear. + */ + public void clearVideoSurface(Surface surface) { + if (surface != null && surface == this.surface) { + setVideoSurface(null); + } + } + /** * Sets the {@link SurfaceHolder} that holds the {@link Surface} onto which video will be * rendered. The player will track the lifecycle of the surface automatically. @@ -256,6 +268,18 @@ public class SimpleExoPlayer implements ExoPlayer { } } + /** + * Clears the {@link SurfaceHolder} that holds the {@link Surface} onto which video is being + * rendered if it matches the one passed. Else does nothing. + * + * @param surfaceHolder The surface holder to clear. + */ + public void clearVideoSurfaceHolder(SurfaceHolder surfaceHolder) { + if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { + setVideoSurfaceHolder(null); + } + } + /** * Sets the {@link SurfaceView} onto which video will be rendered. The player will track the * lifecycle of the surface automatically. @@ -263,7 +287,17 @@ public class SimpleExoPlayer implements ExoPlayer { * @param surfaceView The surface view. */ public void setVideoSurfaceView(SurfaceView surfaceView) { - setVideoSurfaceHolder(surfaceView.getHolder()); + setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); + } + + /** + * Clears the {@link SurfaceView} onto which video is being rendered if it matches the one passed. + * Else does nothing. + * + * @param surfaceView The texture view to clear. + */ + public void clearVideoSurfaceView(SurfaceView surfaceView) { + clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); } /** @@ -287,6 +321,18 @@ public class SimpleExoPlayer implements ExoPlayer { } } + /** + * Clears the {@link TextureView} onto which video is being rendered if it matches the one passed. + * Else does nothing. + * + * @param textureView The texture view to clear. + */ + public void clearVideoTextureView(TextureView textureView) { + if (textureView != null && textureView == this.textureView) { + setVideoTextureView(null); + } + } + /** * 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 @@ -404,6 +450,57 @@ public class SimpleExoPlayer implements ExoPlayer { videoListener = listener; } + /** + * Clears the listener receiving video events if it matches the one passed. Else does nothing. + * + * @param listener The listener to clear. + */ + public void clearVideoListener(VideoListener listener) { + if (videoListener == listener) { + videoListener = null; + } + } + + /** + * Sets an output to receive text events. + * + * @param output The output. + */ + public void setTextOutput(TextRenderer.Output output) { + textOutput = output; + } + + /** + * Clears the output receiving text events if it matches the one passed. Else does nothing. + * + * @param output The output to clear. + */ + public void clearTextOutput(TextRenderer.Output output) { + if (textOutput == output) { + textOutput = null; + } + } + + /** + * Sets a listener to receive metadata events. + * + * @param output The output. + */ + public void setMetadataOutput(MetadataRenderer.Output output) { + metadataOutput = output; + } + + /** + * Clears the output receiving metadata events if it matches the one passed. Else does nothing. + * + * @param output The output to clear. + */ + public void clearMetadataOutput(MetadataRenderer.Output output) { + if (metadataOutput == output) { + metadataOutput = null; + } + } + /** * Sets a listener to receive debug events from the video renderer. * @@ -422,24 +519,6 @@ public class SimpleExoPlayer implements ExoPlayer { audioDebugListener = listener; } - /** - * Sets an output to receive text events. - * - * @param output The output. - */ - public void setTextOutput(TextRenderer.Output output) { - textOutput = output; - } - - /** - * Sets a listener to receive metadata events. - * - * @param output The output. - */ - public void setMetadataOutput(MetadataRenderer.Output output) { - metadataOutput = output; - } - // ExoPlayer implementation @Override diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index bb47226c42..fce05f5bc4 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -21,6 +21,8 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -319,6 +321,30 @@ public final class SimpleExoPlayerView extends FrameLayout { hideController(); } + /** + * Switches the view targeted by a given {@link SimpleExoPlayer}. + * + * @param player The player whose target view is being switched. + * @param oldPlayerView The old view to detach from the player. + * @param newPlayerView The new view to attach to the player. + */ + public static void switchTargetView(@NonNull SimpleExoPlayer player, + @Nullable SimpleExoPlayerView oldPlayerView, @Nullable SimpleExoPlayerView newPlayerView) { + if (oldPlayerView == newPlayerView) { + return; + } + // We attach the new view before detaching the old one because this ordering allows the player + // to swap directly from one surface to another, without transitioning through a state where no + // surface is attached. This is significantly more efficient and achieves a more seamless + // transition when using platform provided video decoders. + if (newPlayerView != null) { + newPlayerView.setPlayer(player); + } + if (oldPlayerView != null) { + oldPlayerView.setPlayer(null); + } + } + /** * Returns the player currently set on this view, or null if no player is set. */ @@ -330,6 +356,12 @@ public final class SimpleExoPlayerView extends FrameLayout { * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous * assignments are overridden. + *
+ * To transition a {@link SimpleExoPlayer} from targeting one view to another, it's recommended to + * use {@link #switchTargetView(SimpleExoPlayer, SimpleExoPlayerView, SimpleExoPlayerView)} rather + * than this method. If you do wish to use this method directly, be sure to attach the player to + * the new view before calling {@code setPlayer(null)} to detach it from the old one. + * This ordering is significantly more efficient and may allow for more seamless transitions. * * @param player The {@link SimpleExoPlayer} to use. */ @@ -338,10 +370,14 @@ public final class SimpleExoPlayerView extends FrameLayout { return; } if (this.player != null) { - this.player.setTextOutput(null); - this.player.setVideoListener(null); this.player.removeListener(componentListener); - this.player.setVideoSurface(null); + this.player.clearTextOutput(componentListener); + this.player.clearVideoListener(componentListener); + if (surfaceView instanceof TextureView) { + this.player.clearVideoTextureView((TextureView) surfaceView); + } else if (surfaceView instanceof SurfaceView) { + this.player.clearVideoSurfaceView((SurfaceView) surfaceView); + } } this.player = player; if (useController) { @@ -357,8 +393,8 @@ public final class SimpleExoPlayerView extends FrameLayout { player.setVideoSurfaceView((SurfaceView) surfaceView); } player.setVideoListener(componentListener); - player.addListener(componentListener); player.setTextOutput(componentListener); + player.addListener(componentListener); maybeShowController(false); updateForCurrentTrackSelections(); } else {