Bug 1517252 - p2: convert out-of-memory error to playback error to avoid crash. r=jya
authorJohn Lin <jolin@mozilla.com>
Fri, 18 Jan 2019 01:51:53 +0000
changeset 515197 e3dee65958a3ebe2c0f1fcec48bc896e5ffbefb6
parent 515196 bec07b59907f2dad66ce976e67cc5adc8bf961fa
child 515198 08a365b043d71ff7f2c0cd08bce4469151296f7e
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1517252
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1517252 - p2: convert out-of-memory error to playback error to avoid crash. r=jya Differential Revision: https://phabricator.services.mozilla.com/D16710
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsAudioRenderer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsRendererBase.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsVideoRenderer.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsAudioRenderer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsAudioRenderer.java
@@ -8,16 +8,17 @@ import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaCodec.CryptoInfo;
 import android.os.Build;
 import android.util.Log;
 
 import org.mozilla.geckoview.BuildConfig;
 
 import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlaybackException;
 import com.google.android.exoplayer2.Format;
 import com.google.android.exoplayer2.RendererCapabilities;
 import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
 import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
 import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
 import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
 import com.google.android.exoplayer2.util.MimeTypes;
 
@@ -97,17 +98,17 @@ public class GeckoHlsAudioRenderer exten
     }
 
     @Override
     protected void handleReconfiguration(DecoderInputBuffer bufferForRead) {
         // Do nothing
     }
 
     @Override
-    protected void handleFormatRead(DecoderInputBuffer bufferForRead) {
+    protected void handleFormatRead(DecoderInputBuffer bufferForRead) throws ExoPlaybackException {
         onInputFormatChanged(mFormatHolder.format);
     }
 
     @Override
     protected void handleEndOfStream(DecoderInputBuffer bufferForRead) {
         mInputStreamEnded = true;
         mDemuxedInputSamples.offer(GeckoHLSSample.EOS);
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsRendererBase.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsRendererBase.java
@@ -6,16 +6,17 @@ package org.mozilla.gecko.media;
 
 import android.util.Log;
 
 import org.mozilla.geckoview.BuildConfig;
 
 import com.google.android.exoplayer2.BaseRenderer;
 import com.google.android.exoplayer2.C;
 import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
+import com.google.android.exoplayer2.ExoPlaybackException;
 import com.google.android.exoplayer2.Format;
 import com.google.android.exoplayer2.FormatHolder;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.Iterator;
 
@@ -36,19 +37,19 @@ public abstract class GeckoHlsRendererBa
 
     protected ByteBuffer mInputBuffer = null;
     protected ArrayList<Format> mFormats = new ArrayList<Format>();
     protected boolean mInitialized = false;
     protected boolean mWaitingForData = true;
     protected boolean mInputStreamEnded = false;
     protected long mFirstSampleStartTime = Long.MIN_VALUE;
 
-    protected abstract void createInputBuffer();
+    protected abstract void createInputBuffer() throws ExoPlaybackException;
     protected abstract void handleReconfiguration(DecoderInputBuffer bufferForRead);
-    protected abstract void handleFormatRead(DecoderInputBuffer bufferForRead);
+    protected abstract void handleFormatRead(DecoderInputBuffer bufferForRead) throws ExoPlaybackException;
     protected abstract void handleEndOfStream(DecoderInputBuffer bufferForRead);
     protected abstract void handleSamplePreparation(DecoderInputBuffer bufferForRead);
     protected abstract void resetRenderer();
     protected abstract boolean clearInputSamplesQueue();
     protected abstract void notifyPlayerInputFormatChanged(Format newFormat);
 
     private DecoderInputBuffer mBufferForRead =
         new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
@@ -140,17 +141,17 @@ public abstract class GeckoHlsRendererBa
         // Referring to ExoPlayer's MediaCodec related renderers, only video
         // renderer handles this.
     }
 
     protected void updateCSDInfo(Format format) {
         // do nothing.
     }
 
-    protected void onInputFormatChanged(Format newFormat) {
+    protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackException {
         Format oldFormat;
         try {
             oldFormat = mFormats.get(mFormats.size() - 1);
         } catch (IndexOutOfBoundsException e) {
             oldFormat = null;
         }
         if (DEBUG) {
             Log.d(LOGTAG, "[onInputFormatChanged] old : " + oldFormat +
@@ -165,36 +166,40 @@ public abstract class GeckoHlsRendererBa
             resetRenderer();
             maybeInitRenderer();
         }
 
         updateCSDInfo(newFormat);
         notifyPlayerInputFormatChanged(newFormat);
     }
 
-    protected void maybeInitRenderer() {
+    protected void maybeInitRenderer() throws ExoPlaybackException {
         if (mInitialized || mFormats.size() == 0) {
             return;
         }
         if (DEBUG) { Log.d(LOGTAG, "Initializing ... "); }
-        createInputBuffer();
-        mInitialized = true;
+        try {
+            createInputBuffer();
+            mInitialized = true;
+        } catch (OutOfMemoryError e) {
+            throw ExoPlaybackException.createForRenderer(new RuntimeException(e), getIndex());
+        }
     }
 
     /*
      * The place we get demuxed data from HlsMediaSource(ExoPlayer).
      * The data will then be converted to GeckoHLSSample and deliver to
      * GeckoHlsDemuxerWrapper for further use.
      * If the return value is ture, that means a GeckoHLSSample is queued
      * successfully. We can try to feed more samples into queue.
      * If the return value is false, that means we might encounter following
      * situation 1) not initialized 2) input stream is ended 3) queue is full.
      * 4) format changed. 5) exception happened.
      */
-    protected synchronized boolean feedInputBuffersQueue() {
+    protected synchronized boolean feedInputBuffersQueue() throws ExoPlaybackException {
         if (!mInitialized || mInputStreamEnded || isQueuedEnoughData()) {
             // Need to reinitialize the renderer or the input stream has ended
             // or we just reached the maximum queue size.
             return false;
         }
 
         mBufferForRead.data = mInputBuffer;
         if (mBufferForRead.data != null) {
@@ -239,17 +244,17 @@ public abstract class GeckoHlsRendererBa
     private void maybeNotifyDataArrived() {
         if (mWaitingForData && isQueuedEnoughData()) {
             if (DEBUG) { Log.d(LOGTAG, "onDataArrived"); }
             mPlayerEventDispatcher.onDataArrived(getTrackType());
             mWaitingForData = false;
         }
     }
 
-    private void readFormat() {
+    private void readFormat() throws ExoPlaybackException {
         mflagsOnlyBuffer.clear();
         int result = readSource(mFormatHolder, mflagsOnlyBuffer, true);
         if (result == C.RESULT_FORMAT_READ) {
             onInputFormatChanged(mFormatHolder.format);
         }
     }
 
     @Override
@@ -283,17 +288,17 @@ public abstract class GeckoHlsRendererBa
     }
 
     /*
      * This is called by ExoPlayerImplInternal.java.
      * ExoPlayer checks the status of renderer, i.e. isReady() / isEnded(), and
      * calls renderer.render by passing its wall clock time.
      */
     @Override
-    public void render(long positionUs, long elapsedRealtimeUs) {
+    public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
         if (BuildConfig.DEBUG_BUILD) {
             Log.d(LOGTAG, "positionUs = " + positionUs +
                           ", mInputStreamEnded = " + mInputStreamEnded);
         }
         if (mInputStreamEnded) {
             return;
         }
         if (mFormats.size() == 0) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsVideoRenderer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsVideoRenderer.java
@@ -8,16 +8,17 @@ import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaCodec.CryptoInfo;
 import android.os.Build;
 import android.util.Log;
 
 import org.mozilla.geckoview.BuildConfig;
 
 import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlaybackException;
 import com.google.android.exoplayer2.Format;
 import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
 import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
 import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
 import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
 import com.google.android.exoplayer2.RendererCapabilities;
 import com.google.android.exoplayer2.util.MimeTypes;
 
@@ -129,27 +130,32 @@ public class GeckoHlsVideoRenderer exten
             RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
         int formatSupport = decoderCapable ?
             RendererCapabilities.FORMAT_HANDLED :
             RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES;
         return adaptiveSupport | formatSupport;
     }
 
     @Override
-    protected final void createInputBuffer() {
+    protected final void createInputBuffer() throws ExoPlaybackException {
         assertTrue(mFormats.size() > 0);
         // Calculate maximum size which might be used for target format.
         Format currentFormat = mFormats.get(mFormats.size() - 1);
         mCodecMaxValues = getCodecMaxValues(currentFormat, mStreamFormats);
         // Create a buffer with maximal size for reading source.
         // Note : Though we are able to dynamically enlarge buffer size by
         // creating DecoderInputBuffer with specific BufferReplacementMode, we
         // still allocate a calculated max size buffer for it at first to reduce
         // runtime overhead.
-        mInputBuffer = ByteBuffer.wrap(new byte[mCodecMaxValues.inputSize]);
+        try {
+            mInputBuffer = ByteBuffer.wrap(new byte[mCodecMaxValues.inputSize]);
+        } catch (OutOfMemoryError e) {
+            Log.e(LOGTAG, "cannot allocate input buffer of size " + mCodecMaxValues.inputSize, e);
+            throw ExoPlaybackException.createForRenderer(new Exception(e), getIndex());
+        }
     }
 
     @Override
     protected void resetRenderer() {
         if (DEBUG) { Log.d(LOGTAG, "[resetRenderer] mInitialized = " + mInitialized); }
         if (mInitialized) {
             mRendererReconfigured = false;
             mRendererReconfigurationState = RECONFIGURATION_STATE.NONE;
@@ -176,17 +182,17 @@ public class GeckoHlsVideoRenderer exten
                 byte[] data = currentFormat.initializationData.get(i);
                 bufferForRead.data.put(data);
             }
             mRendererReconfigurationState = RECONFIGURATION_STATE.QUEUE_PENDING;
         }
     }
 
     @Override
-    protected void handleFormatRead(DecoderInputBuffer bufferForRead) {
+    protected void handleFormatRead(DecoderInputBuffer bufferForRead) throws ExoPlaybackException {
         if (mRendererReconfigurationState == RECONFIGURATION_STATE.QUEUE_PENDING) {
             if (DEBUG) { Log.d(LOGTAG, "[feedInput][QUEUE_PENDING] 2 formats in a row."); }
             // We received two formats in a row. Clear the current buffer of any reconfiguration data
             // associated with the first format.
             bufferForRead.clear();
             mRendererReconfigurationState = RECONFIGURATION_STATE.WRITE_PENDING;
         }
         onInputFormatChanged(mFormatHolder.format);