diff --git a/library/common/src/main/java/com/google/android/exoplayer2/C.java b/library/common/src/main/java/com/google/android/exoplayer2/C.java index 916b52b8bd..de31cade4a 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/C.java @@ -782,7 +782,7 @@ public final class C { */ public static final UUID PLAYREADY_UUID = new UUID(0x9A04F07998404286L, 0xAB92E65BE0885F95L); - /** @deprecated Use {@code Renderer.MSG_SET_SURFACE}. */ + /** @deprecated Use {@code Renderer.MSG_SET_VIDEO_OUTPUT}. */ @Deprecated public static final int MSG_SET_SURFACE = 1; /** @deprecated Use {@code Renderer.MSG_SET_VOLUME}. */ @@ -803,9 +803,6 @@ public final class C { /** @deprecated Use {@code Renderer.MSG_SET_CAMERA_MOTION_LISTENER}. */ @Deprecated public static final int MSG_SET_CAMERA_MOTION_LISTENER = 7; - /** @deprecated Use {@code Renderer.MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER}. */ - @Deprecated public static final int MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER = 8; - /** @deprecated Use {@code Renderer.MSG_CUSTOM_BASE}. */ @Deprecated public static final int MSG_CUSTOM_BASE = 10000; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index 37db0c5544..cb76107df5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -196,7 +196,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // PlayerMessage.Target implementation. @Override - public void handleMessage(int what, @Nullable Object object) throws ExoPlaybackException { + public void handleMessage(int messageType, @Nullable Object payload) throws ExoPlaybackException { // Do nothing. } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java b/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java index 0aba970a25..f84fef5004 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java @@ -24,7 +24,6 @@ import com.google.android.exoplayer2.audio.AuxEffectInfo; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.util.MediaClock; import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.DecoderVideoRenderer; import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer; import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.spherical.CameraMotionListener; @@ -76,11 +75,14 @@ public interface Renderer extends PlayerMessage.Target { /** * The type of a message that can be passed to a video renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be the target {@link Surface}, or - * null. + * ExoPlayer#createMessage(Target)}. The message payload is normally a {@link Surface}, however + * some video renderers may accept other outputs (e.g., {@link VideoDecoderOutputBufferRenderer}). + * + *

If the receiving renderer does not support the payload type as an output, then it will clear + * any existing output that it has. */ @SuppressWarnings("deprecation") - int MSG_SET_SURFACE = C.MSG_SET_SURFACE; + int MSG_SET_VIDEO_OUTPUT = C.MSG_SET_SURFACE; /** * A type of a message that can be passed to an audio renderer via {@link * ExoPlayer#createMessage(Target)}. The message payload should be a {@link Float} with 0 being @@ -142,17 +144,6 @@ public interface Renderer extends PlayerMessage.Target { */ @SuppressWarnings("deprecation") int MSG_SET_CAMERA_MOTION_LISTENER = C.MSG_SET_CAMERA_MOTION_LISTENER; - /** - * The type of a message that can be passed to a {@link DecoderVideoRenderer} via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be the target {@link - * VideoDecoderOutputBufferRenderer}, or null. - * - *

This message is intended only for use with extension renderers that expect a {@link - * VideoDecoderOutputBufferRenderer}. For other use cases, an output surface should be passed via - * {@link #MSG_SET_SURFACE} instead. - */ - @SuppressWarnings("deprecation") - int MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER = C.MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER; /** * The type of a message that can be passed to an audio renderer via {@link * ExoPlayer#createMessage(Target)}. The message payload should be a {@link Boolean} instance 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 e2dae18b4e..f7e53ba2e7 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 @@ -61,7 +61,6 @@ import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.PriorityTaskManager; import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.VideoDecoderGLSurfaceView; import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer; import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.VideoListener; @@ -587,11 +586,12 @@ public class SimpleExoPlayer extends BasePlayer @Nullable private Format videoFormat; @Nullable private Format audioFormat; @Nullable private AudioTrack keepSessionIdAudioTrack; - @Nullable private Surface surface; - private boolean ownsSurface; - @C.VideoScalingMode private int videoScalingMode; + @Nullable private Object videoOutput; + @Nullable private Surface ownedSurface; @Nullable private SurfaceHolder surfaceHolder; + private boolean surfaceHolderSurfaceIsVideoOutput; @Nullable private TextureView textureView; + @C.VideoScalingMode private int videoScalingMode; private int surfaceWidth; private int surfaceHeight; @Nullable private DecoderCounters videoDecoderCounters; @@ -797,14 +797,14 @@ public class SimpleExoPlayer extends BasePlayer public void clearVideoSurface() { verifyApplicationThread(); removeSurfaceCallbacks(); - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ false); + setVideoOutputInternal(/* videoOutput= */ null); maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } @Override public void clearVideoSurface(@Nullable Surface surface) { verifyApplicationThread(); - if (surface != null && surface == this.surface) { + if (surface != null && surface == videoOutput) { clearVideoSurface(); } } @@ -813,10 +813,7 @@ public class SimpleExoPlayer extends BasePlayer public void setVideoSurface(@Nullable Surface surface) { verifyApplicationThread(); removeSurfaceCallbacks(); - if (surface != null) { - setVideoDecoderOutputBufferRenderer(/* videoDecoderOutputBufferRenderer= */ null); - } - setVideoSurfaceInternal(surface, /* ownsSurface= */ false); + setVideoOutputInternal(surface); int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); } @@ -824,23 +821,20 @@ public class SimpleExoPlayer extends BasePlayer @Override public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { verifyApplicationThread(); - removeSurfaceCallbacks(); - if (surfaceHolder != null) { - setVideoDecoderOutputBufferRenderer(/* videoDecoderOutputBufferRenderer= */ null); - } - this.surfaceHolder = surfaceHolder; if (surfaceHolder == null) { - setVideoSurfaceInternal(null, /* ownsSurface= */ false); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + clearVideoSurface(); } else { + removeSurfaceCallbacks(); + this.surfaceHolderSurfaceIsVideoOutput = true; + this.surfaceHolder = surfaceHolder; surfaceHolder.addCallback(componentListener); Surface surface = surfaceHolder.getSurface(); if (surface != null && surface.isValid()) { - setVideoSurfaceInternal(surface, /* ownsSurface= */ false); + setVideoOutputInternal(surface); Rect surfaceSize = surfaceHolder.getSurfaceFrame(); maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); } else { - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ false); + setVideoOutputInternal(/* videoOutput= */ null); maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } } @@ -850,7 +844,7 @@ public class SimpleExoPlayer extends BasePlayer public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { verifyApplicationThread(); if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { - setVideoSurfaceHolder(null); + clearVideoSurface(); } } @@ -858,11 +852,21 @@ public class SimpleExoPlayer extends BasePlayer public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { verifyApplicationThread(); if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { - VideoDecoderOutputBufferRenderer videoDecoderOutputBufferRenderer = - (VideoDecoderOutputBufferRenderer) surfaceView; - clearVideoSurface(); + removeSurfaceCallbacks(); + setVideoOutputInternal(surfaceView); + // Although we won't use the surface directly as the video output, still use the holder to + // query the surface size, to be informed in changes to the size via componentListener, and + // for equality checking in clearVideoSurfaceHolder. + surfaceHolderSurfaceIsVideoOutput = false; surfaceHolder = surfaceView.getHolder(); - setVideoDecoderOutputBufferRenderer(videoDecoderOutputBufferRenderer); + surfaceHolder.addCallback(componentListener); + Surface surface = surfaceHolder.getSurface(); + if (surface != null && surface.isValid()) { + Rect surfaceSize = surfaceHolder.getSurfaceFrame(); + maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); + } else { + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } } else { setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); } @@ -871,39 +875,29 @@ public class SimpleExoPlayer extends BasePlayer @Override public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { verifyApplicationThread(); - if (surfaceView instanceof VideoDecoderGLSurfaceView) { - if (surfaceView.getHolder() == surfaceHolder) { - setVideoDecoderOutputBufferRenderer(null); - surfaceHolder = null; - } - } else { - clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); - } + clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); } @Override public void setVideoTextureView(@Nullable TextureView textureView) { verifyApplicationThread(); - removeSurfaceCallbacks(); - if (textureView != null) { - setVideoDecoderOutputBufferRenderer(/* videoDecoderOutputBufferRenderer= */ null); - } - this.textureView = textureView; if (textureView == null) { - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ true); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + clearVideoSurface(); } else { + removeSurfaceCallbacks(); + this.textureView = textureView; if (textureView.getSurfaceTextureListener() != null) { Log.w(TAG, "Replacing existing SurfaceTextureListener."); } textureView.setSurfaceTextureListener(componentListener); + @Nullable SurfaceTexture surfaceTexture = textureView.isAvailable() ? textureView.getSurfaceTexture() : null; if (surfaceTexture == null) { - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ true); + setVideoOutputInternal(/* videoOutput= */ null); maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } else { - setVideoSurfaceInternal(new Surface(surfaceTexture), /* ownsSurface= */ true); + setSurfaceTextureInternal(surfaceTexture); maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight()); } } @@ -913,7 +907,7 @@ public class SimpleExoPlayer extends BasePlayer public void clearVideoTextureView(@Nullable TextureView textureView) { verifyApplicationThread(); if (textureView != null && textureView == this.textureView) { - setVideoTextureView(null); + clearVideoSurface(); } } @@ -1563,11 +1557,9 @@ public class SimpleExoPlayer extends BasePlayer player.release(); analyticsCollector.release(); removeSurfaceCallbacks(); - if (surface != null) { - if (ownsSurface) { - surface.release(); - } - surface = null; + if (ownedSurface != null) { + ownedSurface.release(); + ownedSurface = null; } if (isPriorityTaskManagerRegistered) { Assertions.checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); @@ -1834,22 +1826,29 @@ public class SimpleExoPlayer extends BasePlayer } } - private void setVideoSurfaceInternal(@Nullable Surface surface, boolean ownsSurface) { - // Note: We don't turn this method into a no-op if the surface is being replaced with itself - // so as to ensure onRenderedFirstFrame callbacks are still called in this case. + private void setSurfaceTextureInternal(SurfaceTexture surfaceTexture) { + Surface surface = new Surface(surfaceTexture); + setVideoOutputInternal(surface); + ownedSurface = surface; + } + + private void setVideoOutputInternal(@Nullable Object videoOutput) { + // Note: We don't turn this method into a no-op if the output is being replaced with itself so + // as to ensure onRenderedFirstFrame callbacks are still called in this case. List messages = new ArrayList<>(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { messages.add( player .createMessage(renderer) - .setType(Renderer.MSG_SET_SURFACE) - .setPayload(surface) + .setType(Renderer.MSG_SET_VIDEO_OUTPUT) + .setPayload(videoOutput) .send()); } } - if (this.surface != null && this.surface != surface) { - // We're replacing a surface. Block to ensure that it's not accessed after the method returns. + if (this.videoOutput != null && this.videoOutput != videoOutput) { + // We're replacing an output. Block to ensure that this output will not be accessed by the + // renderers after this method returns. try { for (PlayerMessage message : messages) { message.blockUntilDelivered(detachSurfaceTimeoutMs); @@ -1863,21 +1862,13 @@ public class SimpleExoPlayer extends BasePlayer ExoPlaybackException.createForRenderer( new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE))); } - // If we created the previous surface, we are responsible for releasing it. - if (this.ownsSurface) { - this.surface.release(); + if (this.videoOutput == ownedSurface) { + // We're replacing a surface that we are responsible for releasing. + ownedSurface.release(); + ownedSurface = null; } } - this.surface = surface; - this.ownsSurface = ownsSurface; - } - - private void setVideoDecoderOutputBufferRenderer( - @Nullable VideoDecoderOutputBufferRenderer videoDecoderOutputBufferRenderer) { - sendRendererMessage( - C.TRACK_TYPE_VIDEO, - Renderer.MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER, - videoDecoderOutputBufferRenderer); + this.videoOutput = videoOutput; } private void maybeNotifySurfaceSizeChanged(int width, int height) { @@ -2060,9 +2051,9 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public void onRenderedFirstFrame(@Nullable Surface surface, long renderTimeMs) { - analyticsCollector.onRenderedFirstFrame(surface, renderTimeMs); - if (SimpleExoPlayer.this.surface == surface) { + public void onRenderedFirstFrame(Object output, long renderTimeMs) { + analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); + if (videoOutput == output) { for (VideoListener videoListener : videoListeners) { videoListener.onRenderedFirstFrame(); } @@ -2178,7 +2169,9 @@ public class SimpleExoPlayer extends BasePlayer @Override public void surfaceCreated(SurfaceHolder holder) { - setVideoSurfaceInternal(holder.getSurface(), false); + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(holder.getSurface()); + } } @Override @@ -2188,7 +2181,9 @@ public class SimpleExoPlayer extends BasePlayer @Override public void surfaceDestroyed(SurfaceHolder holder) { - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ false); + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(/* videoOutput= */ null); + } maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } @@ -2196,7 +2191,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { - setVideoSurfaceInternal(new Surface(surfaceTexture), /* ownsSurface= */ true); + setSurfaceTextureInternal(surfaceTexture); maybeNotifySurfaceSizeChanged(width, height); } @@ -2207,7 +2202,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { - setVideoSurfaceInternal(/* surface= */ null, /* ownsSurface= */ true); + setVideoOutputInternal(/* videoOutput= */ null); maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); return true; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index 6a0c56caf7..51c89217f4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -19,7 +19,6 @@ import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import android.os.Looper; import android.util.SparseArray; -import android.view.Surface; import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -442,17 +441,13 @@ public class AnalyticsCollector eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio)); } - @SuppressWarnings("deprecation") // Calling deprecated listener method. @Override - public final void onRenderedFirstFrame(@Nullable Surface surface, long renderTimeMs) { + public final void onRenderedFirstFrame(Object output, long renderTimeMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( eventTime, AnalyticsListener.EVENT_RENDERED_FIRST_FRAME, - listener -> { - listener.onRenderedFirstFrame(eventTime, surface); - listener.onRenderedFirstFrame(eventTime, surface, renderTimeMs); - }); + listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs)); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java index 643ce08b84..7c4e9750a0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java @@ -48,6 +48,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.util.ExoFlags; +import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer; import com.google.common.base.Objects; import java.io.IOException; import java.lang.annotation.Documented; @@ -1020,16 +1021,11 @@ public interface AnalyticsListener { * renderer was reset, or since the stream being rendered was changed. * * @param eventTime The event time. - * @param surface The {@link Surface} to which a frame has been rendered, or {@code null} if the - * renderer renders to something that isn't a {@link Surface}. + * @param output The output to which a frame has been rendered. Normally a {@link Surface}, + * however may also be other output types (e.g., a {@link VideoDecoderOutputBufferRenderer}). * @param renderTimeMs {@link SystemClock#elapsedRealtime()} when the first frame was rendered. */ - default void onRenderedFirstFrame( - EventTime eventTime, @Nullable Surface surface, long renderTimeMs) {} - - /** @deprecated Use {@link #onRenderedFirstFrame(EventTime, Surface, long)} instead. */ - @Deprecated - default void onRenderedFirstFrame(EventTime eventTime, @Nullable Surface surface) {} + default void onRenderedFirstFrame(EventTime eventTime, Object output, long renderTimeMs) {} /** * Called before a frame is rendered for the first time since setting the surface, and each time diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java index 513621a6fa..ceec6e82cd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java @@ -19,7 +19,6 @@ import static java.lang.Math.min; import android.os.SystemClock; import android.text.TextUtils; -import android.view.Surface; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -453,8 +452,8 @@ public class EventLogger implements AnalyticsListener { } @Override - public void onRenderedFirstFrame(EventTime eventTime, @Nullable Surface surface) { - logd(eventTime, "renderedFirstFrame", String.valueOf(surface)); + public void onRenderedFirstFrame(EventTime eventTime, Object output, long renderTimeMs) { + logd(eventTime, "renderedFirstFrame", String.valueOf(output)); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/DecoderVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/DecoderVideoRenderer.java index 436fa00530..b86e4ff4d8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/DecoderVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/DecoderVideoRenderer.java @@ -29,6 +29,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.C.VideoOutputMode; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; @@ -59,11 +60,9 @@ import java.lang.annotation.RetentionPolicy; * on the playback thread: * *