| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.media; |
| |
| import android.media.MediaPlayer; |
| import android.os.SystemClock; |
| |
| import org.chromium.base.ContextUtils; |
| import org.chromium.base.Log; |
| import org.chromium.base.annotations.CalledByNative; |
| import org.chromium.base.annotations.JNINamespace; |
| import org.chromium.base.annotations.NativeMethods; |
| |
| /** |
| * Class for listening to Android MediaServer crashes to throttle media decoding |
| * when needed. |
| */ |
| @JNINamespace("media") |
| public class MediaServerCrashListener implements MediaPlayer.OnErrorListener { |
| private static final String TAG = "crMediaCrashListener"; |
| private static final long UNKNOWN_TIME = -1; |
| |
| // Watchdog player. Used to listen to all media server crashes. |
| private MediaPlayer mPlayer; |
| |
| // Protecting the creation/release of the watchdog player. |
| private final Object mLock = new Object(); |
| |
| // Approximate time necessary for the MediaServer to restart after a crash. |
| private static final int APPROX_MEDIA_SERVER_RESTART_TIME_IN_MS = 5000; |
| |
| // The last time we reported a failure to create the watchdog as a server crash. |
| private long mLastReportedWatchdogCreationFailure = UNKNOWN_TIME; |
| |
| private long mNativeMediaServerCrashListener; |
| |
| @CalledByNative |
| private static MediaServerCrashListener create(long nativeMediaServerCrashListener) { |
| return new MediaServerCrashListener(nativeMediaServerCrashListener); |
| } |
| |
| private MediaServerCrashListener(long nativeMediaServerCrashListener) { |
| mNativeMediaServerCrashListener = nativeMediaServerCrashListener; |
| } |
| |
| @CalledByNative |
| public void releaseWatchdog() { |
| if (mPlayer == null) return; |
| |
| mPlayer.release(); |
| mPlayer = null; |
| } |
| |
| @CalledByNative |
| public boolean startListening() { |
| if (mPlayer != null) return true; |
| |
| try { |
| mPlayer = MediaPlayer.create(ContextUtils.getApplicationContext(), R.raw.empty); |
| } catch (IllegalStateException e) { |
| Log.e(TAG, "Exception while creating the watchdog player.", e); |
| } catch (RuntimeException e) { |
| Log.e(TAG, "Exception while creating the watchdog player.", e); |
| } |
| |
| if (mPlayer != null) { |
| mPlayer.setOnErrorListener(MediaServerCrashListener.this); |
| |
| // Reset the reported creation failure time on successful |
| // watchdog creation. |
| mLastReportedWatchdogCreationFailure = UNKNOWN_TIME; |
| return true; |
| } |
| |
| long currentTime = SystemClock.elapsedRealtime(); |
| |
| // It takes ~5s for the MediaServer to restart. Do not report a |
| // failure to create a watchdog MediaPlayer as a crash more than |
| // once per 5s, to prevent a burst of calls to startListening() from |
| // artificially inflating the number of crashes. |
| if (mLastReportedWatchdogCreationFailure == UNKNOWN_TIME |
| || (currentTime - mLastReportedWatchdogCreationFailure) |
| > APPROX_MEDIA_SERVER_RESTART_TIME_IN_MS) { |
| Log.e(TAG, "Unable to create watchdog player, treating it as server crash."); |
| MediaServerCrashListenerJni.get().onMediaServerCrashDetected( |
| mNativeMediaServerCrashListener, MediaServerCrashListener.this, false); |
| mLastReportedWatchdogCreationFailure = currentTime; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean onError(MediaPlayer mp, int what, int extra) { |
| if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { |
| MediaServerCrashListenerJni.get().onMediaServerCrashDetected( |
| mNativeMediaServerCrashListener, MediaServerCrashListener.this, true); |
| releaseWatchdog(); |
| } |
| return true; |
| } |
| |
| @NativeMethods |
| interface Natives { |
| void onMediaServerCrashDetected(long nativeMediaServerCrashListener, |
| MediaServerCrashListener caller, boolean watchdogNeedsRelease); |
| } |
| } |