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
This commit is contained in:
krocard 2021-04-16 17:37:28 +01:00 committed by Oliver Woodman
parent a6e9a9c6d2
commit cdebf6c68b
12 changed files with 501 additions and 217 deletions

View File

@ -3,6 +3,11 @@
### dev-v2 (not yet released) ### dev-v2 (not yet released)
* Core Library: * 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 * Add position info of the old and the new position as arguments to
`EventListener.onPositionDiscontinuity`. Add the new reasons `EventListener.onPositionDiscontinuity`. Add the new reasons
`DISCONTINUITY_REASON_SKIP` and `DISCONTINUITY_REASON_REMOVE` and rename `DISCONTINUITY_REASON_SKIP` and `DISCONTINUITY_REASON_REMOVE` and rename

View File

@ -25,8 +25,8 @@ import android.os.Handler;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; 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.Assertions;
import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.GlUtil;
import com.google.android.exoplayer2.util.TimedValueQueue; import com.google.android.exoplayer2.util.TimedValueQueue;
@ -72,7 +72,7 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView {
@Nullable private SurfaceTexture surfaceTexture; @Nullable private SurfaceTexture surfaceTexture;
@Nullable private Surface surface; @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 * 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. * @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) { if (newVideoComponent == videoComponent) {
return; return;
} }

View File

@ -19,6 +19,10 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.min; import static java.lang.Math.min;
import android.os.Looper; 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.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.BasePlayer; 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.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; 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.CastStatusCodes;
import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
@ -276,12 +282,6 @@ public final class CastPlayer extends BasePlayer {
// Player implementation. // Player implementation.
@Override
@Nullable
public VideoComponent getVideoComponent() {
return null;
}
@Override @Override
public Looper getApplicationLooper() { public Looper getApplicationLooper() {
return Looper.getMainLooper(); return Looper.getMainLooper();
@ -653,6 +653,57 @@ public final class CastPlayer extends BasePlayer {
return 1; 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. */ /** This method is not supported and returns an empty list. */
@Override @Override
public ImmutableList<Cue> getCurrentCues() { public ImmutableList<Cue> getCurrentCues() {

View File

@ -37,7 +37,6 @@ import com.google.android.exoplayer2.Player.TimelineChangeReason;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.ErrorMessageProvider; import com.google.android.exoplayer2.util.ErrorMessageProvider;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoListener;
/** Leanback {@code PlayerAdapter} implementation for {@link Player}. */ /** Leanback {@code PlayerAdapter} implementation for {@link Player}. */
public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnable { 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 Context context;
private final Player player; private final Player player;
private final Handler handler; private final Handler handler;
private final ComponentListener componentListener; private final PlayerListener playerListener;
private final int updatePeriodMs; private final int updatePeriodMs;
@Nullable private PlaybackPreparer playbackPreparer; @Nullable private PlaybackPreparer playbackPreparer;
@ -73,7 +72,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
this.player = player; this.player = player;
this.updatePeriodMs = updatePeriodMs; this.updatePeriodMs = updatePeriodMs;
handler = Util.createHandlerForCurrentOrMainLooper(); handler = Util.createHandlerForCurrentOrMainLooper();
componentListener = new ComponentListener(); playerListener = new PlayerListener();
controlDispatcher = new DefaultControlDispatcher(); controlDispatcher = new DefaultControlDispatcher();
} }
@ -118,23 +117,15 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
public void onAttachedToHost(PlaybackGlueHost host) { public void onAttachedToHost(PlaybackGlueHost host) {
if (host instanceof SurfaceHolderGlueHost) { if (host instanceof SurfaceHolderGlueHost) {
surfaceHolderGlueHost = ((SurfaceHolderGlueHost) host); surfaceHolderGlueHost = ((SurfaceHolderGlueHost) host);
surfaceHolderGlueHost.setSurfaceHolderCallback(componentListener); surfaceHolderGlueHost.setSurfaceHolderCallback(playerListener);
} }
notifyStateChanged(); notifyStateChanged();
player.addListener(componentListener); player.addListener(playerListener);
Player.VideoComponent videoComponent = player.getVideoComponent();
if (videoComponent != null) {
videoComponent.addVideoListener(componentListener);
}
} }
@Override @Override
public void onDetachedFromHost() { public void onDetachedFromHost() {
player.removeListener(componentListener); player.removeListener(playerListener);
Player.VideoComponent videoComponent = player.getVideoComponent();
if (videoComponent != null) {
videoComponent.removeVideoListener(componentListener);
}
if (surfaceHolderGlueHost != null) { if (surfaceHolderGlueHost != null) {
removeSurfaceHolderCallback(surfaceHolderGlueHost); removeSurfaceHolderCallback(surfaceHolderGlueHost);
surfaceHolderGlueHost = null; surfaceHolderGlueHost = null;
@ -227,10 +218,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
/* package */ void setVideoSurface(@Nullable Surface surface) { /* package */ void setVideoSurface(@Nullable Surface surface) {
hasSurface = surface != null; hasSurface = surface != null;
Player.VideoComponent videoComponent = player.getVideoComponent(); player.setVideoSurface(surface);
if (videoComponent != null) {
videoComponent.setVideoSurface(surface);
}
maybeNotifyPreparedStateChanged(getCallback()); maybeNotifyPreparedStateChanged(getCallback());
} }
@ -258,8 +246,7 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
surfaceHolderGlueHost.setSurfaceHolderCallback(null); surfaceHolderGlueHost.setSurfaceHolderCallback(null);
} }
private final class ComponentListener private final class PlayerListener implements Player.Listener, SurfaceHolder.Callback {
implements Player.EventListener, SurfaceHolder.Callback, VideoListener {
// SurfaceHolder.Callback implementation. // SurfaceHolder.Callback implementation.

View File

@ -67,146 +67,6 @@ import java.util.List;
*/ */
public interface Player { 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.
*
* <p>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.
*
* <p>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. * 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. */ /** Command to get the text that should currently be displayed by the player. */
int COMMAND_GET_TEXT = 22; 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 * Returns the {@link Looper} associated with the application thread that's used to access the
* player and on which player events are received. * player and on which player events are received.
@ -1830,6 +1686,117 @@ public interface Player {
*/ */
float getVolume(); float getVolume();
/**
* Sets a listener to receive video frame metadata events.
*
* <p>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.
*
* <p>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. */ /** Returns the current {@link Cue Cues}. This list may be empty. */
List<Cue> getCurrentCues(); List<Cue> getCurrentCues();

View File

@ -18,6 +18,10 @@ package com.google.android.exoplayer2;
import android.content.Context; import android.content.Context;
import android.media.AudioTrack; import android.media.AudioTrack;
import android.os.Looper; 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.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.analytics.AnalyticsCollector; 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.Clock;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; 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; import java.util.List;
/** /**
@ -223,6 +230,146 @@ public interface ExoPlayer extends Player {
boolean getSkipSilenceEnabled(); 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.
*
* <p>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.
*
* <p>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}. */ /** The text component of an {@link ExoPlayer}. */
interface TextComponent { interface TextComponent {
@ -651,6 +798,10 @@ public interface ExoPlayer extends Player {
@Nullable @Nullable
AudioComponent getAudioComponent(); 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. */ /** Returns the component of this player for text output, or null if text is not supported. */
@Nullable @Nullable
TextComponent getTextComponent(); TextComponent getTextComponent();

View File

@ -25,6 +25,10 @@ import android.annotation.SuppressLint;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Pair; 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 androidx.annotation.Nullable;
import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.analytics.AnalyticsCollector; 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.ListenerSet;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util; 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 com.google.common.collect.ImmutableList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -1004,6 +1010,58 @@ import java.util.concurrent.CopyOnWriteArraySet;
return 1; 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. */ /** This method is not supported and returns an empty list. */
@Override @Override
public ImmutableList<Cue> getCurrentCues() { public ImmutableList<Cue> getCurrentCues() {

View File

@ -79,7 +79,7 @@ import java.util.concurrent.TimeoutException;
public class SimpleExoPlayer extends BasePlayer public class SimpleExoPlayer extends BasePlayer
implements ExoPlayer, implements ExoPlayer,
ExoPlayer.AudioComponent, ExoPlayer.AudioComponent,
Player.VideoComponent, ExoPlayer.VideoComponent,
ExoPlayer.TextComponent, ExoPlayer.TextComponent,
ExoPlayer.MetadataComponent, ExoPlayer.MetadataComponent,
ExoPlayer.DeviceComponent { ExoPlayer.DeviceComponent {

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ui; package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; 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.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@ -563,14 +564,13 @@ public class PlayerView extends FrameLayout implements AdViewProvider {
@Nullable Player oldPlayer = this.player; @Nullable Player oldPlayer = this.player;
if (oldPlayer != null) { if (oldPlayer != null) {
oldPlayer.removeListener(componentListener); oldPlayer.removeListener(componentListener);
@Nullable Player.VideoComponent oldVideoComponent = oldPlayer.getVideoComponent(); if (oldPlayer.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) {
if (oldVideoComponent != null) {
if (surfaceView instanceof TextureView) { if (surfaceView instanceof TextureView) {
oldVideoComponent.clearVideoTextureView((TextureView) surfaceView); oldPlayer.clearVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SphericalGLSurfaceView) { } else if (surfaceView instanceof SphericalGLSurfaceView) {
((SphericalGLSurfaceView) surfaceView).setVideoComponent(null); ((SphericalGLSurfaceView) surfaceView).setPlayer(null);
} else if (surfaceView instanceof SurfaceView) { } 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(); updateErrorMessage();
updateForCurrentTrackSelections(/* isNewPlayer= */ true); updateForCurrentTrackSelections(/* isNewPlayer= */ true);
if (player != null) { if (player != null) {
@Nullable Player.VideoComponent newVideoComponent = player.getVideoComponent(); if (player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) {
if (newVideoComponent != null) {
if (surfaceView instanceof TextureView) { if (surfaceView instanceof TextureView) {
newVideoComponent.setVideoTextureView((TextureView) surfaceView); player.setVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SphericalGLSurfaceView) { } else if (surfaceView instanceof SphericalGLSurfaceView) {
((SphericalGLSurfaceView) surfaceView).setVideoComponent(newVideoComponent); ((SphericalGLSurfaceView) surfaceView).setPlayer(player);
} else if (surfaceView instanceof SurfaceView) { } else if (surfaceView instanceof SurfaceView) {
newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView); player.setVideoSurfaceView((SurfaceView) surfaceView);
} }
newVideoComponent.addVideoListener(componentListener);
} }
if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) { if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) {
subtitleView.setCues(player.getCurrentCues()); subtitleView.setCues(player.getCurrentCues());

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ui; package com.google.android.exoplayer2.ui;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; 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 static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@ -572,15 +573,12 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider {
@Nullable Player oldPlayer = this.player; @Nullable Player oldPlayer = this.player;
if (oldPlayer != null) { if (oldPlayer != null) {
oldPlayer.removeListener(componentListener); oldPlayer.removeListener(componentListener);
@Nullable Player.VideoComponent oldVideoComponent = oldPlayer.getVideoComponent();
if (oldVideoComponent != null) {
if (surfaceView instanceof TextureView) { if (surfaceView instanceof TextureView) {
oldVideoComponent.clearVideoTextureView((TextureView) surfaceView); oldPlayer.clearVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SphericalGLSurfaceView) { } else if (surfaceView instanceof SphericalGLSurfaceView) {
((SphericalGLSurfaceView) surfaceView).setVideoComponent(null); ((SphericalGLSurfaceView) surfaceView).setPlayer(null);
} else if (surfaceView instanceof SurfaceView) { } else if (surfaceView instanceof SurfaceView) {
oldVideoComponent.clearVideoSurfaceView((SurfaceView) surfaceView); oldPlayer.clearVideoSurfaceView((SurfaceView) surfaceView);
}
} }
} }
if (subtitleView != null) { if (subtitleView != null) {
@ -594,16 +592,14 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider {
updateErrorMessage(); updateErrorMessage();
updateForCurrentTrackSelections(/* isNewPlayer= */ true); updateForCurrentTrackSelections(/* isNewPlayer= */ true);
if (player != null) { if (player != null) {
@Nullable Player.VideoComponent newVideoComponent = player.getVideoComponent(); if (player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) {
if (newVideoComponent != null) {
if (surfaceView instanceof TextureView) { if (surfaceView instanceof TextureView) {
newVideoComponent.setVideoTextureView((TextureView) surfaceView); player.setVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SphericalGLSurfaceView) { } else if (surfaceView instanceof SphericalGLSurfaceView) {
((SphericalGLSurfaceView) surfaceView).setVideoComponent(newVideoComponent); ((SphericalGLSurfaceView) surfaceView).setPlayer(player);
} else if (surfaceView instanceof SurfaceView) { } else if (surfaceView instanceof SurfaceView) {
newVideoComponent.setVideoSurfaceView((SurfaceView) surfaceView); player.setVideoSurfaceView((SurfaceView) surfaceView);
} }
newVideoComponent.addVideoListener(componentListener);
} }
if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) { if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) {
subtitleView.setCues(player.getCurrentCues()); subtitleView.setCues(player.getCurrentCues());

View File

@ -72,7 +72,7 @@ public final class SphericalGLSurfaceView extends GLSurfaceView {
private final SceneRenderer scene; private final SceneRenderer scene;
@Nullable private SurfaceTexture surfaceTexture; @Nullable private SurfaceTexture surfaceTexture;
@Nullable private Surface surface; @Nullable private Surface surface;
@Nullable private Player.VideoComponent videoComponent; @Nullable private Player player;
private boolean useSensorRotation; private boolean useSensorRotation;
private boolean isStarted; private boolean isStarted;
private boolean isOrientationListenerRegistered; private boolean isOrientationListenerRegistered;
@ -125,23 +125,23 @@ public final class SphericalGLSurfaceView extends GLSurfaceView {
scene.setDefaultStereoMode(stereoMode); scene.setDefaultStereoMode(stereoMode);
} }
/** Sets the {@link Player.VideoComponent} to use. */ /** Sets the {@link Player} to use. */
public void setVideoComponent(@Nullable Player.VideoComponent newVideoComponent) { public void setPlayer(@Nullable Player newPlayer) {
if (newVideoComponent == videoComponent) { if (newPlayer == player) {
return; return;
} }
if (videoComponent != null) { if (player != null) {
if (surface != null) { if (surface != null) {
videoComponent.clearVideoSurface(surface); player.clearVideoSurface(surface);
} }
videoComponent.clearVideoFrameMetadataListener(scene); player.clearVideoFrameMetadataListener(scene);
videoComponent.clearCameraMotionListener(scene); player.clearCameraMotionListener(scene);
} }
videoComponent = newVideoComponent; player = newPlayer;
if (videoComponent != null) { if (this.player != null) {
videoComponent.setVideoFrameMetadataListener(scene); player.setVideoFrameMetadataListener(scene);
videoComponent.setCameraMotionListener(scene); player.setCameraMotionListener(scene);
videoComponent.setVideoSurface(surface); player.setVideoSurface(surface);
} }
} }
@ -174,8 +174,8 @@ public final class SphericalGLSurfaceView extends GLSurfaceView {
mainHandler.post( mainHandler.post(
() -> { () -> {
if (surface != null) { if (surface != null) {
if (videoComponent != null) { if (player != null) {
videoComponent.clearVideoSurface(surface); player.clearVideoSurface(surface);
} }
releaseSurface(surfaceTexture, surface); releaseSurface(surfaceTexture, surface);
surfaceTexture = null; surfaceTexture = null;
@ -206,8 +206,8 @@ public final class SphericalGLSurfaceView extends GLSurfaceView {
Surface oldSurface = this.surface; Surface oldSurface = this.surface;
this.surfaceTexture = surfaceTexture; this.surfaceTexture = surfaceTexture;
this.surface = new Surface(surfaceTexture); this.surface = new Surface(surfaceTexture);
if (videoComponent != null) { if (player != null) {
videoComponent.setVideoSurface(surface); player.setVideoSurface(surface);
} }
releaseSurface(oldSurfaceTexture, oldSurface); releaseSurface(oldSurfaceTexture, oldSurface);
}); });

View File

@ -16,6 +16,10 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.os.Looper; 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.Nullable;
import com.google.android.exoplayer2.BasePlayer; import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.ExoPlaybackException; 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.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Clock; 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; import java.util.List;
/** /**
@ -440,6 +446,71 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException(); 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 @Override
public List<Cue> getCurrentCues() { public List<Cue> getCurrentCues() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();