// Copyright (c) 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.MediaPlayer; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import android.view.Surface; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; // A wrapper around android.media.MediaPlayer that allows the native code to use it. // See media/base/android/media_player_bridge.cc for the corresponding native code. @JNINamespace("media") public class MediaPlayerBridge { private static final String TAG = "MediaPlayerBridge"; // Local player to forward this to. We don't initialize it here since the subclass might not // want it. private MediaPlayer mPlayer; @CalledByNative private static MediaPlayerBridge create() { return new MediaPlayerBridge(); } protected MediaPlayer getLocalPlayer() { if (mPlayer == null) { mPlayer = new MediaPlayer(); } return mPlayer; } @CalledByNative protected void setSurface(Surface surface) { getLocalPlayer().setSurface(surface); } @CalledByNative protected void prepareAsync() throws IllegalStateException { getLocalPlayer().prepareAsync(); } @CalledByNative protected boolean isPlaying() { return getLocalPlayer().isPlaying(); } @CalledByNative protected int getVideoWidth() { return getLocalPlayer().getVideoWidth(); } @CalledByNative protected int getVideoHeight() { return getLocalPlayer().getVideoHeight(); } @CalledByNative protected int getCurrentPosition() { return getLocalPlayer().getCurrentPosition(); } @CalledByNative protected int getDuration() { return getLocalPlayer().getDuration(); } @CalledByNative protected void release() { getLocalPlayer().release(); } @CalledByNative protected void setVolume(double volume) { getLocalPlayer().setVolume((float) volume, (float) volume); } @CalledByNative protected void start() { getLocalPlayer().start(); } @CalledByNative protected void pause() { getLocalPlayer().pause(); } @CalledByNative protected void seekTo(int msec) throws IllegalStateException { getLocalPlayer().seekTo(msec); } @CalledByNative protected boolean setDataSource( Context context, String url, String cookies, boolean hideUrlLog) { Uri uri = Uri.parse(url); HashMap<String, String> headersMap = new HashMap<String, String>(); if (hideUrlLog) headersMap.put("x-hide-urls-from-log", "true"); if (!TextUtils.isEmpty(cookies)) headersMap.put("Cookie", cookies); try { getLocalPlayer().setDataSource(context, uri, headersMap); return true; } catch (Exception e) { return false; } } protected void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) { getLocalPlayer().setOnBufferingUpdateListener(listener); } protected void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) { getLocalPlayer().setOnCompletionListener(listener); } protected void setOnErrorListener(MediaPlayer.OnErrorListener listener) { getLocalPlayer().setOnErrorListener(listener); } protected void setOnPreparedListener(MediaPlayer.OnPreparedListener listener) { getLocalPlayer().setOnPreparedListener(listener); } protected void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener) { getLocalPlayer().setOnSeekCompleteListener(listener); } protected void setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener) { getLocalPlayer().setOnVideoSizeChangedListener(listener); } private static class AllowedOperations { private final boolean mCanPause; private final boolean mCanSeekForward; private final boolean mCanSeekBackward; private AllowedOperations(boolean canPause, boolean canSeekForward, boolean canSeekBackward) { mCanPause = canPause; mCanSeekForward = canSeekForward; mCanSeekBackward = canSeekBackward; } @CalledByNative("AllowedOperations") private boolean canPause() { return mCanPause; } @CalledByNative("AllowedOperations") private boolean canSeekForward() { return mCanSeekForward; } @CalledByNative("AllowedOperations") private boolean canSeekBackward() { return mCanSeekBackward; } } /** * Returns an AllowedOperations object to show all the operations that are * allowed on the media player. */ @CalledByNative private static AllowedOperations getAllowedOperations(MediaPlayer player) { boolean canPause = true; boolean canSeekForward = true; boolean canSeekBackward = true; try { Method getMetadata = player.getClass().getDeclaredMethod( "getMetadata", boolean.class, boolean.class); getMetadata.setAccessible(true); Object data = getMetadata.invoke(player, false, false); if (data != null) { Class<?> metadataClass = data.getClass(); Method hasMethod = metadataClass.getDeclaredMethod("has", int.class); Method getBooleanMethod = metadataClass.getDeclaredMethod("getBoolean", int.class); int pause = (Integer) metadataClass.getField("PAUSE_AVAILABLE").get(null); int seekForward = (Integer) metadataClass.getField("SEEK_FORWARD_AVAILABLE").get(null); int seekBackward = (Integer) metadataClass.getField("SEEK_BACKWARD_AVAILABLE").get(null); hasMethod.setAccessible(true); getBooleanMethod.setAccessible(true); canPause = !((Boolean) hasMethod.invoke(data, pause)) || ((Boolean) getBooleanMethod.invoke(data, pause)); canSeekForward = !((Boolean) hasMethod.invoke(data, seekForward)) || ((Boolean) getBooleanMethod.invoke(data, seekForward)); canSeekBackward = !((Boolean) hasMethod.invoke(data, seekBackward)) || ((Boolean) getBooleanMethod.invoke(data, seekBackward)); } } catch (NoSuchMethodException e) { Log.e(TAG, "Cannot find getMetadata() method: " + e); } catch (InvocationTargetException e) { Log.e(TAG, "Cannot invoke MediaPlayer.getMetadata() method: " + e); } catch (IllegalAccessException e) { Log.e(TAG, "Cannot access metadata: " + e); } catch (NoSuchFieldException e) { Log.e(TAG, "Cannot find matching fields in Metadata class: " + e); } return new AllowedOperations(canPause, canSeekForward, canSeekBackward); } }