summaryrefslogtreecommitdiff
path: root/media/base/android
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
committerTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
commit5821806d5e7f356e8fa4b058a389a808ea183019 (patch)
treee19f4793aac92e2c0d9a01087019a60d6657d838 /media/base/android
parent8e79a8efe247f109aafd917a69e8a392961b3687 (diff)
downloadchromium_org-5821806d5e7f356e8fa4b058a389a808ea183019.tar.gz
Merge from Chromium at DEPS revision r167172
This commit was generated by merge_to_master.py. Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
Diffstat (limited to 'media/base/android')
-rw-r--r--media/base/android/OWNERS4
-rw-r--r--media/base/android/cookie_getter.cc11
-rw-r--r--media/base/android/cookie_getter.h30
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java35
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaPlayerListener.java138
-rw-r--r--media/base/android/media_jni_registrar.cc28
-rw-r--r--media/base/android/media_jni_registrar.h19
-rw-r--r--media/base/android/media_player_bridge.cc383
-rw-r--r--media/base/android/media_player_bridge.h224
-rw-r--r--media/base/android/media_player_bridge_manager.cc11
-rw-r--r--media/base/android/media_player_bridge_manager.h34
-rw-r--r--media/base/android/media_player_listener.cc86
-rw-r--r--media/base/android/media_player_listener.h62
13 files changed, 1065 insertions, 0 deletions
diff --git a/media/base/android/OWNERS b/media/base/android/OWNERS
new file mode 100644
index 0000000000..5a000138b9
--- /dev/null
+++ b/media/base/android/OWNERS
@@ -0,0 +1,4 @@
+bulach@chromium.org
+jcivelli@chromium.org
+jrg@chromium.org
+yfriedman@chromium.org
diff --git a/media/base/android/cookie_getter.cc b/media/base/android/cookie_getter.cc
new file mode 100644
index 0000000000..1db105a6e2
--- /dev/null
+++ b/media/base/android/cookie_getter.cc
@@ -0,0 +1,11 @@
+// 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.
+
+#include "media/base/android/cookie_getter.h"
+
+namespace media {
+
+CookieGetter::~CookieGetter() {}
+
+} // namespace media
diff --git a/media/base/android/cookie_getter.h b/media/base/android/cookie_getter.h
new file mode 100644
index 0000000000..bf7cc55d7f
--- /dev/null
+++ b/media/base/android/cookie_getter.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef MEDIA_BASE_ANDROID_COOKIE_GETTER_H_
+#define MEDIA_BASE_ANDROID_COOKIE_GETTER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Class for asynchronously retrieving the cookies for a given URL.
+class MEDIA_EXPORT CookieGetter {
+ public:
+ typedef base::Callback<void(const std::string&)> GetCookieCB;
+ virtual ~CookieGetter();
+
+ // Method for getting the cookies for a given URL.
+ // The callback is executed on the caller's thread.
+ virtual void GetCookies(const std::string& url,
+ const std::string& first_party_for_cookies,
+ const GetCookieCB& callback) = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_COOKIE_GETTER_H_
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
new file mode 100644
index 0000000000..cd3a2b5dc3
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
@@ -0,0 +1,35 @@
+// 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 java.util.HashMap;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+@JNINamespace("media")
+class MediaPlayerBridge {
+ @CalledByNative
+ private static boolean setDataSource(MediaPlayer player, Context context, String url,
+ String cookies, boolean hideUrlLog) {
+ Uri uri = Uri.parse(url);
+ HashMap headersMap = new HashMap<String, String>();
+ if (hideUrlLog)
+ headersMap.put("x-hide-urls-from-log", "true");
+ if (!TextUtils.isEmpty(cookies))
+ headersMap.put("Cookie", cookies);
+ try {
+ player.setDataSource(context, uri, headersMap);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
new file mode 100644
index 0000000000..043dfb92ea
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
@@ -0,0 +1,138 @@
+// 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.content.pm.PackageManager;
+import android.Manifest.permission;
+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 {
+ // 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 int mNativeMediaPlayerListener = 0;
+
+ private MediaPlayerListener(int nativeMediaPlayerListener) {
+ mNativeMediaPlayerListener = nativeMediaPlayerListener;
+ }
+
+ @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);
+ }
+
+ @CalledByNative
+ private static void create(int nativeMediaPlayerListener,
+ Context context, MediaPlayer mediaPlayer) {
+ MediaPlayerListener listener = new MediaPlayerListener(nativeMediaPlayerListener);
+ mediaPlayer.setOnBufferingUpdateListener(listener);
+ mediaPlayer.setOnCompletionListener(listener);
+ mediaPlayer.setOnErrorListener(listener);
+ mediaPlayer.setOnPreparedListener(listener);
+ mediaPlayer.setOnSeekCompleteListener(listener);
+ mediaPlayer.setOnVideoSizeChangedListener(listener);
+ if (PackageManager.PERMISSION_GRANTED ==
+ context.checkCallingPermission(permission.WAKE_LOCK)) {
+ mediaPlayer.setWakeMode(context, android.os.PowerManager.FULL_WAKE_LOCK);
+ }
+ }
+
+ /**
+ * See media/base/android/media_player_listener.cc for all the following functions.
+ */
+ private native void nativeOnMediaError(
+ int nativeMediaPlayerListener,
+ int errorType);
+
+ private native void nativeOnVideoSizeChanged(
+ int nativeMediaPlayerListener,
+ int width, int height);
+
+ private native void nativeOnBufferingUpdate(
+ int nativeMediaPlayerListener,
+ int percent);
+
+ private native void nativeOnMediaPrepared(int nativeMediaPlayerListener);
+
+ private native void nativeOnPlaybackComplete(int nativeMediaPlayerListener);
+
+ private native void nativeOnSeekComplete(int nativeMediaPlayerListener);
+}
diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc
new file mode 100644
index 0000000000..671b6790ef
--- /dev/null
+++ b/media/base/android/media_jni_registrar.cc
@@ -0,0 +1,28 @@
+// 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.
+
+#include "media/base/android/media_jni_registrar.h"
+
+#include "base/basictypes.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+
+#include "media/base/android/media_player_bridge.h"
+#include "media/base/android/media_player_listener.h"
+
+namespace media {
+
+static base::android::RegistrationMethod kMediaRegisteredMethods[] = {
+ { "MediaPlayerBridge",
+ MediaPlayerBridge::RegisterMediaPlayerBridge },
+ { "MediaPlayerListener",
+ MediaPlayerListener::RegisterMediaPlayerListener },
+};
+
+bool RegisterJni(JNIEnv* env) {
+ return base::android::RegisterNativeMethods(
+ env, kMediaRegisteredMethods, arraysize(kMediaRegisteredMethods));
+}
+
+} // namespace media
diff --git a/media/base/android/media_jni_registrar.h b/media/base/android/media_jni_registrar.h
new file mode 100644
index 0000000000..7e937028f8
--- /dev/null
+++ b/media/base/android/media_jni_registrar.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef MEDIA_BASE_ANDROID_MEDIA_JNI_REGISTRAR_H_
+#define MEDIA_BASE_ANDROID_MEDIA_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Register all JNI bindings necessary for media.
+MEDIA_EXPORT bool RegisterJni(JNIEnv* env);
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_JNI_REGISTRAR_H_
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
new file mode 100644
index 0000000000..ad5e1618c1
--- /dev/null
+++ b/media/base/android/media_player_bridge.cc
@@ -0,0 +1,383 @@
+// 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.
+
+#include "media/base/android/media_player_bridge.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/message_loop_proxy.h"
+#include "jni/MediaPlayerBridge_jni.h"
+#include "jni/MediaPlayer_jni.h"
+#include "media/base/android/cookie_getter.h"
+#include "media/base/android/media_player_bridge_manager.h"
+
+using base::android::AttachCurrentThread;
+using base::android::CheckException;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::GetClass;
+using base::android::JavaRef;
+using base::android::MethodID;
+using base::android::ScopedJavaLocalRef;
+
+// These constants are from the android source tree and need to be kept in
+// sync with android/media/MediaMetadata.java.
+static const jint kPauseAvailable = 1;
+static const jint kSeekBackwardAvailable = 2;
+static const jint kSeekForwardAvailable = 3;
+
+// Time update happens every 250ms.
+static const int kTimeUpdateInterval = 250;
+
+// Because we create the media player lazily on android, the duration of the
+// media is initially unknown to us. This makes the user unable to perform
+// seek. To solve this problem, we use a temporary duration of 100 seconds when
+// the duration is unknown. And we scale the seek position later when duration
+// is available.
+// TODO(qinmin): create a thread and use android MediaMetadataRetriever
+// class to extract the duration.
+static const int kTemporaryDuration = 100;
+
+namespace media {
+
+MediaPlayerBridge::MediaPlayerBridge(
+ int player_id,
+ const std::string& url,
+ const std::string& first_party_for_cookies,
+ CookieGetter* cookie_getter,
+ bool hide_url_log,
+ MediaPlayerBridgeManager* manager,
+ const MediaErrorCB& media_error_cb,
+ const VideoSizeChangedCB& video_size_changed_cb,
+ const BufferingUpdateCB& buffering_update_cb,
+ const MediaPreparedCB& media_prepared_cb,
+ const PlaybackCompleteCB& playback_complete_cb,
+ const SeekCompleteCB& seek_complete_cb,
+ const TimeUpdateCB& time_update_cb)
+ : media_error_cb_(media_error_cb),
+ video_size_changed_cb_(video_size_changed_cb),
+ buffering_update_cb_(buffering_update_cb),
+ media_prepared_cb_(media_prepared_cb),
+ playback_complete_cb_(playback_complete_cb),
+ seek_complete_cb_(seek_complete_cb),
+ time_update_cb_(time_update_cb),
+ player_id_(player_id),
+ prepared_(false),
+ pending_play_(false),
+ url_(url),
+ first_party_for_cookies_(first_party_for_cookies),
+ has_cookies_(false),
+ hide_url_log_(hide_url_log),
+ duration_(base::TimeDelta::FromSeconds(kTemporaryDuration)),
+ width_(0),
+ height_(0),
+ can_pause_(true),
+ can_seek_forward_(true),
+ can_seek_backward_(true),
+ manager_(manager),
+ cookie_getter_(cookie_getter),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)),
+ listener_(base::MessageLoopProxy::current(),
+ weak_this_.GetWeakPtr()) {}
+
+MediaPlayerBridge::~MediaPlayerBridge() {
+ Release();
+}
+
+void MediaPlayerBridge::InitializePlayer() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ j_media_player_.Reset(JNI_MediaPlayer::Java_MediaPlayer_Constructor(env));
+
+ jobject j_context = base::android::GetApplicationContext();
+ DCHECK(j_context);
+
+ listener_.CreateMediaPlayerListener(j_context, j_media_player_.obj());
+}
+
+void MediaPlayerBridge::SetVideoSurface(jobject surface) {
+ if (j_media_player_.is_null() && surface != NULL)
+ Prepare();
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ JNI_MediaPlayer::Java_MediaPlayer_setSurface(
+ env, j_media_player_.obj(), surface);
+}
+
+void MediaPlayerBridge::Prepare() {
+ if (j_media_player_.is_null())
+ InitializePlayer();
+
+ if (has_cookies_) {
+ GetCookiesCallback(cookies_);
+ } else {
+ cookie_getter_->GetCookies(url_, first_party_for_cookies_, base::Bind(
+ &MediaPlayerBridge::GetCookiesCallback, weak_this_.GetWeakPtr()));
+ }
+}
+
+void MediaPlayerBridge::GetCookiesCallback(const std::string& cookies) {
+ cookies_ = cookies;
+ has_cookies_ = true;
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ // Create a Java String for the URL.
+ ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url_);
+ ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
+ env, cookies_);
+
+ jobject j_context = base::android::GetApplicationContext();
+ DCHECK(j_context);
+
+ if (Java_MediaPlayerBridge_setDataSource(
+ env, j_media_player_.obj(), j_context, j_url_string.obj(),
+ j_cookies.obj(), hide_url_log_)) {
+ if (manager_)
+ manager_->RequestMediaResources(this);
+ JNI_MediaPlayer::Java_MediaPlayer_prepareAsync(
+ env, j_media_player_.obj());
+ } else {
+ media_error_cb_.Run(player_id_, MEDIA_ERROR_FORMAT);
+ }
+}
+
+void MediaPlayerBridge::Start() {
+ if (j_media_player_.is_null()) {
+ pending_play_ = true;
+ Prepare();
+ } else {
+ if (prepared_)
+ StartInternal();
+ else
+ pending_play_ = true;
+ }
+}
+
+void MediaPlayerBridge::Pause() {
+ if (j_media_player_.is_null()) {
+ pending_play_ = false;
+ } else {
+ if (prepared_ && IsPlaying())
+ PauseInternal();
+ else
+ pending_play_ = false;
+ }
+}
+
+bool MediaPlayerBridge::IsPlaying() {
+ if (!prepared_)
+ return pending_play_;
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+ jboolean result = JNI_MediaPlayer::Java_MediaPlayer_isPlaying(
+ env, j_media_player_.obj());
+ return result;
+}
+
+int MediaPlayerBridge::GetVideoWidth() {
+ if (!prepared_)
+ return width_;
+ JNIEnv* env = AttachCurrentThread();
+ return JNI_MediaPlayer::Java_MediaPlayer_getVideoWidth(
+ env, j_media_player_.obj());
+}
+
+int MediaPlayerBridge::GetVideoHeight() {
+ if (!prepared_)
+ return height_;
+ JNIEnv* env = AttachCurrentThread();
+ return JNI_MediaPlayer::Java_MediaPlayer_getVideoHeight(
+ env, j_media_player_.obj());
+}
+
+void MediaPlayerBridge::SeekTo(base::TimeDelta time) {
+ // Record the time to seek when OnMediaPrepared() is called.
+ pending_seek_ = time;
+
+ if (j_media_player_.is_null())
+ Prepare();
+ else if (prepared_)
+ SeekInternal(time);
+}
+
+base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
+ if (!prepared_)
+ return pending_seek_;
+ JNIEnv* env = AttachCurrentThread();
+ return base::TimeDelta::FromMilliseconds(
+ JNI_MediaPlayer::Java_MediaPlayer_getCurrentPosition(
+ env, j_media_player_.obj()));
+}
+
+base::TimeDelta MediaPlayerBridge::GetDuration() {
+ if (!prepared_)
+ return duration_;
+ JNIEnv* env = AttachCurrentThread();
+ return base::TimeDelta::FromMilliseconds(
+ JNI_MediaPlayer::Java_MediaPlayer_getDuration(
+ env, j_media_player_.obj()));
+}
+
+void MediaPlayerBridge::Release() {
+ if (j_media_player_.is_null())
+ return;
+
+ time_update_timer_.Stop();
+ if (prepared_)
+ pending_seek_ = GetCurrentTime();
+ if (manager_)
+ manager_->ReleaseMediaResources(this);
+ prepared_ = false;
+ pending_play_ = false;
+ SetVideoSurface(NULL);
+
+ JNIEnv* env = AttachCurrentThread();
+ JNI_MediaPlayer::Java_MediaPlayer_release(env, j_media_player_.obj());
+ j_media_player_.Reset();
+}
+
+void MediaPlayerBridge::SetVolume(float left_volume, float right_volume) {
+ if (j_media_player_.is_null())
+ return;
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+ JNI_MediaPlayer::Java_MediaPlayer_setVolume(
+ env, j_media_player_.obj(), left_volume, right_volume);
+}
+
+void MediaPlayerBridge::DoTimeUpdate() {
+ base::TimeDelta current = GetCurrentTime();
+ time_update_cb_.Run(player_id_, current);
+}
+
+void MediaPlayerBridge::OnMediaError(int error_type) {
+ media_error_cb_.Run(player_id_, error_type);
+}
+
+void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
+ width_ = width;
+ height_ = height;
+ video_size_changed_cb_.Run(player_id_, width, height);
+}
+
+void MediaPlayerBridge::OnBufferingUpdate(int percent) {
+ buffering_update_cb_.Run(player_id_, percent);
+}
+
+void MediaPlayerBridge::OnPlaybackComplete() {
+ time_update_timer_.Stop();
+ playback_complete_cb_.Run(player_id_);
+}
+
+void MediaPlayerBridge::OnSeekComplete() {
+ seek_complete_cb_.Run(player_id_, GetCurrentTime());
+}
+
+void MediaPlayerBridge::OnMediaPrepared() {
+ if (j_media_player_.is_null())
+ return;
+
+ prepared_ = true;
+
+ base::TimeDelta dur = duration_;
+ duration_ = GetDuration();
+
+ if (duration_ != dur && 0 != dur.InMilliseconds()) {
+ // Scale the |pending_seek_| according to the new duration.
+ pending_seek_ = base::TimeDelta::FromSeconds(
+ pending_seek_.InSecondsF() * duration_.InSecondsF() / dur.InSecondsF());
+ }
+
+ // If media player was recovered from a saved state, consume all the pending
+ // events.
+ SeekInternal(pending_seek_);
+
+ if (pending_play_) {
+ StartInternal();
+ pending_play_ = false;
+ }
+
+ GetMetadata();
+
+ media_prepared_cb_.Run(player_id_, duration_);
+}
+
+void MediaPlayerBridge::GetMetadata() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ ScopedJavaLocalRef<jclass> media_player_class(
+ GetClass(env, "android/media/MediaPlayer"));
+ jmethodID method = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, media_player_class.obj(), "getMetadata",
+ "(ZZ)Landroid/media/Metadata;");
+ ScopedJavaLocalRef<jobject> j_metadata(
+ env, env->CallObjectMethod(
+ j_media_player_.obj(), method, JNI_FALSE, JNI_FALSE));
+ CheckException(env);
+ if (j_metadata.is_null())
+ return;
+
+ ScopedJavaLocalRef<jclass> metadata_class(
+ GetClass(env, "android/media/Metadata"));
+ jmethodID get_boolean = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, metadata_class.obj(), "getBoolean", "(I)Z");
+ can_pause_ = env->CallBooleanMethod(j_metadata.obj(),
+ get_boolean,
+ kPauseAvailable);
+ CheckException(env);
+ can_seek_forward_ = env->CallBooleanMethod(j_metadata.obj(),
+ get_boolean,
+ kSeekBackwardAvailable);
+ CheckException(env);
+ can_seek_backward_ = env->CallBooleanMethod(j_metadata.obj(),
+ get_boolean,
+ kSeekForwardAvailable);
+ CheckException(env);
+}
+
+void MediaPlayerBridge::StartInternal() {
+ JNIEnv* env = AttachCurrentThread();
+ JNI_MediaPlayer::Java_MediaPlayer_start(env, j_media_player_.obj());
+ if (!time_update_timer_.IsRunning()) {
+ time_update_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
+ this, &MediaPlayerBridge::DoTimeUpdate);
+ }
+}
+
+void MediaPlayerBridge::PauseInternal() {
+ JNIEnv* env = AttachCurrentThread();
+ JNI_MediaPlayer::Java_MediaPlayer_pause(env, j_media_player_.obj());
+ time_update_timer_.Stop();
+}
+
+void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ int time_msec = static_cast<int>(time.InMilliseconds());
+ JNI_MediaPlayer::Java_MediaPlayer_seekTo(
+ env, j_media_player_.obj(), time_msec);
+}
+
+bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
+ bool ret = RegisterNativesImpl(env);
+ DCHECK(g_MediaPlayerBridge_clazz);
+ if (ret)
+ ret = JNI_MediaPlayer::RegisterNativesImpl(env);
+ return ret;
+}
+
+} // namespace media
diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h
new file mode 100644
index 0000000000..820707f253
--- /dev/null
+++ b/media/base/android/media_player_bridge.h
@@ -0,0 +1,224 @@
+// 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.
+
+#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_H_
+#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_H_
+
+#include <jni.h>
+#include <map>
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "media/base/media_export.h"
+#include "media/base/android/media_player_listener.h"
+
+namespace media {
+
+class CookieGetter;
+class MediaPlayerBridgeManager;
+
+// This class serves as a bridge for native code to call java functions inside
+// android mediaplayer class. For more information on android mediaplayer, check
+// http://developer.android.com/reference/android/media/MediaPlayer.html
+// The actual android mediaplayer instance is created lazily when Start(),
+// Pause(), SeekTo() gets called. As a result, media information may not
+// be available until one of those operations is performed. After that, we
+// will cache those information in case the mediaplayer gets released.
+class MEDIA_EXPORT MediaPlayerBridge {
+ public:
+ // Error types for MediaErrorCB.
+ enum MediaErrorType {
+ MEDIA_ERROR_FORMAT,
+ MEDIA_ERROR_DECODE,
+ MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
+ MEDIA_ERROR_INVALID_CODE,
+ };
+
+ // Callback when error happens. Args: player ID, error type.
+ typedef base::Callback<void(int, int)> MediaErrorCB;
+
+ // Callback when video size has changed. Args: player ID, width, height.
+ typedef base::Callback<void(int, int, int)> VideoSizeChangedCB;
+
+ // Callback when buffering has changed. Args: player ID, percentage
+ // of the media.
+ typedef base::Callback<void(int, int)> BufferingUpdateCB;
+
+ // Callback when player got prepared. Args: player ID, duration of the media.
+ typedef base::Callback<void(int, base::TimeDelta)> MediaPreparedCB;
+
+ // Callbacks when seek completed. Args: player ID, current time.
+ typedef base::Callback<void(int, base::TimeDelta)> SeekCompleteCB;
+
+ // Callbacks when playback completed. Args: player ID.
+ typedef base::Callback<void(int)> PlaybackCompleteCB;
+
+ // Callback when time update messages need to be sent. Args: player ID,
+ // current time.
+ typedef base::Callback<void(int, base::TimeDelta)> TimeUpdateCB;
+
+ static bool RegisterMediaPlayerBridge(JNIEnv* env);
+
+ // Construct a MediaPlayerBridge object with all the needed media player
+ // callbacks. This object needs to call |manager|'s RequestMediaResources()
+ // before decoding the media stream. This allows |manager| to track
+ // unused resources and free them when needed. On the other hand, it needs
+ // to call ReleaseMediaResources() when it is done with decoding.
+ MediaPlayerBridge(int player_id,
+ const std::string& url,
+ const std::string& first_party_for_cookies,
+ CookieGetter* cookie_getter,
+ bool hide_url_log,
+ MediaPlayerBridgeManager* manager,
+ const MediaErrorCB& media_error_cb,
+ const VideoSizeChangedCB& video_size_changed_cb,
+ const BufferingUpdateCB& buffering_update_cb,
+ const MediaPreparedCB& media_prepared_cb,
+ const PlaybackCompleteCB& playback_complete_cb,
+ const SeekCompleteCB& seek_complete_cb,
+ const TimeUpdateCB& time_update_cb);
+ ~MediaPlayerBridge();
+
+ typedef std::map<std::string, std::string> HeadersMap;
+
+ void SetVideoSurface(jobject surface);
+
+ // Start playing the media.
+ void Start();
+
+ // Pause the media.
+ void Pause();
+
+ // Seek to a particular position. When succeeds, OnSeekComplete() will be
+ // called. Otherwise, nothing will happen.
+ void SeekTo(base::TimeDelta time);
+
+ // Release the player resources.
+ void Release();
+
+ // Set the player volume.
+ void SetVolume(float leftVolume, float rightVolume);
+
+ // Get the media information from the player.
+ int GetVideoWidth();
+ int GetVideoHeight();
+ base::TimeDelta GetCurrentTime();
+ base::TimeDelta GetDuration();
+ bool IsPlaying();
+
+ // Get metadata from the media.
+ void GetMetadata();
+
+ // Called by the timer to check for current time routinely and generates
+ // time update events.
+ void DoTimeUpdate();
+
+ // Called by the MediaPlayerListener and mirrored to corresponding
+ // callbacks.
+ void OnMediaError(int error_type);
+ void OnVideoSizeChanged(int width, int height);
+ void OnBufferingUpdate(int percent);
+ void OnPlaybackComplete();
+ void OnSeekComplete();
+ void OnMediaPrepared();
+
+ // Prepare the player for playback, asynchronously. When succeeds,
+ // OnMediaPrepared() will be called. Otherwise, OnMediaError() will
+ // be called with an error type.
+ void Prepare();
+
+ // Callback function passed to |cookies_retriever_|.
+ void GetCookiesCallback(const std::string& cookies);
+
+ int player_id() { return player_id_; }
+ bool can_pause() { return can_pause_; }
+ bool can_seek_forward() { return can_seek_forward_; }
+ bool can_seek_backward() { return can_seek_backward_; }
+ bool prepared() { return prepared_; }
+
+ private:
+ // Create the actual android media player.
+ void InitializePlayer();
+
+ // Functions that implements media player control.
+ void StartInternal();
+ void PauseInternal();
+ void SeekInternal(base::TimeDelta time);
+
+ // Callbacks when events are received.
+ MediaErrorCB media_error_cb_;
+ VideoSizeChangedCB video_size_changed_cb_;
+ BufferingUpdateCB buffering_update_cb_;
+ MediaPreparedCB media_prepared_cb_;
+ PlaybackCompleteCB playback_complete_cb_;
+ SeekCompleteCB seek_complete_cb_;
+
+ // Callbacks when timer events are received.
+ TimeUpdateCB time_update_cb_;
+
+ // Player ID assigned to this player.
+ int player_id_;
+
+ // Whether the player is prepared for playback.
+ bool prepared_;
+
+ // Pending play event while player is preparing.
+ bool pending_play_;
+
+ // Pending seek time while player is preparing.
+ base::TimeDelta pending_seek_;
+
+ // Url for playback.
+ std::string url_;
+
+ // First party url for cookies.
+ std::string first_party_for_cookies_;
+
+ // Whether cookies are available.
+ bool has_cookies_;
+
+ // Hide url log from media player.
+ bool hide_url_log_;
+
+ // Stats about the media.
+ base::TimeDelta duration_;
+ int width_;
+ int height_;
+
+ // Meta data about actions can be taken.
+ bool can_pause_;
+ bool can_seek_forward_;
+ bool can_seek_backward_;
+
+ // Cookies for |url_|
+ std::string cookies_;
+
+ // Resource manager for all the media players.
+ MediaPlayerBridgeManager* manager_;
+
+ // Object for retrieving cookies for this media player.
+ scoped_ptr<CookieGetter> cookie_getter_;
+
+ // Java MediaPlayer instance.
+ base::android::ScopedJavaGlobalRef<jobject> j_media_player_;
+
+ base::RepeatingTimer<MediaPlayerBridge> time_update_timer_;
+
+ // Weak pointer passed to |listener_| for callbacks.
+ base::WeakPtrFactory<MediaPlayerBridge> weak_this_;
+
+ // Listener object that listens to all the media player events.
+ MediaPlayerListener listener_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaPlayerBridge);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_H_
diff --git a/media/base/android/media_player_bridge_manager.cc b/media/base/android/media_player_bridge_manager.cc
new file mode 100644
index 0000000000..26f2219693
--- /dev/null
+++ b/media/base/android/media_player_bridge_manager.cc
@@ -0,0 +1,11 @@
+// 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.
+
+#include "media/base/android/media_player_bridge_manager.h"
+
+namespace media {
+
+MediaPlayerBridgeManager::~MediaPlayerBridgeManager() {}
+
+} // namespace media
diff --git a/media/base/android/media_player_bridge_manager.h b/media/base/android/media_player_bridge_manager.h
new file mode 100644
index 0000000000..93bd99cbd2
--- /dev/null
+++ b/media/base/android/media_player_bridge_manager.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_
+#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+class MediaPlayerBridge;
+
+// This class is responsible for managing active MediaPlayerBridge objects.
+// It is implemented by webkit_media::MediaPlayerBridgeManagerImpl and
+// content::MediaPlayerManagerAndroid.
+class MEDIA_EXPORT MediaPlayerBridgeManager {
+ public:
+ virtual ~MediaPlayerBridgeManager();
+
+ // Called by a MediaPlayerBridge object when it is going to decode
+ // media streams. This helps the manager object maintain an array
+ // of active MediaPlayerBridge objects and release the resources
+ // when needed.
+ virtual void RequestMediaResources(MediaPlayerBridge* player) = 0;
+
+ // Called when a MediaPlayerBridge object releases all its decoding
+ // resources.
+ virtual void ReleaseMediaResources(MediaPlayerBridge* player) = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_PLAYER_BRIDGE_MANAGER_H_
diff --git a/media/base/android/media_player_listener.cc b/media/base/android/media_player_listener.cc
new file mode 100644
index 0000000000..de1820190c
--- /dev/null
+++ b/media/base/android/media_player_listener.cc
@@ -0,0 +1,86 @@
+// 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.
+
+#include "media/base/android/media_player_listener.h"
+
+#include "base/android/jni_android.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "media/base/android/media_player_bridge.h"
+
+// Auto generated jni class from MediaPlayerListener.java.
+// Check base/android/jni_generator/golden_sample_for_tests_jni.h for example.
+#include "jni/MediaPlayerListener_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::CheckException;
+using base::android::ScopedJavaLocalRef;
+
+namespace media {
+
+MediaPlayerListener::MediaPlayerListener(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ base::WeakPtr<MediaPlayerBridge> media_player)
+ : message_loop_(message_loop),
+ media_player_(media_player) {
+ DCHECK(message_loop_);
+ DCHECK(media_player_);
+}
+
+MediaPlayerListener::~MediaPlayerListener() {}
+
+void MediaPlayerListener::CreateMediaPlayerListener(
+ jobject context, jobject media_player) {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_MediaPlayerListener_create(
+ env, reinterpret_cast<intptr_t>(this), context, media_player);
+}
+
+void MediaPlayerListener::OnMediaError(
+ JNIEnv* /* env */, jobject /* obj */, jint error_type) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnMediaError, media_player_, error_type));
+}
+
+void MediaPlayerListener::OnVideoSizeChanged(
+ JNIEnv* /* env */, jobject /* obj */, jint width, jint height) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnVideoSizeChanged, media_player_,
+ width, height));
+}
+
+void MediaPlayerListener::OnBufferingUpdate(
+ JNIEnv* /* env */, jobject /* obj */, jint percent) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnBufferingUpdate, media_player_, percent));
+}
+
+void MediaPlayerListener::OnPlaybackComplete(
+ JNIEnv* /* env */, jobject /* obj */) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnPlaybackComplete, media_player_));
+}
+
+void MediaPlayerListener::OnSeekComplete(
+ JNIEnv* /* env */, jobject /* obj */) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnSeekComplete, media_player_));
+}
+
+void MediaPlayerListener::OnMediaPrepared(
+ JNIEnv* /* env */, jobject /* obj */) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MediaPlayerBridge::OnMediaPrepared, media_player_));
+}
+
+bool MediaPlayerListener::RegisterMediaPlayerListener(JNIEnv* env) {
+ bool ret = RegisterNativesImpl(env);
+ DCHECK(g_MediaPlayerListener_clazz);
+ return ret;
+}
+
+} // namespace media
diff --git a/media/base/android/media_player_listener.h b/media/base/android/media_player_listener.h
new file mode 100644
index 0000000000..e88faae041
--- /dev/null
+++ b/media/base/android/media_player_listener.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_LISTENER_H_
+#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_LISTENER_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace media {
+
+class MediaPlayerBridge;
+
+// Acts as a thread proxy between java MediaPlayerListener object and
+// MediaPlayerBridge so that callbacks are posted onto the UI thread.
+class MediaPlayerListener {
+ public:
+ // Construct a native MediaPlayerListener object. Callbacks from the java
+ // side object will be forwarded to |media_player| by posting a task on the
+ // |message_loop|.
+ MediaPlayerListener(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ base::WeakPtr<MediaPlayerBridge> media_player);
+ virtual ~MediaPlayerListener();
+
+ // Called by the Java MediaPlayerListener and mirrored to corresponding
+ // callbacks.
+ void OnMediaError(JNIEnv* /* env */, jobject /* obj */, jint error_type);
+ void OnVideoSizeChanged(JNIEnv* /* env */, jobject /* obj */,
+ jint width, jint height);
+ void OnBufferingUpdate(JNIEnv* /* env */, jobject /* obj */, jint percent);
+ void OnPlaybackComplete(JNIEnv* /* env */, jobject /* obj */);
+ void OnSeekComplete(JNIEnv* /* env */, jobject /* obj */);
+ void OnMediaPrepared(JNIEnv* /* env */, jobject /* obj */);
+
+ // Create a Java MediaPlayerListener object.
+ void CreateMediaPlayerListener(jobject context, jobject media_player);
+
+ // Register MediaPlayerListener in the system library loader.
+ static bool RegisterMediaPlayerListener(JNIEnv* env);
+
+ private:
+ // The message loop where |media_player_| lives.
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ // The MediaPlayerBridge object all the callbacks should be send to.
+ base::WeakPtr<MediaPlayerBridge> media_player_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaPlayerListener);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_PLAYER_LISTENER_H_