Simplify extension video renderers

- This change also adds support for VideoFrameMetadataListener in the
  AV1 renderer
- This is a preliminary step prior to adding FfmpegVideoDecoder

Issue: #2159
PiperOrigin-RevId: 301702460
This commit is contained in:
olly 2020-03-19 00:19:33 +00:00 committed by Oliver Woodman
parent ee6afe5eb9
commit 0a274946ac
5 changed files with 60 additions and 96 deletions

View File

@ -21,37 +21,17 @@ import android.os.Handler;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderException;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.DecoderVideoRenderer; import com.google.android.exoplayer2.video.DecoderVideoRenderer;
import com.google.android.exoplayer2.video.VideoDecoderInputBuffer;
import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer; import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer;
import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
/** /** Decodes and renders video using libgav1 decoder. */
* Decodes and renders video using libgav1 decoder.
*
* <p>This renderer accepts the following messages sent via {@link ExoPlayer#createMessage(Target)}
* on the playback thread:
*
* <ul>
* <li>Message with type {@link #MSG_SET_SURFACE} to set the output surface. The message payload
* should be the target {@link Surface}, or null.
* <li>Message with type {@link #MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER} to set the output
* buffer renderer. The message payload should be the target {@link
* VideoDecoderOutputBufferRenderer}, or null.
* </ul>
*/
public class Libgav1VideoRenderer extends DecoderVideoRenderer { public class Libgav1VideoRenderer extends DecoderVideoRenderer {
private static final int DEFAULT_NUM_OF_INPUT_BUFFERS = 4; private static final int DEFAULT_NUM_OF_INPUT_BUFFERS = 4;
@ -73,7 +53,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
@Nullable private Gav1Decoder decoder; @Nullable private Gav1Decoder decoder;
/** /**
* Creates a Libgav1VideoRenderer. * Creates a new instance.
* *
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback. * can attempt to seamlessly join an ongoing playback.
@ -99,7 +79,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
} }
/** /**
* Creates a Libgav1VideoRenderer. * Creates a new instance.
* *
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback. * can attempt to seamlessly join an ongoing playback.
@ -140,9 +120,8 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
} }
@Override @Override
protected SimpleDecoder< protected Gav1Decoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
VideoDecoderInputBuffer, ? extends VideoDecoderOutputBuffer, ? extends DecoderException> throws Gav1DecoderException {
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) throws DecoderException {
TraceUtil.beginSection("createGav1Decoder"); TraceUtil.beginSection("createGav1Decoder");
int initialInputBufferSize = int initialInputBufferSize =
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE; format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
@ -170,17 +149,4 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
decoder.setOutputMode(outputMode); decoder.setOutputMode(outputMode);
} }
} }
// PlayerMessage.Target implementation.
@Override
public void handleMessage(int messageType, @Nullable Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) {
setOutputSurface((Surface) message);
} else if (messageType == MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER) {
setOutputBufferRenderer((VideoDecoderOutputBufferRenderer) message);
} else {
super.handleMessage(messageType, message);
}
}
} }

View File

@ -21,37 +21,16 @@ import android.os.Handler;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderException;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.video.DecoderVideoRenderer; import com.google.android.exoplayer2.video.DecoderVideoRenderer;
import com.google.android.exoplayer2.video.VideoDecoderInputBuffer;
import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer; import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer;
import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer;
import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
/** /** Decodes and renders video using the native VP9 decoder. */
* Decodes and renders video using the native VP9 decoder.
*
* <p>This renderer accepts the following messages sent via {@link ExoPlayer#createMessage(Target)}
* on the playback thread:
*
* <ul>
* <li>Message with type {@link #MSG_SET_SURFACE} to set the output surface. The message payload
* should be the target {@link Surface}, or null.
* <li>Message with type {@link #MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER} to set the output
* buffer renderer. The message payload should be the target {@link
* VideoDecoderOutputBufferRenderer}, or null.
* </ul>
*/
public class LibvpxVideoRenderer extends DecoderVideoRenderer { public class LibvpxVideoRenderer extends DecoderVideoRenderer {
/** The number of input buffers. */ /** The number of input buffers. */
@ -70,9 +49,10 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
private final int threads; private final int threads;
@Nullable private VpxDecoder decoder; @Nullable private VpxDecoder decoder;
@Nullable private VideoFrameMetadataListener frameMetadataListener;
/** /**
* Creates a new instance.
*
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback. * can attempt to seamlessly join an ongoing playback.
*/ */
@ -81,6 +61,8 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
} }
/** /**
* Creates a new instance.
*
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback. * can attempt to seamlessly join an ongoing playback.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
@ -105,6 +87,8 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
} }
/** /**
* Creates a new instance.
*
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback. * can attempt to seamlessly join an ongoing playback.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
@ -147,9 +131,8 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
} }
@Override @Override
protected SimpleDecoder< protected VpxDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
VideoDecoderInputBuffer, ? extends VideoDecoderOutputBuffer, ? extends DecoderException> throws VpxDecoderException {
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) throws DecoderException {
TraceUtil.beginSection("createVpxDecoder"); TraceUtil.beginSection("createVpxDecoder");
int initialInputBufferSize = int initialInputBufferSize =
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE; format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
@ -161,17 +144,6 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
return decoder; return decoder;
} }
@Override
protected void renderOutputBuffer(
VideoDecoderOutputBuffer outputBuffer, long presentationTimeUs, Format outputFormat)
throws DecoderException {
if (frameMetadataListener != null) {
frameMetadataListener.onVideoFrameAboutToBeRendered(
presentationTimeUs, System.nanoTime(), outputFormat, /* mediaFormat= */ null);
}
super.renderOutputBuffer(outputBuffer, presentationTimeUs, outputFormat);
}
@Override @Override
protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface) protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)
throws VpxDecoderException { throws VpxDecoderException {
@ -189,19 +161,4 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
decoder.setOutputMode(outputMode); decoder.setOutputMode(outputMode);
} }
} }
// PlayerMessage.Target implementation.
@Override
public void handleMessage(int messageType, @Nullable Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) {
setOutputSurface((Surface) message);
} else if (messageType == MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER) {
setOutputBufferRenderer((VideoDecoderOutputBufferRenderer) message);
} else if (messageType == MSG_SET_VIDEO_FRAME_METADATA_LISTENER) {
frameMetadataListener = (VideoFrameMetadataListener) message;
} else {
super.handleMessage(messageType, message);
}
}
} }

View File

@ -24,8 +24,10 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.decoder.Decoder; import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderException; import com.google.android.exoplayer2.decoder.DecoderException;
@ -42,7 +44,23 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** Decodes and renders video using a {@link Decoder}. */ /**
* Decodes and renders video using a {@link Decoder}.
*
* <p>This renderer accepts the following messages sent via {@link ExoPlayer#createMessage(Target)}
* on the playback thread:
*
* <ul>
* <li>Message with type {@link #MSG_SET_SURFACE} to set the output surface. The message payload
* should be the target {@link Surface}, or null.
* <li>Message with type {@link #MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER} to set the output
* buffer renderer. The message payload should be the target {@link
* VideoDecoderOutputBufferRenderer}, or null.
* <li>Message with type {@link #MSG_SET_VIDEO_FRAME_METADATA_LISTENER} to set a listener for
* metadata associated with frames being rendered. The message payload should be the {@link
* VideoFrameMetadataListener}, or null.
* </ul>
*/
public abstract class DecoderVideoRenderer extends BaseRenderer { public abstract class DecoderVideoRenderer extends BaseRenderer {
/** Decoder reinitialization states. */ /** Decoder reinitialization states. */
@ -84,6 +102,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
private VideoDecoderOutputBuffer outputBuffer; private VideoDecoderOutputBuffer outputBuffer;
@Nullable private Surface surface; @Nullable private Surface surface;
@Nullable private VideoDecoderOutputBufferRenderer outputBufferRenderer; @Nullable private VideoDecoderOutputBufferRenderer outputBufferRenderer;
@Nullable private VideoFrameMetadataListener frameMetadataListener;
@C.VideoOutputMode private int outputMode; @C.VideoOutputMode private int outputMode;
@Nullable private DrmSession<ExoMediaCrypto> decoderDrmSession; @Nullable private DrmSession<ExoMediaCrypto> decoderDrmSession;
@ -214,6 +233,21 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
} }
} }
// PlayerMessage.Target implementation.
@Override
public void handleMessage(int messageType, @Nullable Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) {
setOutputSurface((Surface) message);
} else if (messageType == MSG_SET_VIDEO_DECODER_OUTPUT_BUFFER_RENDERER) {
setOutputBufferRenderer((VideoDecoderOutputBufferRenderer) message);
} else if (messageType == MSG_SET_VIDEO_FRAME_METADATA_LISTENER) {
frameMetadataListener = (VideoFrameMetadataListener) message;
} else {
super.handleMessage(messageType, message);
}
}
// Protected methods. // Protected methods.
@Override @Override
@ -507,6 +541,10 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
protected void renderOutputBuffer( protected void renderOutputBuffer(
VideoDecoderOutputBuffer outputBuffer, long presentationTimeUs, Format outputFormat) VideoDecoderOutputBuffer outputBuffer, long presentationTimeUs, Format outputFormat)
throws DecoderException { throws DecoderException {
if (frameMetadataListener != null) {
frameMetadataListener.onVideoFrameAboutToBeRendered(
presentationTimeUs, System.nanoTime(), outputFormat, /* mediaFormat= */ null);
}
lastRenderTimeUs = C.msToUs(SystemClock.elapsedRealtime() * 1000); lastRenderTimeUs = C.msToUs(SystemClock.elapsedRealtime() * 1000);
int bufferMode = outputBuffer.mode; int bufferMode = outputBuffer.mode;
boolean renderSurface = bufferMode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && surface != null; boolean renderSurface = bufferMode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && surface != null;

View File

@ -73,6 +73,9 @@ import java.util.List;
* payload should be one of the integer scaling modes in {@link VideoScalingMode}. Note that * payload should be one of the integer scaling modes in {@link VideoScalingMode}. Note that
* the scaling mode only applies if the {@link Surface} targeted by this renderer is owned by * the scaling mode only applies if the {@link Surface} targeted by this renderer is owned by
* a {@link android.view.SurfaceView}. * a {@link android.view.SurfaceView}.
* <li>Message with type {@link #MSG_SET_VIDEO_FRAME_METADATA_LISTENER} to set a listener for
* metadata associated with frames being rendered. The message payload should be the {@link
* VideoFrameMetadataListener}, or null.
* </ul> * </ul>
*/ */
public class MediaCodecVideoRenderer extends MediaCodecRenderer { public class MediaCodecVideoRenderer extends MediaCodecRenderer {

View File

@ -19,14 +19,14 @@ import android.media.MediaFormat;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
/** A listener for metadata corresponding to video frame being rendered. */ /** A listener for metadata corresponding to video frames being rendered. */
public interface VideoFrameMetadataListener { public interface VideoFrameMetadataListener {
/** /**
* Called when the video frame about to be rendered. This method is called on the playback thread. * Called on the playback thread when a video frame is about to be rendered.
* *
* @param presentationTimeUs The presentation time of the output buffer, in microseconds. * @param presentationTimeUs The presentation time of the frame, in microseconds.
* @param releaseTimeNs The wallclock time at which the frame should be displayed, in nanoseconds. * @param releaseTimeNs The wallclock time at which the frame should be displayed, in nanoseconds.
* If the platform API version of the device is less than 21, then this is the best effort. * If the platform API version of the device is less than 21, then this is a best effort.
* @param format The format associated with the frame. * @param format The format associated with the frame.
* @param mediaFormat The framework media format associated with the frame, or {@code null} if not * @param mediaFormat The framework media format associated with the frame, or {@code null} if not
* known or not applicable (e.g., because the frame was not output by a {@link * known or not applicable (e.g., because the frame was not output by a {@link