Add plumbing for reporting internal decoder errors
PiperOrigin-RevId: 357732695
This commit is contained in:
parent
e009322edd
commit
1a34de5954
@ -9,6 +9,8 @@
|
|||||||
* Audio:
|
* Audio:
|
||||||
* Fix `SimpleExoPlayer` reporting audio session ID as 0 in some cases
|
* Fix `SimpleExoPlayer` reporting audio session ID as 0 in some cases
|
||||||
([#8585](https://github.com/google/ExoPlayer/issues/8585)).
|
([#8585](https://github.com/google/ExoPlayer/issues/8585)).
|
||||||
|
* Analytics:
|
||||||
|
* Add `onAudioCodecError` and `onVideoCodecError` to `AnalyticsListener`.
|
||||||
* Library restructuring:
|
* Library restructuring:
|
||||||
* `DebugTextViewHelper` moved from `ui` package to `util` package.
|
* `DebugTextViewHelper` moved from `ui` package to `util` package.
|
||||||
* Spherical UI components moved from `video.spherical` package to
|
* Spherical UI components moved from `video.spherical` package to
|
||||||
|
@ -2098,6 +2098,11 @@ public class SimpleExoPlayer extends BasePlayer
|
|||||||
analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount);
|
analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoCodecError(Exception videoCodecError) {
|
||||||
|
analyticsCollector.onVideoCodecError(videoCodecError);
|
||||||
|
}
|
||||||
|
|
||||||
// AudioRendererEventListener implementation
|
// AudioRendererEventListener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -2156,6 +2161,11 @@ public class SimpleExoPlayer extends BasePlayer
|
|||||||
analyticsCollector.onAudioSinkError(audioSinkError);
|
analyticsCollector.onAudioSinkError(audioSinkError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioCodecError(Exception audioCodecError) {
|
||||||
|
analyticsCollector.onAudioCodecError(audioCodecError);
|
||||||
|
}
|
||||||
|
|
||||||
// TextOutput implementation
|
// TextOutput implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -309,6 +309,15 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onAudioSinkError(eventTime, audioSinkError));
|
listener -> listener.onAudioSinkError(eventTime, audioSinkError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onAudioCodecError(Exception audioCodecError) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_AUDIO_CODEC_ERROR,
|
||||||
|
listener -> listener.onAudioCodecError(eventTime, audioCodecError));
|
||||||
|
}
|
||||||
|
|
||||||
// Additional audio events.
|
// Additional audio events.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,6 +465,15 @@ public class AnalyticsCollector
|
|||||||
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
|
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onVideoCodecError(Exception videoCodecError) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_VIDEO_CODEC_ERROR,
|
||||||
|
listener -> listener.onVideoCodecError(eventTime, videoCodecError));
|
||||||
|
}
|
||||||
|
|
||||||
// Additional video events.
|
// Additional video events.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,8 @@ package com.google.android.exoplayer2.analytics;
|
|||||||
|
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
@ -35,6 +37,7 @@ import com.google.android.exoplayer2.Timeline;
|
|||||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||||
import com.google.android.exoplayer2.audio.AudioSink;
|
import com.google.android.exoplayer2.audio.AudioSink;
|
||||||
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.DecoderReuseEvaluation;
|
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||||
@ -198,6 +201,8 @@ public interface AnalyticsListener {
|
|||||||
EVENT_DRM_KEYS_REMOVED,
|
EVENT_DRM_KEYS_REMOVED,
|
||||||
EVENT_DRM_SESSION_RELEASED,
|
EVENT_DRM_SESSION_RELEASED,
|
||||||
EVENT_PLAYER_RELEASED,
|
EVENT_PLAYER_RELEASED,
|
||||||
|
EVENT_AUDIO_CODEC_ERROR,
|
||||||
|
EVENT_VIDEO_CODEC_ERROR,
|
||||||
})
|
})
|
||||||
@interface EventFlags {}
|
@interface EventFlags {}
|
||||||
/** {@link Player#getCurrentTimeline()} changed. */
|
/** {@link Player#getCurrentTimeline()} changed. */
|
||||||
@ -312,6 +317,10 @@ public interface AnalyticsListener {
|
|||||||
int EVENT_DRM_SESSION_RELEASED = 1035;
|
int EVENT_DRM_SESSION_RELEASED = 1035;
|
||||||
/** The player was released. */
|
/** The player was released. */
|
||||||
int EVENT_PLAYER_RELEASED = 1036;
|
int EVENT_PLAYER_RELEASED = 1036;
|
||||||
|
/** The audio codec encountered an error. */
|
||||||
|
int EVENT_AUDIO_CODEC_ERROR = 1037;
|
||||||
|
/** The video codec encountered an error. */
|
||||||
|
int EVENT_VIDEO_CODEC_ERROR = 1038;
|
||||||
|
|
||||||
/** Time information of an event. */
|
/** Time information of an event. */
|
||||||
final class EventTime {
|
final class EventTime {
|
||||||
@ -649,8 +658,14 @@ public interface AnalyticsListener {
|
|||||||
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {}
|
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a media source loading error occurred. These errors are just for informational
|
* Called when a media source loading error occurred.
|
||||||
* purposes and the player may recover.
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
*
|
*
|
||||||
* @param eventTime The event time.
|
* @param eventTime The event time.
|
||||||
* @param loadEventInfo The {@link LoadEventInfo} defining the load event.
|
* @param loadEventInfo The {@link LoadEventInfo} defining the load event.
|
||||||
@ -829,8 +844,14 @@ public interface AnalyticsListener {
|
|||||||
default void onSkipSilenceEnabledChanged(EventTime eventTime, boolean skipSilenceEnabled) {}
|
default void onSkipSilenceEnabledChanged(EventTime eventTime, boolean skipSilenceEnabled) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when {@link AudioSink} has encountered an error. These errors are just for informational
|
* Called when {@link AudioSink} has encountered an error.
|
||||||
* purposes and the player may recover.
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
*
|
*
|
||||||
* @param eventTime The event time.
|
* @param eventTime The event time.
|
||||||
* @param audioSinkError Either a {@link AudioSink.InitializationException} or a {@link
|
* @param audioSinkError Either a {@link AudioSink.InitializationException} or a {@link
|
||||||
@ -838,6 +859,22 @@ public interface AnalyticsListener {
|
|||||||
*/
|
*/
|
||||||
default void onAudioSinkError(EventTime eventTime, Exception audioSinkError) {}
|
default void onAudioSinkError(EventTime eventTime, Exception audioSinkError) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an audio decoder encounters an error.
|
||||||
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param audioCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
|
*/
|
||||||
|
default void onAudioCodecError(EventTime eventTime, Exception audioCodecError) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the volume changes.
|
* Called when the volume changes.
|
||||||
*
|
*
|
||||||
@ -931,6 +968,22 @@ public interface AnalyticsListener {
|
|||||||
default void onVideoFrameProcessingOffset(
|
default void onVideoFrameProcessingOffset(
|
||||||
EventTime eventTime, long totalProcessingOffsetUs, int frameCount) {}
|
EventTime eventTime, long totalProcessingOffsetUs, int frameCount) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a video decoder encounters an error.
|
||||||
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
|
*/
|
||||||
|
default void onVideoCodecError(EventTime eventTime, Exception videoCodecError) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a frame is rendered for the first time since setting the surface, or since the
|
* Called when a frame is rendered for the first time since setting the surface, or since the
|
||||||
* renderer was reset, or since the stream being rendered was changed.
|
* renderer was reset, or since the stream being rendered was changed.
|
||||||
@ -987,8 +1040,14 @@ public interface AnalyticsListener {
|
|||||||
default void onDrmKeysLoaded(EventTime eventTime) {}
|
default void onDrmKeysLoaded(EventTime eventTime) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a drm error occurs. These errors are just for informational purposes and the player
|
* Called when a drm error occurs.
|
||||||
* may recover.
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
*
|
*
|
||||||
* @param eventTime The event time.
|
* @param eventTime The event time.
|
||||||
* @param error The error.
|
* @param error The error.
|
||||||
|
@ -18,15 +18,17 @@ package com.google.android.exoplayer2.audio;
|
|||||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
|
|
||||||
import android.media.AudioTrack;
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
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.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Renderer;
|
import com.google.android.exoplayer2.Renderer;
|
||||||
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.DecoderReuseEvaluation;
|
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
|
||||||
@ -113,6 +115,21 @@ public interface AudioRendererEventListener {
|
|||||||
*/
|
*/
|
||||||
default void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {}
|
default void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an audio decoder encounters an error.
|
||||||
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
|
*
|
||||||
|
* @param audioCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
|
*/
|
||||||
|
default void onAudioCodecError(Exception audioCodecError) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when {@link AudioSink} has encountered an error.
|
* Called when {@link AudioSink} has encountered an error.
|
||||||
*
|
*
|
||||||
@ -120,18 +137,14 @@ public interface AudioRendererEventListener {
|
|||||||
* AudioTrack} errors.
|
* AudioTrack} errors.
|
||||||
*
|
*
|
||||||
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
* The player may be able to recover from the error (for example by recreating the AudioTrack,
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
* possibly with different settings) and continue. Hence applications should <em>not</em>
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
* implement this method to display a user visible error or initiate an application level retry
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
* ({@link Player.EventListener#onPlayerError} is the appropriate place to implement such
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
* behavior). This method is called to provide the application with an opportunity to log the
|
* wishes to do so.
|
||||||
* error if it wishes to do so.
|
|
||||||
*
|
*
|
||||||
* <p>Fatal errors that cannot be recovered will be reported wrapped in a {@link
|
* @param audioSinkError The error that occurred. Typically an {@link
|
||||||
* ExoPlaybackException} by {@link Player.EventListener#onPlayerError(ExoPlaybackException)}.
|
* AudioSink.InitializationException} or a {@link AudioSink.WriteException}.
|
||||||
*
|
|
||||||
* @param audioSinkError Either an {@link AudioSink.InitializationException} or a {@link
|
|
||||||
* AudioSink.WriteException} describing the error.
|
|
||||||
*/
|
*/
|
||||||
default void onAudioSinkError(Exception audioSinkError) {}
|
default void onAudioSinkError(Exception audioSinkError) {}
|
||||||
|
|
||||||
@ -230,5 +243,12 @@ public interface AudioRendererEventListener {
|
|||||||
handler.post(() -> castNonNull(listener).onAudioSinkError(audioSinkError));
|
handler.post(() -> castNonNull(listener).onAudioSinkError(audioSinkError));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Invokes {@link AudioRendererEventListener#onAudioCodecError(Exception)}. */
|
||||||
|
public void audioCodecError(Exception audioCodecError) {
|
||||||
|
if (handler != null) {
|
||||||
|
handler.post(() -> castNonNull(listener).onAudioCodecError(audioCodecError));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@ public abstract class DecoderAudioRenderer<
|
|||||||
while (feedInputBuffer()) {}
|
while (feedInputBuffer()) {}
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
} catch (DecoderException e) {
|
} catch (DecoderException e) {
|
||||||
|
eventDispatcher.audioCodecError(e);
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
} catch (AudioSink.ConfigurationException e) {
|
} catch (AudioSink.ConfigurationException e) {
|
||||||
throw createRendererException(e, e.format);
|
throw createRendererException(e, e.format);
|
||||||
@ -618,7 +619,10 @@ public abstract class DecoderAudioRenderer<
|
|||||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||||
decoderCounters.decoderInitCount++;
|
decoderCounters.decoderInitCount++;
|
||||||
} catch (DecoderException | OutOfMemoryError e) {
|
} catch (DecoderException e) {
|
||||||
|
eventDispatcher.audioCodecError(e);
|
||||||
|
throw createRendererException(e, inputFormat);
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,6 +414,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
eventDispatcher.decoderReleased(name);
|
eventDispatcher.decoderReleased(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCodecError(Exception codecError) {
|
||||||
|
eventDispatcher.audioCodecError(codecError);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
protected DecoderReuseEvaluation onInputFormatChanged(FormatHolder formatHolder)
|
protected DecoderReuseEvaluation onInputFormatChanged(FormatHolder formatHolder)
|
||||||
|
@ -848,6 +848,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
if (isMediaCodecException(e)) {
|
if (isMediaCodecException(e)) {
|
||||||
|
onCodecError(e);
|
||||||
boolean isRecoverable =
|
boolean isRecoverable =
|
||||||
enableRecoverableCodecExceptionRetries
|
enableRecoverableCodecExceptionRetries
|
||||||
&& Util.SDK_INT >= 21
|
&& Util.SDK_INT >= 21
|
||||||
@ -1406,6 +1407,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a codec error has occurred.
|
||||||
|
*
|
||||||
|
* <p>The default implementation is a no-op.
|
||||||
|
*
|
||||||
|
* @param codecError The error.
|
||||||
|
*/
|
||||||
|
protected void onCodecError(Exception codecError) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a new {@link Format} is read from the upstream {@link MediaPeriod}.
|
* Called when a new {@link Format} is read from the upstream {@link MediaPeriod}.
|
||||||
*
|
*
|
||||||
|
@ -92,11 +92,11 @@ public interface MediaSourceEventListener {
|
|||||||
* <em>not</em> be called in addition to this method.
|
* <em>not</em> be called in addition to this method.
|
||||||
*
|
*
|
||||||
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
* The player may be able to recover from the error and continue. Hence applications should
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
* <em>not</em> implement this method to display a user visible error or initiate an application
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
* level retry ({@link Player.EventListener#onPlayerError} is the appropriate place to implement
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
* such behavior). This method is called to provide the application with an opportunity to log the
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
* error if it wishes to do so.
|
* wishes to do so.
|
||||||
*
|
*
|
||||||
* @param windowIndex The window index in the timeline of the media source this load belongs to.
|
* @param windowIndex The window index in the timeline of the media source this load belongs to.
|
||||||
* @param mediaPeriodId The {@link MediaPeriodId} this load belongs to. Null if the load does not
|
* @param mediaPeriodId The {@link MediaPeriodId} this load belongs to. Null if the load does not
|
||||||
|
@ -206,6 +206,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
|||||||
while (feedInputBuffer()) {}
|
while (feedInputBuffer()) {}
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
} catch (DecoderException e) {
|
} catch (DecoderException e) {
|
||||||
|
eventDispatcher.videoCodecError(e);
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
}
|
}
|
||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
@ -707,7 +708,10 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
|||||||
decoderInitializedTimestamp,
|
decoderInitializedTimestamp,
|
||||||
decoderInitializedTimestamp - decoderInitializingTimestamp);
|
decoderInitializedTimestamp - decoderInitializingTimestamp);
|
||||||
decoderCounters.decoderInitCount++;
|
decoderCounters.decoderInitCount++;
|
||||||
} catch (DecoderException | OutOfMemoryError e) {
|
} catch (DecoderException e) {
|
||||||
|
eventDispatcher.videoCodecError(e);
|
||||||
|
throw createRendererException(e, inputFormat);
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -690,6 +690,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
eventDispatcher.decoderReleased(name);
|
eventDispatcher.decoderReleased(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCodecError(Exception codecError) {
|
||||||
|
eventDispatcher.videoCodecError(codecError);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
protected DecoderReuseEvaluation onInputFormatChanged(FormatHolder formatHolder)
|
protected DecoderReuseEvaluation onInputFormatChanged(FormatHolder formatHolder)
|
||||||
|
@ -17,14 +17,18 @@ package com.google.android.exoplayer2.video;
|
|||||||
|
|
||||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Renderer;
|
import com.google.android.exoplayer2.Renderer;
|
||||||
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.DecoderReuseEvaluation;
|
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
|
||||||
@ -147,8 +151,21 @@ public interface VideoRendererEventListener {
|
|||||||
default void onVideoDisabled(DecoderCounters counters) {}
|
default void onVideoDisabled(DecoderCounters counters) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches events to a {@link VideoRendererEventListener}.
|
* Called when a video decoder encounters an error.
|
||||||
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.EventListener#onPlayerError} is the appropriate place to implement such behavior.
|
||||||
|
* This method is called to provide the application with an opportunity to log the error if it
|
||||||
|
* wishes to do so.
|
||||||
|
*
|
||||||
|
* @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
*/
|
*/
|
||||||
|
default void onVideoCodecError(Exception videoCodecError) {}
|
||||||
|
|
||||||
|
/** Dispatches events to a {@link VideoRendererEventListener}. */
|
||||||
final class EventDispatcher {
|
final class EventDispatcher {
|
||||||
|
|
||||||
@Nullable private final Handler handler;
|
@Nullable private final Handler handler;
|
||||||
@ -254,6 +271,12 @@ public interface VideoRendererEventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Invokes {@link VideoRendererEventListener#onVideoCodecError(Exception)}. */
|
||||||
|
public void videoCodecError(Exception videoCodecError) {
|
||||||
|
if (handler != null) {
|
||||||
|
handler.post(() -> castNonNull(listener).onVideoCodecError(videoCodecError));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user