From a04cc94afd2c0f71eb9cb057851d8a9fd806a0e2 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 31 May 2022 17:01:02 +0000 Subject: [PATCH] Add async error listener to transformer to avoid exception wrapping. This internal listener avoids wrapping the TransformationExceptions in PlaybackExceptions that are handled via the Player.Listener and is also used for FrameProcessingExceptions which already avoided the PlaybackException layer previously. This listener will also be useful in follow-ups for encoder-related TransformationExceptions that are thrown in the SurfaceProvider that will be called on the GL thread. PiperOrigin-RevId: 452074575 (cherry picked from commit 960422e36f22a4c76e0617672aac9b9bcef50f43) --- .../exoplayer2/transformer/Transformer.java | 45 ++++++++++++------- .../transformer/TransformerAudioRenderer.java | 9 +++- .../transformer/TransformerBaseRenderer.java | 44 ++++++------------ .../transformer/TransformerVideoRenderer.java | 17 ++++--- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 0440e7e045..a7919ab28d 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -761,7 +761,7 @@ public final class Transformer { encoderFactory, decoderFactory, new FallbackListener(mediaItem, listeners, transformationRequest), - playerListener, + /* asyncErrorListener= */ playerListener, debugViewProvider)) .setMediaSourceFactory(mediaSourceFactory) .setTrackSelector(trackSelector) @@ -875,7 +875,7 @@ public final class Transformer { private final Codec.EncoderFactory encoderFactory; private final Codec.DecoderFactory decoderFactory; private final FallbackListener fallbackListener; - private final FrameProcessorChain.Listener frameProcessorChainListener; + private final AsyncErrorListener asyncErrorListener; private final Transformer.DebugViewProvider debugViewProvider; public TransformerRenderersFactory( @@ -889,7 +889,7 @@ public final class Transformer { Codec.EncoderFactory encoderFactory, Codec.DecoderFactory decoderFactory, FallbackListener fallbackListener, - FrameProcessorChain.Listener frameProcessorChainListener, + AsyncErrorListener asyncErrorListener, Transformer.DebugViewProvider debugViewProvider) { this.context = context; this.muxerWrapper = muxerWrapper; @@ -901,7 +901,7 @@ public final class Transformer { this.encoderFactory = encoderFactory; this.decoderFactory = decoderFactory; this.fallbackListener = fallbackListener; - this.frameProcessorChainListener = frameProcessorChainListener; + this.asyncErrorListener = asyncErrorListener; this.debugViewProvider = debugViewProvider; mediaClock = new TransformerMediaClock(); } @@ -924,6 +924,7 @@ public final class Transformer { transformationRequest, encoderFactory, decoderFactory, + asyncErrorListener, fallbackListener); index++; } @@ -938,8 +939,8 @@ public final class Transformer { videoFrameEffects, encoderFactory, decoderFactory, + asyncErrorListener, fallbackListener, - frameProcessorChainListener, debugViewProvider); index++; } @@ -947,8 +948,7 @@ public final class Transformer { } } - private final class TransformerPlayerListener - implements Player.Listener, FrameProcessorChain.Listener { + private final class TransformerPlayerListener implements Player.Listener, AsyncErrorListener { private final MediaItem mediaItem; private final MuxerWrapper muxerWrapper; @@ -999,11 +999,12 @@ public final class Transformer { @Override public void onPlayerError(PlaybackException error) { - @Nullable Throwable cause = error.getCause(); TransformationException transformationException = - cause instanceof TransformationException - ? (TransformationException) cause - : TransformationException.createForPlaybackException(error); + TransformationException.createForPlaybackException(error); + handleTransformationException(transformationException); + } + + private void handleTransformationException(TransformationException transformationException) { if (isCancelling) { // Resources are already being released. listeners.queueEvent( @@ -1055,12 +1056,22 @@ public final class Transformer { } @Override - public void onFrameProcessingError(FrameProcessingException exception) { - handler.post( - () -> - handleTransformationEnded( - TransformationException.createForFrameProcessorChain( - exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED))); + public void onTransformationException(TransformationException exception) { + if (Looper.myLooper() == looper) { + handleTransformationException(exception); + } else { + handler.post(() -> handleTransformationException(exception)); + } } } + + /** Listener for exceptions that occur during a transformation. */ + /* package */ interface AsyncErrorListener { + /** + * Called when a {@link TransformationException} occurs. + * + *

Can be called from any thread. + */ + void onTransformationException(TransformationException exception); + } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java index 272bf81091..82e9698c4f 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java @@ -42,8 +42,15 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; TransformationRequest transformationRequest, Codec.EncoderFactory encoderFactory, Codec.DecoderFactory decoderFactory, + Transformer.AsyncErrorListener asyncErrorListener, FallbackListener fallbackListener) { - super(C.TRACK_TYPE_AUDIO, muxerWrapper, mediaClock, transformationRequest, fallbackListener); + super( + C.TRACK_TYPE_AUDIO, + muxerWrapper, + mediaClock, + transformationRequest, + asyncErrorListener, + fallbackListener); this.encoderFactory = encoderFactory; this.decoderFactory = decoderFactory; decoderInputBuffer = diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java index 1ade3ff98a..0e05a16f2f 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java @@ -21,9 +21,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull; import androidx.annotation.Nullable; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; @@ -39,11 +37,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; protected final MuxerWrapper muxerWrapper; protected final TransformerMediaClock mediaClock; protected final TransformationRequest transformationRequest; + protected final Transformer.AsyncErrorListener asyncErrorListener; protected final FallbackListener fallbackListener; - protected boolean isRendererStarted; - protected boolean muxerWrapperTrackAdded; - protected boolean muxerWrapperTrackEnded; + private boolean isTransformationRunning; + private boolean muxerWrapperTrackAdded; + private boolean muxerWrapperTrackEnded; protected long streamOffsetUs; protected long streamStartPositionUs; protected @MonotonicNonNull SamplePipeline samplePipeline; @@ -53,11 +52,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; MuxerWrapper muxerWrapper, TransformerMediaClock mediaClock, TransformationRequest transformationRequest, + Transformer.AsyncErrorListener asyncErrorListener, FallbackListener fallbackListener) { super(trackType); this.muxerWrapper = muxerWrapper; this.mediaClock = mediaClock; this.transformationRequest = transformationRequest; + this.asyncErrorListener = asyncErrorListener; this.fallbackListener = fallbackListener; } @@ -91,17 +92,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } @Override - public final void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public final void render(long positionUs, long elapsedRealtimeUs) { try { - if (!isRendererStarted || isEnded() || !ensureConfigured()) { + if (!isTransformationRunning || isEnded() || !ensureConfigured()) { return; } while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {} } catch (TransformationException e) { - throw wrapTransformationException(e); + isTransformationRunning = false; + asyncErrorListener.onTransformationException(e); } catch (Muxer.MuxerException e) { - throw wrapTransformationException( + isTransformationRunning = false; + asyncErrorListener.onTransformationException( TransformationException.createForMuxer( e, TransformationException.ERROR_CODE_MUXING_FAILED)); } @@ -122,12 +125,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; @Override protected final void onStarted() { - isRendererStarted = true; + isTransformationRunning = true; } @Override protected final void onStopped() { - isRendererStarted = false; + isTransformationRunning = false; } @Override @@ -225,23 +228,4 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return false; } } - - /** - * Returns an {@link ExoPlaybackException} wrapping the {@link TransformationException}. - * - *

This temporary wrapping is needed due to the dependence on ExoPlayer's BaseRenderer. {@link - * Transformer} extracts the {@link TransformationException} from this {@link - * ExoPlaybackException} again. - */ - private ExoPlaybackException wrapTransformationException( - TransformationException transformationException) { - return ExoPlaybackException.createForRenderer( - transformationException, - "Transformer", - getIndex(), - /* rendererFormat= */ null, - C.FORMAT_HANDLED, - /* isRecoverable= */ false, - PlaybackException.ERROR_CODE_UNSPECIFIED); - } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java index a0229b681d..a4f941b800 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java @@ -39,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final ImmutableList effects; private final Codec.EncoderFactory encoderFactory; private final Codec.DecoderFactory decoderFactory; - private final FrameProcessorChain.Listener frameProcessorChainListener; private final Transformer.DebugViewProvider debugViewProvider; private final DecoderInputBuffer decoderInputBuffer; @@ -54,16 +53,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ImmutableList effects, Codec.EncoderFactory encoderFactory, Codec.DecoderFactory decoderFactory, + Transformer.AsyncErrorListener asyncErrorListener, FallbackListener fallbackListener, - FrameProcessorChain.Listener frameProcessorChainListener, Transformer.DebugViewProvider debugViewProvider) { - super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformationRequest, fallbackListener); + super( + C.TRACK_TYPE_VIDEO, + muxerWrapper, + mediaClock, + transformationRequest, + asyncErrorListener, + fallbackListener); this.context = context; this.clippingStartsAtKeyFrame = clippingStartsAtKeyFrame; this.effects = effects; this.encoderFactory = encoderFactory; this.decoderFactory = decoderFactory; - this.frameProcessorChainListener = frameProcessorChainListener; this.debugViewProvider = debugViewProvider; decoderInputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); @@ -102,7 +106,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; encoderFactory, muxerWrapper.getSupportedSampleMimeTypes(getTrackType()), fallbackListener, - frameProcessorChainListener, + /* frameProcessorChainListener= */ exception -> + asyncErrorListener.onTransformationException( + TransformationException.createForFrameProcessorChain( + exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED)), debugViewProvider); } if (transformationRequest.flattenForSlowMotion) {