From cdebf6c68b0f76360d78114dcb47c58e63b354a7 Mon Sep 17 00:00:00 2001 From: krocard Date: Fri, 16 Apr 2021 17:37:28 +0100 Subject: [PATCH] Move VideoComponent in ExoPlayer VideoFrameMetadataListener and CameraMotionListener are still part of the Player interface as a good way to break the UI dependency on them has not yet been finalised. PiperOrigin-RevId: 368863829 --- RELEASENOTES.md | 5 + .../gldemo/VideoProcessingGLSurfaceView.java | 6 +- .../exoplayer2/ext/cast/CastPlayer.java | 63 ++++- .../ext/leanback/LeanbackPlayerAdapter.java | 27 +- .../com/google/android/exoplayer2/Player.java | 255 ++++++++---------- .../google/android/exoplayer2/ExoPlayer.java | 151 +++++++++++ .../android/exoplayer2/ExoPlayerImpl.java | 58 ++++ .../android/exoplayer2/SimpleExoPlayer.java | 2 +- .../android/exoplayer2/ui/PlayerView.java | 20 +- .../exoplayer2/ui/StyledPlayerView.java | 26 +- .../ui/spherical/SphericalGLSurfaceView.java | 34 +-- .../exoplayer2/testutil/StubExoPlayer.java | 71 +++++ 12 files changed, 501 insertions(+), 217 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index afdc7f52c5..cde47c05f0 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -3,6 +3,11 @@ ### dev-v2 (not yet released) * Core Library: + * Move `Player` components to `ExoPlayer`. For example + `Player.VideoComponent` is now `ExoPlayer.VideoComponent`. + * The most used methods of `Player`'s audio/video/text/metadata components + have been added to `Player`. Support can be queried using + `Player.isCommandAvailable` instead of testing for component nullness. * Add position info of the old and the new position as arguments to `EventListener.onPositionDiscontinuity`. Add the new reasons `DISCONTINUITY_REASON_SKIP` and `DISCONTINUITY_REASON_REMOVE` and rename diff --git a/demos/gl/src/main/java/com/google/android/exoplayer2/gldemo/VideoProcessingGLSurfaceView.java b/demos/gl/src/main/java/com/google/android/exoplayer2/gldemo/VideoProcessingGLSurfaceView.java index 7aee74801f..d1202052fe 100644 --- a/demos/gl/src/main/java/com/google/android/exoplayer2/gldemo/VideoProcessingGLSurfaceView.java +++ b/demos/gl/src/main/java/com/google/android/exoplayer2/gldemo/VideoProcessingGLSurfaceView.java @@ -25,8 +25,8 @@ import android.os.Handler; import android.view.Surface; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.TimedValueQueue; @@ -72,7 +72,7 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView { @Nullable private SurfaceTexture surfaceTexture; @Nullable private Surface surface; - @Nullable private Player.VideoComponent videoComponent; + @Nullable private ExoPlayer.VideoComponent videoComponent; /** * Creates a new instance. Pass {@code true} for {@code requireSecureContext} if the {@link @@ -151,7 +151,7 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView { * * @param newVideoComponent The new video component, or {@code null} to detach this view. */ - public void setVideoComponent(@Nullable Player.VideoComponent newVideoComponent) { + public void setVideoComponent(@Nullable ExoPlayer.VideoComponent newVideoComponent) { if (newVideoComponent == videoComponent) { return; } diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 75fd2a7d66..8dccc9e8b2 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -19,6 +19,10 @@ import static com.google.android.exoplayer2.util.Util.castNonNull; import static java.lang.Math.min; import android.os.Looper; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.BasePlayer; @@ -44,6 +48,8 @@ import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.video.VideoFrameMetadataListener; +import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import com.google.android.gms.cast.CastStatusCodes; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaQueueItem; @@ -276,12 +282,6 @@ public final class CastPlayer extends BasePlayer { // Player implementation. - @Override - @Nullable - public VideoComponent getVideoComponent() { - return null; - } - @Override public Looper getApplicationLooper() { return Looper.getMainLooper(); @@ -653,6 +653,57 @@ public final class CastPlayer extends BasePlayer { return 1; } + /** This method is not supported and does nothing. */ + @Override + public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void setCameraMotionListener(CameraMotionListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearCameraMotionListener(CameraMotionListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurface() {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurface(@Nullable Surface surface) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurface(@Nullable Surface surface) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoTextureView(@Nullable TextureView textureView) {} + /** This method is not supported and does nothing. */ + @Override + public void clearVideoTextureView(@Nullable TextureView textureView) {} + /** This method is not supported and returns an empty list. */ @Override public ImmutableList getCurrentCues() { diff --git a/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java b/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java index 8a9d8a80d4..f2fc3279b8 100644 --- a/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java +++ b/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java @@ -37,7 +37,6 @@ import com.google.android.exoplayer2.Player.TimelineChangeReason; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.util.ErrorMessageProvider; import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.VideoListener; /** Leanback {@code PlayerAdapter} implementation for {@link Player}. */ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnable { @@ -49,7 +48,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab private final Context context; private final Player player; private final Handler handler; - private final ComponentListener componentListener; + private final PlayerListener playerListener; private final int updatePeriodMs; @Nullable private PlaybackPreparer playbackPreparer; @@ -73,7 +72,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab this.player = player; this.updatePeriodMs = updatePeriodMs; handler = Util.createHandlerForCurrentOrMainLooper(); - componentListener = new ComponentListener(); + playerListener = new PlayerListener(); controlDispatcher = new DefaultControlDispatcher(); } @@ -118,23 +117,15 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab public void onAttachedToHost(PlaybackGlueHost host) { if (host instanceof SurfaceHolderGlueHost) { surfaceHolderGlueHost = ((SurfaceHolderGlueHost) host); - surfaceHolderGlueHost.setSurfaceHolderCallback(componentListener); + surfaceHolderGlueHost.setSurfaceHolderCallback(playerListener); } notifyStateChanged(); - player.addListener(componentListener); - Player.VideoComponent videoComponent = player.getVideoComponent(); - if (videoComponent != null) { - videoComponent.addVideoListener(componentListener); - } + player.addListener(playerListener); } @Override public void onDetachedFromHost() { - player.removeListener(componentListener); - Player.VideoComponent videoComponent = player.getVideoComponent(); - if (videoComponent != null) { - videoComponent.removeVideoListener(componentListener); - } + player.removeListener(playerListener); if (surfaceHolderGlueHost != null) { removeSurfaceHolderCallback(surfaceHolderGlueHost); surfaceHolderGlueHost = null; @@ -227,10 +218,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab /* package */ void setVideoSurface(@Nullable Surface surface) { hasSurface = surface != null; - Player.VideoComponent videoComponent = player.getVideoComponent(); - if (videoComponent != null) { - videoComponent.setVideoSurface(surface); - } + player.setVideoSurface(surface); maybeNotifyPreparedStateChanged(getCallback()); } @@ -258,8 +246,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab surfaceHolderGlueHost.setSurfaceHolderCallback(null); } - private final class ComponentListener - implements Player.EventListener, SurfaceHolder.Callback, VideoListener { + private final class PlayerListener implements Player.Listener, SurfaceHolder.Callback { // SurfaceHolder.Callback implementation. diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index 9a721a9545..76b1b02f03 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -67,146 +67,6 @@ import java.util.List; */ public interface Player { - /** The video component of a {@link Player}. */ - interface VideoComponent { - - /** - * Sets the {@link C.VideoScalingMode}. - * - * @param videoScalingMode The {@link C.VideoScalingMode}. - */ - void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode); - - /** Returns the {@link C.VideoScalingMode}. */ - @C.VideoScalingMode - int getVideoScalingMode(); - - /** - * Adds a listener to receive video events. - * - * @param listener The listener to register. - */ - void addVideoListener(VideoListener listener); - - /** - * Removes a listener of video events. - * - * @param listener The listener to unregister. - */ - void removeVideoListener(VideoListener listener); - - /** - * Sets a listener to receive video frame metadata events. - * - *

This method is intended to be called by the same component that sets the {@link Surface} - * onto which video will be rendered. If using ExoPlayer's standard UI components, this method - * should not be called directly from application code. - * - * @param listener The listener. - */ - void setVideoFrameMetadataListener(VideoFrameMetadataListener listener); - - /** - * Clears the listener which receives video frame metadata events if it matches the one passed. - * Else does nothing. - * - * @param listener The listener to clear. - */ - void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener); - - /** - * Sets a listener of camera motion events. - * - * @param listener The listener. - */ - void setCameraMotionListener(CameraMotionListener listener); - - /** - * Clears the listener which receives camera motion events if it matches the one passed. Else - * does nothing. - * - * @param listener The listener to clear. - */ - void clearCameraMotionListener(CameraMotionListener listener); - - /** - * Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView} - * currently set on the player. - */ - void clearVideoSurface(); - - /** - * 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. - */ - void clearVideoSurface(@Nullable Surface surface); - - /** - * Sets the {@link Surface} onto which video will be rendered. The caller is responsible for - * tracking the lifecycle of the surface, and must clear the surface by calling {@code - * setVideoSurface(null)} if the surface is destroyed. - * - *

If the surface is held by a {@link SurfaceView}, {@link TextureView} or {@link - * SurfaceHolder} then it's recommended to use {@link #setVideoSurfaceView(SurfaceView)}, {@link - * #setVideoTextureView(TextureView)} or {@link #setVideoSurfaceHolder(SurfaceHolder)} rather - * than this method, since passing the holder allows the player to track the lifecycle of the - * surface automatically. - * - * @param surface The {@link Surface}. - */ - void setVideoSurface(@Nullable Surface surface); - - /** - * 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. - * - * @param surfaceHolder The surface holder. - */ - void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); - - /** - * 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. - */ - void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); - - /** - * Sets the {@link SurfaceView} onto which video will be rendered. The player will track the - * lifecycle of the surface automatically. - * - * @param surfaceView The surface view. - */ - void setVideoSurfaceView(@Nullable SurfaceView surfaceView); - - /** - * 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. - */ - void clearVideoSurfaceView(@Nullable SurfaceView surfaceView); - - /** - * Sets the {@link TextureView} onto which video will be rendered. The player will track the - * lifecycle of the surface automatically. - * - * @param textureView The texture view. - */ - void setVideoTextureView(@Nullable TextureView textureView); - - /** - * 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. - */ - void clearVideoTextureView(@Nullable TextureView textureView); - } - /** * Listener of changes in player state. * @@ -1152,10 +1012,6 @@ public interface Player { /** Command to get the text that should currently be displayed by the player. */ int COMMAND_GET_TEXT = 22; - /** Returns the component of this player for video output, or null if video is not supported. */ - @Nullable - VideoComponent getVideoComponent(); - /** * Returns the {@link Looper} associated with the application thread that's used to access the * player and on which player events are received. @@ -1830,6 +1686,117 @@ public interface Player { */ float getVolume(); + /** + * Sets a listener to receive video frame metadata events. + * + *

This method is intended to be called by the same component that sets the {@link Surface} + * onto which video will be rendered. If using ExoPlayer's standard UI components, this method + * should not be called directly from application code. + * + * @param listener The listener. + */ + void setVideoFrameMetadataListener(VideoFrameMetadataListener listener); + + /** + * Clears the listener which receives video frame metadata events if it matches the one passed. + * Else does nothing. + * + * @param listener The listener to clear. + */ + void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener); + + /** + * Sets a listener of camera motion events. + * + * @param listener The listener. + */ + void setCameraMotionListener(CameraMotionListener listener); + + /** + * Clears the listener which receives camera motion events if it matches the one passed. Else does + * nothing. + * + * @param listener The listener to clear. + */ + void clearCameraMotionListener(CameraMotionListener listener); + + /** + * Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView} + * currently set on the player. + */ + void clearVideoSurface(); + + /** + * 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. + */ + void clearVideoSurface(@Nullable Surface surface); + + /** + * Sets the {@link Surface} onto which video will be rendered. The caller is responsible for + * tracking the lifecycle of the surface, and must clear the surface by calling {@code + * setVideoSurface(null)} if the surface is destroyed. + * + *

If the surface is held by a {@link SurfaceView}, {@link TextureView} or {@link + * SurfaceHolder} then it's recommended to use {@link #setVideoSurfaceView(SurfaceView)}, {@link + * #setVideoTextureView(TextureView)} or {@link #setVideoSurfaceHolder(SurfaceHolder)} rather than + * this method, since passing the holder allows the player to track the lifecycle of the surface + * automatically. + * + * @param surface The {@link Surface}. + */ + void setVideoSurface(@Nullable Surface surface); + + /** + * 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. + * + * @param surfaceHolder The surface holder. + */ + void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); + + /** + * 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. + */ + void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); + + /** + * Sets the {@link SurfaceView} onto which video will be rendered. The player will track the + * lifecycle of the surface automatically. + * + * @param surfaceView The surface view. + */ + void setVideoSurfaceView(@Nullable SurfaceView surfaceView); + + /** + * 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. + */ + void clearVideoSurfaceView(@Nullable SurfaceView surfaceView); + + /** + * Sets the {@link TextureView} onto which video will be rendered. The player will track the + * lifecycle of the surface automatically. + * + * @param textureView The texture view. + */ + void setVideoTextureView(@Nullable TextureView textureView); + + /** + * 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. + */ + void clearVideoTextureView(@Nullable TextureView textureView); + /** Returns the current {@link Cue Cues}. This list may be empty. */ List getCurrentCues(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 06d4bd165d..11bef0a035 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -18,6 +18,10 @@ package com.google.android.exoplayer2; import android.content.Context; import android.media.AudioTrack; import android.os.Looper; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.analytics.AnalyticsCollector; @@ -48,6 +52,9 @@ import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; +import com.google.android.exoplayer2.video.VideoFrameMetadataListener; +import com.google.android.exoplayer2.video.VideoListener; +import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import java.util.List; /** @@ -223,6 +230,146 @@ public interface ExoPlayer extends Player { boolean getSkipSilenceEnabled(); } + /** The video component of an {@link ExoPlayer}. */ + interface VideoComponent { + + /** + * Sets the {@link C.VideoScalingMode}. + * + * @param videoScalingMode The {@link C.VideoScalingMode}. + */ + void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode); + + /** Returns the {@link C.VideoScalingMode}. */ + @C.VideoScalingMode + int getVideoScalingMode(); + + /** + * Adds a listener to receive video events. + * + * @param listener The listener to register. + */ + void addVideoListener(VideoListener listener); + + /** + * Removes a listener of video events. + * + * @param listener The listener to unregister. + */ + void removeVideoListener(VideoListener listener); + + /** + * Sets a listener to receive video frame metadata events. + * + *

This method is intended to be called by the same component that sets the {@link Surface} + * onto which video will be rendered. If using ExoPlayer's standard UI components, this method + * should not be called directly from application code. + * + * @param listener The listener. + */ + void setVideoFrameMetadataListener(VideoFrameMetadataListener listener); + + /** + * Clears the listener which receives video frame metadata events if it matches the one passed. + * Else does nothing. + * + * @param listener The listener to clear. + */ + void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener); + + /** + * Sets a listener of camera motion events. + * + * @param listener The listener. + */ + void setCameraMotionListener(CameraMotionListener listener); + + /** + * Clears the listener which receives camera motion events if it matches the one passed. Else + * does nothing. + * + * @param listener The listener to clear. + */ + void clearCameraMotionListener(CameraMotionListener listener); + + /** + * Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView} + * currently set on the player. + */ + void clearVideoSurface(); + + /** + * 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. + */ + void clearVideoSurface(@Nullable Surface surface); + + /** + * Sets the {@link Surface} onto which video will be rendered. The caller is responsible for + * tracking the lifecycle of the surface, and must clear the surface by calling {@code + * setVideoSurface(null)} if the surface is destroyed. + * + *

If the surface is held by a {@link SurfaceView}, {@link TextureView} or {@link + * SurfaceHolder} then it's recommended to use {@link #setVideoSurfaceView(SurfaceView)}, {@link + * #setVideoTextureView(TextureView)} or {@link #setVideoSurfaceHolder(SurfaceHolder)} rather + * than this method, since passing the holder allows the player to track the lifecycle of the + * surface automatically. + * + * @param surface The {@link Surface}. + */ + void setVideoSurface(@Nullable Surface surface); + + /** + * 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. + * + * @param surfaceHolder The surface holder. + */ + void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); + + /** + * 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. + */ + void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); + + /** + * Sets the {@link SurfaceView} onto which video will be rendered. The player will track the + * lifecycle of the surface automatically. + * + * @param surfaceView The surface view. + */ + void setVideoSurfaceView(@Nullable SurfaceView surfaceView); + + /** + * 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. + */ + void clearVideoSurfaceView(@Nullable SurfaceView surfaceView); + + /** + * Sets the {@link TextureView} onto which video will be rendered. The player will track the + * lifecycle of the surface automatically. + * + * @param textureView The texture view. + */ + void setVideoTextureView(@Nullable TextureView textureView); + + /** + * 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. + */ + void clearVideoTextureView(@Nullable TextureView textureView); + } + /** The text component of an {@link ExoPlayer}. */ interface TextComponent { @@ -651,6 +798,10 @@ public interface ExoPlayer extends Player { @Nullable AudioComponent getAudioComponent(); + /** Returns the component of this player for video output, or null if video is not supported. */ + @Nullable + VideoComponent getVideoComponent(); + /** Returns the component of this player for text output, or null if text is not supported. */ @Nullable TextComponent getTextComponent(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index eb9b538293..dcd91f3323 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -25,6 +25,10 @@ import android.annotation.SuppressLint; import android.os.Handler; import android.os.Looper; import android.util.Pair; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; import androidx.annotation.Nullable; import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.analytics.AnalyticsCollector; @@ -48,6 +52,8 @@ import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.video.VideoFrameMetadataListener; +import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; @@ -1004,6 +1010,58 @@ import java.util.concurrent.CopyOnWriteArraySet; return 1; } + /** This method is not supported and does nothing. */ + @Override + public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void setCameraMotionListener(CameraMotionListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearCameraMotionListener(CameraMotionListener listener) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurface() {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurface(@Nullable Surface surface) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurface(@Nullable Surface surface) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) {} + + /** This method is not supported and does nothing. */ + @Override + public void setVideoTextureView(@Nullable TextureView textureView) {} + + /** This method is not supported and does nothing. */ + @Override + public void clearVideoTextureView(@Nullable TextureView textureView) {} + /** This method is not supported and returns an empty list. */ @Override public ImmutableList getCurrentCues() { 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 e712d4396d..d498483ebd 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 @@ -79,7 +79,7 @@ import java.util.concurrent.TimeoutException; public class SimpleExoPlayer extends BasePlayer implements ExoPlayer, ExoPlayer.AudioComponent, - Player.VideoComponent, + ExoPlayer.VideoComponent, ExoPlayer.TextComponent, ExoPlayer.MetadataComponent, ExoPlayer.DeviceComponent { diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java index 85bbc4d38d..9690f620c7 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.ui; import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE; import android.annotation.SuppressLint; import android.content.Context; @@ -563,14 +564,13 @@ public class PlayerView extends FrameLayout implements AdViewProvider { @Nullable Player oldPlayer = this.player; if (oldPlayer != null) { oldPlayer.removeListener(componentListener); - @Nullable Player.VideoComponent oldVideoComponent = oldPlayer.getVideoComponent(); - if (oldVideoComponent != null) { + if (oldPlayer.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) { if (surfaceView instanceof TextureView) { - oldVideoComponent.clearVideoTextureView((TextureView) surfaceView); + oldPlayer.clearVideoTextureView((TextureView) surfaceView); } else if (surfaceView instanceof SphericalGLSurfaceView) { - ((SphericalGLSurfaceView) surfaceView).setVideoComponent(null); + ((SphericalGLSurfaceView) surfaceView).setPlayer(null); } else if (surfaceView instanceof SurfaceView) { - oldVideoComponent.clearVideoSurfaceView((SurfaceView) surfaceView); + oldPlayer.clearVideoSurfaceView((SurfaceView) surfaceView); } } } @@ -585,16 +585,14 @@ public class PlayerView extends FrameLayout implements AdViewProvider { updateErrorMessage(); updateForCurrentTrackSelections(/* isNewPlayer= */ true); if (player != null) { - @Nullable Player.VideoComponent newVideoComponent = player.getVideoComponent(); - if (newVideoComponent != null) { + if (player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) { if (surfaceView instanceof TextureView) { - newVideoComponent.setVideoTextureView((TextureView) surfaceView); + player.setVideoTextureView((TextureView) surfaceView); } else if (surfaceView instanceof SphericalGLSurfaceView) { - ((SphericalGLSurfaceView) surfaceView).setVideoComponent(newVideoComponent); + ((SphericalGLSurfaceView) surfaceView).setPlayer(player); } else if (surfaceView instanceof SurfaceView) { - newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView); + player.setVideoSurfaceView((SurfaceView) surfaceView); } - newVideoComponent.addVideoListener(componentListener); } if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) { subtitleView.setCues(player.getCurrentCues()); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java index 0eb9b40286..6e286e0cc2 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.ui; import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE; import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import android.annotation.SuppressLint; @@ -572,15 +573,12 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { @Nullable Player oldPlayer = this.player; if (oldPlayer != null) { oldPlayer.removeListener(componentListener); - @Nullable Player.VideoComponent oldVideoComponent = oldPlayer.getVideoComponent(); - if (oldVideoComponent != null) { - if (surfaceView instanceof TextureView) { - oldVideoComponent.clearVideoTextureView((TextureView) surfaceView); - } else if (surfaceView instanceof SphericalGLSurfaceView) { - ((SphericalGLSurfaceView) surfaceView).setVideoComponent(null); - } else if (surfaceView instanceof SurfaceView) { - oldVideoComponent.clearVideoSurfaceView((SurfaceView) surfaceView); - } + if (surfaceView instanceof TextureView) { + oldPlayer.clearVideoTextureView((TextureView) surfaceView); + } else if (surfaceView instanceof SphericalGLSurfaceView) { + ((SphericalGLSurfaceView) surfaceView).setPlayer(null); + } else if (surfaceView instanceof SurfaceView) { + oldPlayer.clearVideoSurfaceView((SurfaceView) surfaceView); } } if (subtitleView != null) { @@ -594,16 +592,14 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { updateErrorMessage(); updateForCurrentTrackSelections(/* isNewPlayer= */ true); if (player != null) { - @Nullable Player.VideoComponent newVideoComponent = player.getVideoComponent(); - if (newVideoComponent != null) { + if (player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) { if (surfaceView instanceof TextureView) { - newVideoComponent.setVideoTextureView((TextureView) surfaceView); + player.setVideoTextureView((TextureView) surfaceView); } else if (surfaceView instanceof SphericalGLSurfaceView) { - ((SphericalGLSurfaceView) surfaceView).setVideoComponent(newVideoComponent); + ((SphericalGLSurfaceView) surfaceView).setPlayer(player); } else if (surfaceView instanceof SurfaceView) { - newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView); + player.setVideoSurfaceView((SurfaceView) surfaceView); } - newVideoComponent.addVideoListener(componentListener); } if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) { subtitleView.setCues(player.getCurrentCues()); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalGLSurfaceView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalGLSurfaceView.java index 97e750eb43..0dd9466f48 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalGLSurfaceView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalGLSurfaceView.java @@ -72,7 +72,7 @@ public final class SphericalGLSurfaceView extends GLSurfaceView { private final SceneRenderer scene; @Nullable private SurfaceTexture surfaceTexture; @Nullable private Surface surface; - @Nullable private Player.VideoComponent videoComponent; + @Nullable private Player player; private boolean useSensorRotation; private boolean isStarted; private boolean isOrientationListenerRegistered; @@ -125,23 +125,23 @@ public final class SphericalGLSurfaceView extends GLSurfaceView { scene.setDefaultStereoMode(stereoMode); } - /** Sets the {@link Player.VideoComponent} to use. */ - public void setVideoComponent(@Nullable Player.VideoComponent newVideoComponent) { - if (newVideoComponent == videoComponent) { + /** Sets the {@link Player} to use. */ + public void setPlayer(@Nullable Player newPlayer) { + if (newPlayer == player) { return; } - if (videoComponent != null) { + if (player != null) { if (surface != null) { - videoComponent.clearVideoSurface(surface); + player.clearVideoSurface(surface); } - videoComponent.clearVideoFrameMetadataListener(scene); - videoComponent.clearCameraMotionListener(scene); + player.clearVideoFrameMetadataListener(scene); + player.clearCameraMotionListener(scene); } - videoComponent = newVideoComponent; - if (videoComponent != null) { - videoComponent.setVideoFrameMetadataListener(scene); - videoComponent.setCameraMotionListener(scene); - videoComponent.setVideoSurface(surface); + player = newPlayer; + if (this.player != null) { + player.setVideoFrameMetadataListener(scene); + player.setCameraMotionListener(scene); + player.setVideoSurface(surface); } } @@ -174,8 +174,8 @@ public final class SphericalGLSurfaceView extends GLSurfaceView { mainHandler.post( () -> { if (surface != null) { - if (videoComponent != null) { - videoComponent.clearVideoSurface(surface); + if (player != null) { + player.clearVideoSurface(surface); } releaseSurface(surfaceTexture, surface); surfaceTexture = null; @@ -206,8 +206,8 @@ public final class SphericalGLSurfaceView extends GLSurfaceView { Surface oldSurface = this.surface; this.surfaceTexture = surfaceTexture; this.surface = new Surface(surfaceTexture); - if (videoComponent != null) { - videoComponent.setVideoSurface(surface); + if (player != null) { + player.setVideoSurface(surface); } releaseSurface(oldSurfaceTexture, oldSurface); }); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 7c5b9fa0db..cdac50b4b3 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -16,6 +16,10 @@ package com.google.android.exoplayer2.testutil; import android.os.Looper; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; import androidx.annotation.Nullable; import com.google.android.exoplayer2.BasePlayer; import com.google.android.exoplayer2.ExoPlaybackException; @@ -37,6 +41,8 @@ import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.video.VideoFrameMetadataListener; +import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import java.util.List; /** @@ -440,6 +446,71 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCameraMotionListener(CameraMotionListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearCameraMotionListener(CameraMotionListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurface() { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurface(@Nullable Surface surface) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurface(@Nullable Surface surface) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { + throw new UnsupportedOperationException(); + } + + @Override + public void setVideoTextureView(@Nullable TextureView textureView) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearVideoTextureView(@Nullable TextureView textureView) { + throw new UnsupportedOperationException(); + } + @Override public List getCurrentCues() { throw new UnsupportedOperationException();