Commit 960422e3 by hschlueter Committed by Marc Baechinger

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
parent c3866449
...@@ -761,7 +761,7 @@ public final class Transformer { ...@@ -761,7 +761,7 @@ public final class Transformer {
encoderFactory, encoderFactory,
decoderFactory, decoderFactory,
new FallbackListener(mediaItem, listeners, transformationRequest), new FallbackListener(mediaItem, listeners, transformationRequest),
playerListener, /* asyncErrorListener= */ playerListener,
debugViewProvider)) debugViewProvider))
.setMediaSourceFactory(mediaSourceFactory) .setMediaSourceFactory(mediaSourceFactory)
.setTrackSelector(trackSelector) .setTrackSelector(trackSelector)
...@@ -875,7 +875,7 @@ public final class Transformer { ...@@ -875,7 +875,7 @@ public final class Transformer {
private final Codec.EncoderFactory encoderFactory; private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory; private final Codec.DecoderFactory decoderFactory;
private final FallbackListener fallbackListener; private final FallbackListener fallbackListener;
private final FrameProcessorChain.Listener frameProcessorChainListener; private final AsyncErrorListener asyncErrorListener;
private final Transformer.DebugViewProvider debugViewProvider; private final Transformer.DebugViewProvider debugViewProvider;
public TransformerRenderersFactory( public TransformerRenderersFactory(
...@@ -889,7 +889,7 @@ public final class Transformer { ...@@ -889,7 +889,7 @@ public final class Transformer {
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
FallbackListener fallbackListener, FallbackListener fallbackListener,
FrameProcessorChain.Listener frameProcessorChainListener, AsyncErrorListener asyncErrorListener,
Transformer.DebugViewProvider debugViewProvider) { Transformer.DebugViewProvider debugViewProvider) {
this.context = context; this.context = context;
this.muxerWrapper = muxerWrapper; this.muxerWrapper = muxerWrapper;
...@@ -901,7 +901,7 @@ public final class Transformer { ...@@ -901,7 +901,7 @@ public final class Transformer {
this.encoderFactory = encoderFactory; this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
this.fallbackListener = fallbackListener; this.fallbackListener = fallbackListener;
this.frameProcessorChainListener = frameProcessorChainListener; this.asyncErrorListener = asyncErrorListener;
this.debugViewProvider = debugViewProvider; this.debugViewProvider = debugViewProvider;
mediaClock = new TransformerMediaClock(); mediaClock = new TransformerMediaClock();
} }
...@@ -924,6 +924,7 @@ public final class Transformer { ...@@ -924,6 +924,7 @@ public final class Transformer {
transformationRequest, transformationRequest,
encoderFactory, encoderFactory,
decoderFactory, decoderFactory,
asyncErrorListener,
fallbackListener); fallbackListener);
index++; index++;
} }
...@@ -938,8 +939,8 @@ public final class Transformer { ...@@ -938,8 +939,8 @@ public final class Transformer {
videoFrameEffects, videoFrameEffects,
encoderFactory, encoderFactory,
decoderFactory, decoderFactory,
asyncErrorListener,
fallbackListener, fallbackListener,
frameProcessorChainListener,
debugViewProvider); debugViewProvider);
index++; index++;
} }
...@@ -947,8 +948,7 @@ public final class Transformer { ...@@ -947,8 +948,7 @@ public final class Transformer {
} }
} }
private final class TransformerPlayerListener private final class TransformerPlayerListener implements Player.Listener, AsyncErrorListener {
implements Player.Listener, FrameProcessorChain.Listener {
private final MediaItem mediaItem; private final MediaItem mediaItem;
private final MuxerWrapper muxerWrapper; private final MuxerWrapper muxerWrapper;
...@@ -999,11 +999,12 @@ public final class Transformer { ...@@ -999,11 +999,12 @@ public final class Transformer {
@Override @Override
public void onPlayerError(PlaybackException error) { public void onPlayerError(PlaybackException error) {
@Nullable Throwable cause = error.getCause();
TransformationException transformationException = TransformationException transformationException =
cause instanceof TransformationException TransformationException.createForPlaybackException(error);
? (TransformationException) cause handleTransformationException(transformationException);
: TransformationException.createForPlaybackException(error); }
private void handleTransformationException(TransformationException transformationException) {
if (isCancelling) { if (isCancelling) {
// Resources are already being released. // Resources are already being released.
listeners.queueEvent( listeners.queueEvent(
...@@ -1055,12 +1056,22 @@ public final class Transformer { ...@@ -1055,12 +1056,22 @@ public final class Transformer {
} }
@Override @Override
public void onFrameProcessingError(FrameProcessingException exception) { public void onTransformationException(TransformationException exception) {
handler.post( if (Looper.myLooper() == looper) {
() -> handleTransformationException(exception);
handleTransformationEnded( } else {
TransformationException.createForFrameProcessorChain( handler.post(() -> handleTransformationException(exception));
exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED))); }
} }
} }
/** Listener for exceptions that occur during a transformation. */
/* package */ interface AsyncErrorListener {
/**
* Called when a {@link TransformationException} occurs.
*
* <p>Can be called from any thread.
*/
void onTransformationException(TransformationException exception);
}
} }
...@@ -42,8 +42,15 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; ...@@ -42,8 +42,15 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
Transformer.AsyncErrorListener asyncErrorListener,
FallbackListener fallbackListener) { 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.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
decoderInputBuffer = decoderInputBuffer =
......
...@@ -21,9 +21,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull; ...@@ -21,9 +21,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import androidx.annotation.Nullable; 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.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
...@@ -39,11 +37,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -39,11 +37,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
protected final MuxerWrapper muxerWrapper; protected final MuxerWrapper muxerWrapper;
protected final TransformerMediaClock mediaClock; protected final TransformerMediaClock mediaClock;
protected final TransformationRequest transformationRequest; protected final TransformationRequest transformationRequest;
protected final Transformer.AsyncErrorListener asyncErrorListener;
protected final FallbackListener fallbackListener; protected final FallbackListener fallbackListener;
protected boolean isRendererStarted; private boolean isTransformationRunning;
protected boolean muxerWrapperTrackAdded; private boolean muxerWrapperTrackAdded;
protected boolean muxerWrapperTrackEnded; private boolean muxerWrapperTrackEnded;
protected long streamOffsetUs; protected long streamOffsetUs;
protected long streamStartPositionUs; protected long streamStartPositionUs;
protected @MonotonicNonNull SamplePipeline samplePipeline; protected @MonotonicNonNull SamplePipeline samplePipeline;
...@@ -53,11 +52,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -53,11 +52,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
MuxerWrapper muxerWrapper, MuxerWrapper muxerWrapper,
TransformerMediaClock mediaClock, TransformerMediaClock mediaClock,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
Transformer.AsyncErrorListener asyncErrorListener,
FallbackListener fallbackListener) { FallbackListener fallbackListener) {
super(trackType); super(trackType);
this.muxerWrapper = muxerWrapper; this.muxerWrapper = muxerWrapper;
this.mediaClock = mediaClock; this.mediaClock = mediaClock;
this.transformationRequest = transformationRequest; this.transformationRequest = transformationRequest;
this.asyncErrorListener = asyncErrorListener;
this.fallbackListener = fallbackListener; this.fallbackListener = fallbackListener;
} }
...@@ -91,17 +92,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -91,17 +92,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
@Override @Override
public final void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { public final void render(long positionUs, long elapsedRealtimeUs) {
try { try {
if (!isRendererStarted || isEnded() || !ensureConfigured()) { if (!isTransformationRunning || isEnded() || !ensureConfigured()) {
return; return;
} }
while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {} while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {}
} catch (TransformationException e) { } catch (TransformationException e) {
throw wrapTransformationException(e); isTransformationRunning = false;
asyncErrorListener.onTransformationException(e);
} catch (Muxer.MuxerException e) { } catch (Muxer.MuxerException e) {
throw wrapTransformationException( isTransformationRunning = false;
asyncErrorListener.onTransformationException(
TransformationException.createForMuxer( TransformationException.createForMuxer(
e, TransformationException.ERROR_CODE_MUXING_FAILED)); e, TransformationException.ERROR_CODE_MUXING_FAILED));
} }
...@@ -122,12 +125,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -122,12 +125,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@Override @Override
protected final void onStarted() { protected final void onStarted() {
isRendererStarted = true; isTransformationRunning = true;
} }
@Override @Override
protected final void onStopped() { protected final void onStopped() {
isRendererStarted = false; isTransformationRunning = false;
} }
@Override @Override
...@@ -225,23 +228,4 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -225,23 +228,4 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return false; return false;
} }
} }
/**
* Returns an {@link ExoPlaybackException} wrapping the {@link TransformationException}.
*
* <p>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);
}
} }
...@@ -39,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -39,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final ImmutableList<GlEffect> effects; private final ImmutableList<GlEffect> effects;
private final Codec.EncoderFactory encoderFactory; private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory; private final Codec.DecoderFactory decoderFactory;
private final FrameProcessorChain.Listener frameProcessorChainListener;
private final Transformer.DebugViewProvider debugViewProvider; private final Transformer.DebugViewProvider debugViewProvider;
private final DecoderInputBuffer decoderInputBuffer; private final DecoderInputBuffer decoderInputBuffer;
...@@ -54,16 +53,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -54,16 +53,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
ImmutableList<GlEffect> effects, ImmutableList<GlEffect> effects,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
Transformer.AsyncErrorListener asyncErrorListener,
FallbackListener fallbackListener, FallbackListener fallbackListener,
FrameProcessorChain.Listener frameProcessorChainListener,
Transformer.DebugViewProvider debugViewProvider) { 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.context = context;
this.clippingStartsAtKeyFrame = clippingStartsAtKeyFrame; this.clippingStartsAtKeyFrame = clippingStartsAtKeyFrame;
this.effects = effects; this.effects = effects;
this.encoderFactory = encoderFactory; this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
this.frameProcessorChainListener = frameProcessorChainListener;
this.debugViewProvider = debugViewProvider; this.debugViewProvider = debugViewProvider;
decoderInputBuffer = decoderInputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
...@@ -102,7 +106,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -102,7 +106,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
encoderFactory, encoderFactory,
muxerWrapper.getSupportedSampleMimeTypes(getTrackType()), muxerWrapper.getSupportedSampleMimeTypes(getTrackType()),
fallbackListener, fallbackListener,
frameProcessorChainListener, /* frameProcessorChainListener= */ exception ->
asyncErrorListener.onTransformationException(
TransformationException.createForFrameProcessorChain(
exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED)),
debugViewProvider); debugViewProvider);
} }
if (transformationRequest.flattenForSlowMotion) { if (transformationRequest.flattenForSlowMotion) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment