// Copyright 2012 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.content.Context; import android.media.AudioManager; import android.media.MediaPlayer; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; // This class implements all the listener interface for android mediaplayer. // Callbacks will be sent to the native class for processing. @JNINamespace("media") class MediaPlayerListener implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnErrorListener, AudioManager.OnAudioFocusChangeListener { // These values are mirrored as enums in media/base/android/media_player_bridge.h. // Please ensure they stay in sync. private static final int MEDIA_ERROR_FORMAT = 0; private static final int MEDIA_ERROR_DECODE = 1; private static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 2; private static final int MEDIA_ERROR_INVALID_CODE = 3; // These values are copied from android media player. public static final int MEDIA_ERROR_MALFORMED = -1007; public static final int MEDIA_ERROR_TIMED_OUT = -110; // Used to determine the class instance to dispatch the native call to. private long mNativeMediaPlayerListener = 0; private final Context mContext; private MediaPlayerListener(long nativeMediaPlayerListener, Context context) { mNativeMediaPlayerListener = nativeMediaPlayerListener; mContext = context; } @Override public boolean onError(MediaPlayer mp, int what, int extra) { int errorType; switch (what) { case MediaPlayer.MEDIA_ERROR_UNKNOWN: switch (extra) { case MEDIA_ERROR_MALFORMED: errorType = MEDIA_ERROR_DECODE; break; case MEDIA_ERROR_TIMED_OUT: errorType = MEDIA_ERROR_INVALID_CODE; break; default: errorType = MEDIA_ERROR_FORMAT; break; } break; case MediaPlayer.MEDIA_ERROR_SERVER_DIED: errorType = MEDIA_ERROR_DECODE; break; case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: errorType = MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK; break; default: // There are some undocumented error codes for android media player. // For example, when surfaceTexture got deleted before we setVideoSuface // to NULL, mediaplayer will report error -38. These errors should be ignored // and not be treated as an error to webkit. errorType = MEDIA_ERROR_INVALID_CODE; break; } nativeOnMediaError(mNativeMediaPlayerListener, errorType); return true; } @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { nativeOnVideoSizeChanged(mNativeMediaPlayerListener, width, height); } @Override public void onSeekComplete(MediaPlayer mp) { nativeOnSeekComplete(mNativeMediaPlayerListener); } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { nativeOnBufferingUpdate(mNativeMediaPlayerListener, percent); } @Override public void onCompletion(MediaPlayer mp) { nativeOnPlaybackComplete(mNativeMediaPlayerListener); } @Override public void onPrepared(MediaPlayer mp) { nativeOnMediaPrepared(mNativeMediaPlayerListener); } @Override public void onAudioFocusChange(int focusChange) { if (focusChange == AudioManager.AUDIOFOCUS_LOSS || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { nativeOnMediaInterrupted(mNativeMediaPlayerListener); } } @CalledByNative public void releaseResources() { if (mContext != null) { // Unregister the wish for audio focus. AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); if (am != null) { am.abandonAudioFocus(this); } } } @CalledByNative private static MediaPlayerListener create(long nativeMediaPlayerListener, Context context, MediaPlayerBridge mediaPlayerBridge) { final MediaPlayerListener listener = new MediaPlayerListener(nativeMediaPlayerListener, context); if (mediaPlayerBridge != null) { mediaPlayerBridge.setOnBufferingUpdateListener(listener); mediaPlayerBridge.setOnCompletionListener(listener); mediaPlayerBridge.setOnErrorListener(listener); mediaPlayerBridge.setOnPreparedListener(listener); mediaPlayerBridge.setOnSeekCompleteListener(listener); mediaPlayerBridge.setOnVideoSizeChangedListener(listener); } AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); am.requestAudioFocus( listener, AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); return listener; } /** * See media/base/android/media_player_listener.cc for all the following functions. */ private native void nativeOnMediaError( long nativeMediaPlayerListener, int errorType); private native void nativeOnVideoSizeChanged( long nativeMediaPlayerListener, int width, int height); private native void nativeOnBufferingUpdate( long nativeMediaPlayerListener, int percent); private native void nativeOnMediaPrepared(long nativeMediaPlayerListener); private native void nativeOnPlaybackComplete(long nativeMediaPlayerListener); private native void nativeOnSeekComplete(long nativeMediaPlayerListener); private native void nativeOnMediaInterrupted(long nativeMediaPlayerListener); }