Chromium Code Reviews| Index: media/base/android/media_player_bridge.cc |
| diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc |
| index 2728271217a8e6f0793f305707309465e80ed00a..c917c3f43c30480e939a97c653d941a2ffde3f94 100644 |
| --- a/media/base/android/media_player_bridge.cc |
| +++ b/media/base/android/media_player_bridge.cc |
| @@ -9,10 +9,10 @@ |
| #include "base/basictypes.h" |
| #include "base/logging.h" |
| #include "base/stringprintf.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" |
| +#include "base/message_loop_proxy.h" |
| +#include "media/base/android/cookie_getter.h" |
| +#include "media/base/android/media_player_bridge_manager.h" |
| +#include "media/base/android/media_player_listener.h" |
| using base::android::AttachCurrentThread; |
| using base::android::CheckException; |
| @@ -31,9 +31,65 @@ static const jint kSeekForwardAvailable = 3; |
| // This needs to be kept in sync with android.os.PowerManager |
| static const int kAndroidFullWakeLock = 26; |
| +// 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() { |
| +MediaPlayerBridge::MediaPlayerBridge( |
| + int player_id, |
| + const std::string& url, |
| + const std::string& first_party_for_cookies, |
| + media::CookieGetter* cookie_getter, |
| + bool hide_url_log, |
| + media::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_(new MediaPlayerListener(base::MessageLoopProxy::current(), |
| + weak_this_.GetWeakPtr())) {} |
| + |
| +MediaPlayerBridge::~MediaPlayerBridge() { |
| + Release(); |
| +} |
| + |
| +void MediaPlayerBridge::InitializePlayer() { |
| JNIEnv* env = AttachCurrentThread(); |
| CHECK(env); |
| @@ -48,16 +104,13 @@ MediaPlayerBridge::MediaPlayerBridge() { |
| j_media_player_.Reset(tmp); |
| ScopedJavaLocalRef<jobject> j_listener( |
| - Java_MediaPlayerListener_create(env, |
| - reinterpret_cast<intptr_t>(this))); |
| - DCHECK(!j_listener.is_null()); |
| + listener_->CreateMediaPlayerListener()); |
| // Set it as the various listeners. |
| const char* listeners[] = { |
| "OnBufferingUpdateListener", |
| "OnCompletionListener", |
| "OnErrorListener", |
| - "OnInfoListener", |
| "OnPreparedListener", |
| "OnSeekCompleteListener", |
| "OnVideoSizeChangedListener", |
| @@ -73,22 +126,53 @@ MediaPlayerBridge::MediaPlayerBridge() { |
| env->CallVoidMethod(j_media_player_.obj(), method, j_listener.obj()); |
| CheckException(env); |
| } |
| + |
| + jobject j_context = base::android::GetApplicationContext(); |
| + DCHECK(j_context); |
| + jmethodID method = GetMethodID(env, j_media_player_class_, |
| + "setWakeMode", "(Landroid/content/Context;I)V"); |
| + env->CallVoidMethod(j_media_player_.obj(), method, j_context, |
| + kAndroidFullWakeLock); |
| + CheckException(env); |
| } |
| -MediaPlayerBridge::~MediaPlayerBridge() { |
| - SetVideoSurface(NULL); |
| - CallVoidMethod("release"); |
| +void MediaPlayerBridge::SetVideoSurface(jobject surface) { |
| + if (j_media_player_.is_null() && surface != NULL) |
| + Prepare(); |
| + |
| + JNIEnv* env = AttachCurrentThread(); |
| + CHECK(env); |
| + |
| + jmethodID method = GetMethodID(env, |
| + j_media_player_class_, |
| + "setSurface", |
| + "(Landroid/view/Surface;)V"); |
| + env->CallVoidMethod(j_media_player_.obj(), method, surface); |
| + CheckException(env); |
| } |
| -void MediaPlayerBridge::SetDataSource( |
| - const std::string& url, |
| - const std::string& cookies, |
| - bool hide_url_log) { |
| +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(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_url_string = |
| + ConvertUTF8ToJavaString(env, url_); |
|
scherkus (not reviewing)
2012/09/13 10:40:14
why the code change? looks like it should fit on p
qinmin
2012/09/13 18:51:16
looks like the url->url_ just make this line 80 ch
|
| // Create the android.net.Uri object. |
| ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/net/Uri")); |
| @@ -108,11 +192,11 @@ void MediaPlayerBridge::SetDataSource( |
| // Construct headers that needs to be sent with the url. |
| HeadersMap headers; |
| // For incognito mode, we need a header to hide url log. |
| - if (hide_url_log) |
| + if (hide_url_log_) |
| headers.insert(std::make_pair("x-hide-urls-from-log", "true")); |
| // If cookies are present, add them in the header. |
| - if (!cookies.empty()) |
| - headers.insert(std::make_pair("Cookie", cookies)); |
| + if (!cookies_.empty()) |
| + headers.insert(std::make_pair("Cookie", cookies_)); |
| // Fill the Map with the headers. |
| for (HeadersMap::const_iterator iter = headers.begin(); |
| @@ -131,101 +215,112 @@ void MediaPlayerBridge::SetDataSource( |
| jmethodID set_data_source = |
| GetMethodID(env, j_media_player_class_, "setDataSource", |
| "(Landroid/content/Context;Landroid/net/Uri;Ljava/util/Map;)V"); |
| - DCHECK(set_data_source); |
| env->CallVoidMethod(j_media_player_.obj(), set_data_source, j_context, |
| j_uri.obj(), j_map.obj()); |
| - CheckException(env); |
| -} |
| - |
| -void MediaPlayerBridge::SetVideoSurface(jobject surface) { |
| - JNIEnv* env = AttachCurrentThread(); |
| - CHECK(env); |
| - |
| - jmethodID method = GetMethodID(env, |
| - j_media_player_class_, |
| - "setSurface", |
| - "(Landroid/view/Surface;)V"); |
| - env->CallVoidMethod(j_media_player_.obj(), method, surface); |
| - CheckException(env); |
| -} |
| - |
| -void MediaPlayerBridge::Prepare(const MediaInfoCB& media_info_cb, |
| - const MediaErrorCB& media_error_cb, |
| - const VideoSizeChangedCB& video_size_changed_cb, |
| - const BufferingUpdateCB& buffering_update_cb, |
| - const base::Closure& media_prepared_cb) { |
| - media_info_cb_ = media_info_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; |
| - CallVoidMethod("prepareAsync"); |
| + bool is_data_source_set_ = !base::android::ClearException(env); |
| + |
| + if (is_data_source_set_) { |
| + if (manager_) |
| + manager_->RequestMediaResources(this); |
| + CallVoidMethod("prepareAsync"); |
| + } else { |
| + media_error_cb_.Run(player_id_, MEDIA_ERROR_UNKNOWN); |
| + } |
| } |
| -void MediaPlayerBridge::Start(const base::Closure& playback_complete_cb) { |
| - playback_complete_cb_ = playback_complete_cb; |
| - CallVoidMethod("start"); |
| +void MediaPlayerBridge::Start() { |
| + if (j_media_player_.is_null()) { |
| + pending_play_ = true; |
| + Prepare(); |
| + } else { |
| + if (prepared_) |
| + StartInternal(); |
| + else |
| + pending_play_ = true; |
| + } |
| } |
| void MediaPlayerBridge::Pause() { |
| - CallVoidMethod("pause"); |
| + if (j_media_player_.is_null()) { |
| + pending_play_ = false; |
| + } else { |
| + if (prepared_ && IsPlaying()) |
| + PauseInternal(); |
| + else |
| + pending_play_ = false; |
| + } |
| } |
| -void MediaPlayerBridge::Stop() { |
| - CallVoidMethod("stop"); |
| -} |
| bool MediaPlayerBridge::IsPlaying() { |
| + if (!prepared_) |
| + return pending_play_; |
| + |
| JNIEnv* env = AttachCurrentThread(); |
| CHECK(env); |
| - jmethodID method = GetMethodID(env, |
| - j_media_player_class_, |
| - "isPlaying", |
| + jmethodID method = GetMethodID(env, j_media_player_class_, "isPlaying", |
| "()Z"); |
| - jint result = env->CallBooleanMethod(j_media_player_.obj(), method); |
| + jboolean result = env->CallBooleanMethod(j_media_player_.obj(), method); |
| CheckException(env); |
| return result; |
| } |
| int MediaPlayerBridge::GetVideoWidth() { |
| + if (!prepared_) |
| + return width_; |
| return CallIntMethod("getVideoWidth"); |
| } |
| int MediaPlayerBridge::GetVideoHeight() { |
| + if (!prepared_) |
| + return height_; |
| return CallIntMethod("getVideoHeight"); |
| } |
| -void MediaPlayerBridge::SeekTo(base::TimeDelta time, |
| - const base::Closure& seek_complete_cb) { |
| - seek_complete_cb_ = seek_complete_cb; |
| - JNIEnv* env = AttachCurrentThread(); |
| - CHECK(env); |
| +void MediaPlayerBridge::SeekTo(base::TimeDelta time) { |
| + // Record the time to seek when OnMediaPrepared() is called. |
| + pending_seek_ = time; |
| - jmethodID method = GetMethodID(env, j_media_player_class_, "seekTo", "(I)V"); |
| - DCHECK(method); |
| - int time_msec = static_cast<int>(time.InMilliseconds()); |
| - DCHECK_EQ(time.InMilliseconds(), static_cast<int64>(time_msec)); |
| - env->CallVoidMethod(j_media_player_.obj(), |
| - method, |
| - time_msec); |
| - CheckException(env); |
| + if (j_media_player_.is_null()) |
| + Prepare(); |
| + else if (prepared_) |
| + SeekInternal(time); |
| } |
| base::TimeDelta MediaPlayerBridge::GetCurrentTime() { |
| + if (!prepared_) |
| + return pending_seek_; |
| return base::TimeDelta::FromMilliseconds(CallIntMethod("getCurrentPosition")); |
| } |
| base::TimeDelta MediaPlayerBridge::GetDuration() { |
| + if (!prepared_) |
| + return duration_; |
| return base::TimeDelta::FromMilliseconds(CallIntMethod("getDuration")); |
| } |
| -void MediaPlayerBridge::Reset() { |
| - CallVoidMethod("reset"); |
| +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); |
| + CallVoidMethod("release"); |
| + 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); |
| @@ -239,62 +334,64 @@ void MediaPlayerBridge::SetVolume(float left_volume, float right_volume) { |
| CheckException(env); |
| } |
| -void MediaPlayerBridge::SetStayAwakeWhilePlaying() { |
| - JNIEnv* env = AttachCurrentThread(); |
| - CHECK(env); |
| - |
| - jobject j_context = base::android::GetApplicationContext(); |
| - DCHECK(j_context); |
| - jint j_mode = kAndroidFullWakeLock; |
| - jmethodID method = GetMethodID(env, j_media_player_class_, |
| - "setWakeMode", "(Landroid/content/Context;I)V"); |
| - env->CallVoidMethod(j_media_player_.obj(), method, j_context, j_mode); |
| - CheckException(env); |
| +void MediaPlayerBridge::DoTimeUpdate() { |
| + base::TimeDelta current = GetCurrentTime(); |
| + time_update_cb_.Run(player_id_, current); |
| } |
| -void MediaPlayerBridge::OnMediaError(JNIEnv* /* env */, |
| - jobject /* obj */, |
| - jint error_type) { |
| - media_error_cb_.Run(error_type); |
| +void MediaPlayerBridge::OnMediaError(int error_type) { |
| + media_error_cb_.Run(player_id_, error_type); |
| } |
| -void MediaPlayerBridge::OnMediaInfo(JNIEnv* /* env */, |
| - jobject /* obj */, |
| - jint info_type) { |
| - media_info_cb_.Run(info_type); |
| +void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) { |
| + width_ = width; |
| + height_ = height_; |
| + video_size_changed_cb_.Run(player_id_, width, height); |
| } |
| -void MediaPlayerBridge::OnVideoSizeChanged(JNIEnv* /* env */, |
| - jobject /* obj */, |
| - jint width, |
| - jint height) { |
| - video_size_changed_cb_.Run(width, height); |
| +void MediaPlayerBridge::OnBufferingUpdate(int percent) { |
| + buffering_update_cb_.Run(player_id_, percent); |
| } |
| -void MediaPlayerBridge::OnBufferingUpdate(JNIEnv* /* env */, |
| - jobject /* obj */, |
| - jint percent) { |
| - buffering_update_cb_.Run(percent); |
| +void MediaPlayerBridge::OnPlaybackComplete() { |
| + time_update_timer_.Stop(); |
| + playback_complete_cb_.Run(player_id_); |
| } |
| -void MediaPlayerBridge::OnPlaybackComplete(JNIEnv* /* env */, |
| - jobject /* obj */) { |
| - playback_complete_cb_.Run(); |
| +void MediaPlayerBridge::OnSeekComplete() { |
| + seek_complete_cb_.Run(player_id_, GetCurrentTime()); |
| } |
| -void MediaPlayerBridge::OnSeekComplete(JNIEnv* /* env */, |
| - jobject /* obj */) { |
| - seek_complete_cb_.Run(); |
| -} |
| +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; |
| + } |
| -void MediaPlayerBridge::OnMediaPrepared(JNIEnv* /* env */, |
| - jobject /* obj */) { |
| - media_prepared_cb_.Run(); |
| + GetMetadata(); |
| + |
| + media_prepared_cb_.Run(player_id_, duration_); |
| } |
| -void MediaPlayerBridge::GetMetadata(bool* can_pause, |
| - bool* can_seek_forward, |
| - bool* can_seek_backward) { |
| +void MediaPlayerBridge::GetMetadata() { |
| JNIEnv* env = AttachCurrentThread(); |
| CHECK(env); |
| @@ -311,17 +408,46 @@ void MediaPlayerBridge::GetMetadata(bool* can_pause, |
| ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/media/Metadata")); |
| jmethodID get_boolean = GetMethodID(env, cls, "getBoolean", "(I)Z"); |
| - *can_pause = env->CallBooleanMethod(j_metadata.obj(), |
| + can_pause_ = env->CallBooleanMethod(j_metadata.obj(), |
| get_boolean, |
| kPauseAvailable); |
| CheckException(env); |
| - *can_seek_backward = env->CallBooleanMethod(j_metadata.obj(), |
| + can_seek_forward_ = env->CallBooleanMethod(j_metadata.obj(), |
| + get_boolean, |
| + kSeekBackwardAvailable); |
| + CheckException(env); |
| + can_seek_backward_ = env->CallBooleanMethod(j_metadata.obj(), |
| get_boolean, |
| - kSeekBackwardAvailable); |
| + kSeekForwardAvailable); |
| CheckException(env); |
| - *can_seek_forward = env->CallBooleanMethod(j_metadata.obj(), |
| - get_boolean, |
| - kSeekForwardAvailable); |
| +} |
| + |
| +void MediaPlayerBridge::StartInternal() { |
| + CallVoidMethod("start"); |
| + if (!time_update_timer_.IsRunning()) { |
| + time_update_timer_.Start( |
| + FROM_HERE, |
| + base::TimeDelta::FromMilliseconds(kTimeUpdateInterval), |
| + this, &MediaPlayerBridge::DoTimeUpdate); |
| + } |
| +} |
| + |
| +void MediaPlayerBridge::PauseInternal() { |
| + CallVoidMethod("pause"); |
| + time_update_timer_.Stop(); |
| +} |
| + |
| +void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { |
| + JNIEnv* env = AttachCurrentThread(); |
| + CHECK(env); |
| + |
| + jmethodID method = GetMethodID(env, j_media_player_class_, "seekTo", "(I)V"); |
| + DCHECK(method); |
| + int time_msec = static_cast<int>(time.InMilliseconds()); |
| + DCHECK_EQ(time.InMilliseconds(), static_cast<int64>(time_msec)); |
| + env->CallVoidMethod(j_media_player_.obj(), |
| + method, |
| + time_msec); |
| CheckException(env); |
| } |
| @@ -352,10 +478,4 @@ int MediaPlayerBridge::CallIntMethod(std::string method_name) { |
| return j_result; |
| } |
| -bool MediaPlayerBridge::RegisterMediaPlayerListener(JNIEnv* env) { |
| - bool ret = RegisterNativesImpl(env); |
| - DCHECK(g_MediaPlayerListener_clazz); |
| - return ret; |
| -} |
| - |
| } // namespace media |