From 27f23041cf8133c9dee0603b7228de85e270ba8c 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 35b5147eb1404698132d9b063c905202c22e31d8) --- .../media3/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/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 94fa717381..db03b0e0ee 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -763,7 +763,7 @@ public final class Transformer { encoderFactory, decoderFactory, new FallbackListener(mediaItem, listeners, transformationRequest), - playerListener, + /* asyncErrorListener= */ playerListener, debugViewProvider)) .setMediaSourceFactory(mediaSourceFactory) .setTrackSelector(trackSelector) @@ -877,7 +877,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( @@ -891,7 +891,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; @@ -903,7 +903,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(); } @@ -926,6 +926,7 @@ public final class Transformer { transformationRequest, encoderFactory, decoderFactory, + asyncErrorListener, fallbackListener); index++; } @@ -940,8 +941,8 @@ public final class Transformer { videoFrameEffects, encoderFactory, decoderFactory, + asyncErrorListener, fallbackListener, - frameProcessorChainListener, debugViewProvider); index++; } @@ -949,8 +950,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; @@ -1001,11 +1001,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( @@ -1057,12 +1058,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/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java index 8180b199cd..b7bad84ae5 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java @@ -42,8 +42,15 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData; 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/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index b1da9c5ca3..4cde06566b 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -22,10 +22,8 @@ import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; -import androidx.media3.common.PlaybackException; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.exoplayer.BaseRenderer; -import androidx.media3.exoplayer.ExoPlaybackException; import androidx.media3.exoplayer.MediaClock; import androidx.media3.exoplayer.RendererCapabilities; import androidx.media3.exoplayer.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/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java index 086f0bfc17..6ea492fcb9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/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) {