mirror of
https://github.com/androidx/media.git
synced 2025-05-07 15:40:37 +08:00
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)
This commit is contained in:
parent
22ee071b53
commit
27f23041cf
@ -763,7 +763,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)
|
||||||
@ -877,7 +877,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(
|
||||||
@ -891,7 +891,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;
|
||||||
@ -903,7 +903,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();
|
||||||
}
|
}
|
||||||
@ -926,6 +926,7 @@ public final class Transformer {
|
|||||||
transformationRequest,
|
transformationRequest,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
decoderFactory,
|
decoderFactory,
|
||||||
|
asyncErrorListener,
|
||||||
fallbackListener);
|
fallbackListener);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@ -940,8 +941,8 @@ public final class Transformer {
|
|||||||
videoFrameEffects,
|
videoFrameEffects,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
decoderFactory,
|
decoderFactory,
|
||||||
|
asyncErrorListener,
|
||||||
fallbackListener,
|
fallbackListener,
|
||||||
frameProcessorChainListener,
|
|
||||||
debugViewProvider);
|
debugViewProvider);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@ -949,8 +950,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;
|
||||||
@ -1001,11 +1001,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(
|
||||||
@ -1057,12 +1058,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 androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
|||||||
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 =
|
||||||
|
@ -22,10 +22,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.PlaybackException;
|
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
import androidx.media3.exoplayer.BaseRenderer;
|
import androidx.media3.exoplayer.BaseRenderer;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
|
||||||
import androidx.media3.exoplayer.MediaClock;
|
import androidx.media3.exoplayer.MediaClock;
|
||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
||||||
@ -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;
|
|||||||
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;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
|||||||
|
|
||||||
@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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user