Remove ExoPlaybackException.TYPE_OUT_OF_MEMORY

Catching OOM errors is bad practise unless there is a specific known
cause that tried to allocate a large amount of memory. Without this
known cause with a large allocation, the source of the error is
likely somewhere else in the app and every random small further
allocation may lead to additional OOM errors (for example b/145134199).

We have three known causes in ExoPlayer:
 1. Source allocations based on unexpected values in streams. This is
    caught on the loader thread and reported as an
    UnexpectedLoaderException.
 2. Output buffer allocations by non-MediaCodec decoders. These are
    caught in SimpleDecoder on the decoder thread and reported as
    UnexpectedDecodeException.
 3. Input buffer allocations by non-MediaCodc decoders in their
    constructors. These are currently caught on a higher-level and
    reported as ExoPlaybackException.TYPE_OUT_OF_MEMORY.

For consistency and to prevent catching OOM errors without known cause
we can remove the generic TYPE_OUT_OF_MEMORY and catch the specific
exception where it occurs to report it as an
ExoPlaybackException.TYPE_RENDERER. This also has the added advantage
that the format metadata is added to the exception.

PiperOrigin-RevId: 351326688
This commit is contained in:
tonihei 2021-01-12 10:00:28 +00:00 committed by Oliver Woodman
parent b8b9a6411d
commit c63f3d92ba
6 changed files with 14 additions and 48 deletions

View File

@ -42,6 +42,7 @@
creating subtitle media sources from
`MediaItem.playbackProperties.subtitles`
([#8430](https://github.com/google/ExoPlayer/issues/8430)).
* Remove `ExoPlaybackException.OutOfMemoryError`.
* Extractors:
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
allow decoder capability checks based on codec profile/level

View File

@ -34,20 +34,12 @@ public final class ExoPlaybackException extends Exception {
/**
* The type of source that produced the error. One of {@link #TYPE_SOURCE}, {@link #TYPE_RENDERER}
* {@link #TYPE_UNEXPECTED}, {@link #TYPE_REMOTE}, {@link #TYPE_OUT_OF_MEMORY} or {@link
* #TYPE_TIMEOUT}. Note that new types may be added in the future and error handling should handle
* unknown type values.
* {@link #TYPE_UNEXPECTED}, {@link #TYPE_REMOTE} or {@link #TYPE_TIMEOUT}. Note that new types
* may be added in the future and error handling should handle unknown type values.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
TYPE_SOURCE,
TYPE_RENDERER,
TYPE_UNEXPECTED,
TYPE_REMOTE,
TYPE_OUT_OF_MEMORY,
TYPE_TIMEOUT
})
@IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED, TYPE_REMOTE, TYPE_TIMEOUT})
public @interface Type {}
/**
* The error occurred loading data from a {@code MediaSource}.
@ -75,10 +67,9 @@ public final class ExoPlaybackException extends Exception {
* <p>Call {@link #getMessage()} to retrieve the message associated with the error.
*/
public static final int TYPE_REMOTE = 3;
/** The error was an {@link OutOfMemoryError}. */
public static final int TYPE_OUT_OF_MEMORY = 4;
/** The error was a {@link TimeoutException}. */
public static final int TYPE_TIMEOUT = 5;
public static final int TYPE_TIMEOUT = 4;
/** The {@link Type} of the playback failure. */
@Type public final int type;
@ -175,7 +166,7 @@ public final class ExoPlaybackException extends Exception {
* @return The created instance.
*/
public static ExoPlaybackException createForRenderer(
Exception cause,
Throwable cause,
String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@ -202,7 +193,7 @@ public final class ExoPlaybackException extends Exception {
* @return The created instance.
*/
public static ExoPlaybackException createForRenderer(
Exception cause,
Throwable cause,
String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@ -240,16 +231,6 @@ public final class ExoPlaybackException extends Exception {
return new ExoPlaybackException(TYPE_REMOTE, message);
}
/**
* Creates an instance of type {@link #TYPE_OUT_OF_MEMORY}.
*
* @param cause The cause of the failure.
* @return The created instance.
*/
public static ExoPlaybackException createForOutOfMemory(OutOfMemoryError cause) {
return new ExoPlaybackException(TYPE_OUT_OF_MEMORY, cause);
}
/**
* Creates an instance of type {@link #TYPE_TIMEOUT}.
*
@ -382,16 +363,6 @@ public final class ExoPlaybackException extends Exception {
return (RuntimeException) Assertions.checkNotNull(cause);
}
/**
* Retrieves the underlying error when {@link #type} is {@link #TYPE_OUT_OF_MEMORY}.
*
* @throws IllegalStateException If {@link #type} is not {@link #TYPE_OUT_OF_MEMORY}.
*/
public OutOfMemoryError getOutOfMemoryError() {
Assertions.checkState(type == TYPE_OUT_OF_MEMORY);
return (OutOfMemoryError) Assertions.checkNotNull(cause);
}
/**
* Retrieves the underlying error when {@link #type} is {@link #TYPE_TIMEOUT}.
*
@ -451,9 +422,6 @@ public final class ExoPlaybackException extends Exception {
case TYPE_REMOTE:
message = "Remote error";
break;
case TYPE_OUT_OF_MEMORY:
message = "Out of memory error";
break;
case TYPE_TIMEOUT:
message = "Timeout error";
break;

View File

@ -340,7 +340,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* @param format The current format used by the renderer. May be null.
*/
protected final ExoPlaybackException createRendererException(
Exception cause, @Nullable Format format) {
Throwable cause, @Nullable Format format) {
return createRendererException(cause, format, /* isRecoverable= */ false);
}
@ -353,7 +353,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* @param isRecoverable If the error is recoverable by disabling and re-enabling the renderer.
*/
protected final ExoPlaybackException createRendererException(
Exception cause, @Nullable Format format, boolean isRecoverable) {
Throwable cause, @Nullable Format format, boolean isRecoverable) {
@C.FormatSupport int formatSupport = C.FORMAT_HANDLED;
if (format != null && !throwRendererExceptionIsExecuting) {
// Prevent recursive re-entry from subclass supportsFormat implementations.

View File

@ -582,11 +582,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
stopInternal(/* forceResetRenderers= */ false, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(error);
maybeNotifyPlaybackInfoChanged();
} catch (RuntimeException | OutOfMemoryError e) {
ExoPlaybackException error =
e instanceof OutOfMemoryError
? ExoPlaybackException.createForOutOfMemory((OutOfMemoryError) e)
: ExoPlaybackException.createForUnexpected((RuntimeException) e);
} catch (RuntimeException e) {
ExoPlaybackException error = ExoPlaybackException.createForUnexpected(e);
Log.e(TAG, "Playback error", error);
stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(error);

View File

@ -619,7 +619,7 @@ public abstract class DecoderAudioRenderer<
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp);
decoderCounters.decoderInitCount++;
} catch (DecoderException e) {
} catch (DecoderException | OutOfMemoryError e) {
throw createRendererException(e, inputFormat);
}
}

View File

@ -707,7 +707,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
decoderInitializedTimestamp,
decoderInitializedTimestamp - decoderInitializingTimestamp);
decoderCounters.decoderInitCount++;
} catch (DecoderException e) {
} catch (DecoderException | OutOfMemoryError e) {
throw createRendererException(e, inputFormat);
}
}