mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Support efficient switching between SimpleExoPlayerView instances
Prior to this change, the only way to switch SimpleExoPlayerView was to do: oldView.setPlayer(null); newView.setPlayer(player); This would cause the video renderer to have to transition through oldSurface->noSurface->newSurface, which is inefficient (noSurface requires platform decoders to be fully released). After this change we support: newView.setPlayer(player); oldView.setPlayer(null); This results in direct oldSurface->newSurface transitions, which are seamless on Android M and above. The change also adds some robustness against developers ending up with strange behavior as a result of clearing the player from a view in a different ordering than we expect w.r.t. registering of other listeners. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154044976
This commit is contained in:
parent
4c0b539054
commit
8e8a6a2994
2 changed files with 138 additions and 23 deletions
|
|
@ -239,6 +239,18 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
setVideoSurfaceInternal(surface, false);
|
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
|
* 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.
|
* 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
|
* Sets the {@link SurfaceView} onto which video will be rendered. The player will track the
|
||||||
* lifecycle of the surface automatically.
|
* lifecycle of the surface automatically.
|
||||||
|
|
@ -263,7 +287,17 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
* @param surfaceView The surface view.
|
* @param surfaceView The surface view.
|
||||||
*/
|
*/
|
||||||
public void setVideoSurfaceView(SurfaceView surfaceView) {
|
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
|
* 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
|
* {@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;
|
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.
|
* Sets a listener to receive debug events from the video renderer.
|
||||||
*
|
*
|
||||||
|
|
@ -422,24 +519,6 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
audioDebugListener = listener;
|
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
|
// ExoPlayer implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
@ -319,6 +321,30 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
||||||
hideController();
|
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.
|
* 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
|
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and
|
||||||
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
|
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
|
||||||
* assignments are overridden.
|
* assignments are overridden.
|
||||||
|
* <p>
|
||||||
|
* 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 <em>before</em> 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.
|
* @param player The {@link SimpleExoPlayer} to use.
|
||||||
*/
|
*/
|
||||||
|
|
@ -338,10 +370,14 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.player != null) {
|
if (this.player != null) {
|
||||||
this.player.setTextOutput(null);
|
|
||||||
this.player.setVideoListener(null);
|
|
||||||
this.player.removeListener(componentListener);
|
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;
|
this.player = player;
|
||||||
if (useController) {
|
if (useController) {
|
||||||
|
|
@ -357,8 +393,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
||||||
player.setVideoSurfaceView((SurfaceView) surfaceView);
|
player.setVideoSurfaceView((SurfaceView) surfaceView);
|
||||||
}
|
}
|
||||||
player.setVideoListener(componentListener);
|
player.setVideoListener(componentListener);
|
||||||
player.addListener(componentListener);
|
|
||||||
player.setTextOutput(componentListener);
|
player.setTextOutput(componentListener);
|
||||||
|
player.addListener(componentListener);
|
||||||
maybeShowController(false);
|
maybeShowController(false);
|
||||||
updateForCurrentTrackSelections();
|
updateForCurrentTrackSelections();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue