Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/base/android/media_player_bridge.h" | 5 #include "media/base/android/media_player_bridge.h" |
| 6 | 6 |
| 7 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
| 8 #include "base/android/jni_string.h" | 8 #include "base/android/jni_string.h" |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
| 12 | 12 #include "base/message_loop_proxy.h" |
| 13 // Auto generated jni class from MediaPlayerListener.java. | 13 #include "media/base/android/cookie_getter.h" |
| 14 // Check base/android/jni_generator/golden_sample_for_tests_jni.h for example. | 14 #include "media/base/android/media_player_bridge_manager.h" |
| 15 #include "jni/MediaPlayerListener_jni.h" | 15 #include "media/base/android/media_player_listener.h" |
| 16 | 16 |
| 17 using base::android::AttachCurrentThread; | 17 using base::android::AttachCurrentThread; |
| 18 using base::android::CheckException; | 18 using base::android::CheckException; |
| 19 using base::android::ConvertUTF8ToJavaString; | 19 using base::android::ConvertUTF8ToJavaString; |
| 20 using base::android::GetClass; | 20 using base::android::GetClass; |
| 21 using base::android::GetMethodID; | 21 using base::android::GetMethodID; |
| 22 using base::android::JavaRef; | 22 using base::android::JavaRef; |
| 23 using base::android::ScopedJavaLocalRef; | 23 using base::android::ScopedJavaLocalRef; |
| 24 | 24 |
| 25 // These constants are from the android source tree and need to be kept in | 25 // These constants are from the android source tree and need to be kept in |
| 26 // sync with android/media/MediaMetadata.java. | 26 // sync with android/media/MediaMetadata.java. |
| 27 static const jint kPauseAvailable = 1; | 27 static const jint kPauseAvailable = 1; |
| 28 static const jint kSeekBackwardAvailable = 2; | 28 static const jint kSeekBackwardAvailable = 2; |
| 29 static const jint kSeekForwardAvailable = 3; | 29 static const jint kSeekForwardAvailable = 3; |
| 30 | 30 |
| 31 // This needs to be kept in sync with android.os.PowerManager | 31 // This needs to be kept in sync with android.os.PowerManager |
| 32 static const int kAndroidFullWakeLock = 26; | 32 static const int kAndroidFullWakeLock = 26; |
| 33 | 33 |
| 34 // Time update happens every 250ms. | |
| 35 static const int kTimeUpdateInterval = 250; | |
| 36 | |
| 37 // Because we create the media player lazily on android, the duration of the | |
| 38 // media is initially unknown to us. This makes the user unable to perform | |
| 39 // seek. To solve this problem, we use a temporary duration of 100 seconds when | |
| 40 // the duration is unknown. And we scale the seek position later when duration | |
| 41 // is available. | |
| 42 // TODO(qinmin): create a thread and use android MediaMetadataRetriever | |
| 43 // class to extract the duration. | |
| 44 static const int kTemporaryDuration = 100; | |
| 45 | |
| 34 namespace media { | 46 namespace media { |
| 35 | 47 |
| 36 MediaPlayerBridge::MediaPlayerBridge() { | 48 MediaPlayerBridge::MediaPlayerBridge( |
| 49 int player_id, | |
| 50 const std::string& url, | |
| 51 const std::string& first_party_for_cookies, | |
| 52 media::CookieGetter* cookie_getter, | |
| 53 bool hide_url_log, | |
| 54 media::MediaPlayerBridgeManager* manager, | |
| 55 const MediaErrorCB& media_error_cb, | |
| 56 const VideoSizeChangedCB& video_size_changed_cb, | |
| 57 const BufferingUpdateCB& buffering_update_cb, | |
| 58 const MediaPreparedCB& media_prepared_cb, | |
| 59 const PlaybackCompleteCB& playback_complete_cb, | |
| 60 const SeekCompleteCB& seek_complete_cb, | |
| 61 const TimeUpdateCB& time_update_cb) | |
| 62 : media_error_cb_(media_error_cb), | |
| 63 video_size_changed_cb_(video_size_changed_cb), | |
| 64 buffering_update_cb_(buffering_update_cb), | |
| 65 media_prepared_cb_(media_prepared_cb), | |
| 66 playback_complete_cb_(playback_complete_cb), | |
| 67 seek_complete_cb_(seek_complete_cb), | |
| 68 time_update_cb_(time_update_cb), | |
| 69 player_id_(player_id), | |
| 70 prepared_(false), | |
| 71 pending_play_(false), | |
| 72 url_(url), | |
| 73 first_party_for_cookies_(first_party_for_cookies), | |
| 74 has_cookies_(false), | |
| 75 hide_url_log_(hide_url_log), | |
| 76 duration_(base::TimeDelta::FromSeconds(kTemporaryDuration)), | |
| 77 width_(0), | |
| 78 height_(0), | |
| 79 can_pause_(true), | |
| 80 can_seek_forward_(true), | |
| 81 can_seek_backward_(true), | |
| 82 manager_(manager), | |
| 83 cookie_getter_(cookie_getter), | |
| 84 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), | |
| 85 listener_(new MediaPlayerListener(base::MessageLoopProxy::current(), | |
| 86 weak_this_.GetWeakPtr())) {} | |
| 87 | |
| 88 MediaPlayerBridge::~MediaPlayerBridge() { | |
| 89 Release(); | |
| 90 } | |
| 91 | |
| 92 void MediaPlayerBridge::InitializePlayer() { | |
| 37 JNIEnv* env = AttachCurrentThread(); | 93 JNIEnv* env = AttachCurrentThread(); |
| 38 CHECK(env); | 94 CHECK(env); |
| 39 | 95 |
| 40 j_media_player_class_.Reset(GetClass(env, "android/media/MediaPlayer")); | 96 j_media_player_class_.Reset(GetClass(env, "android/media/MediaPlayer")); |
| 41 | 97 |
| 42 jmethodID constructor = GetMethodID(env, | 98 jmethodID constructor = GetMethodID(env, |
| 43 j_media_player_class_, | 99 j_media_player_class_, |
| 44 "<init>", | 100 "<init>", |
| 45 "()V"); | 101 "()V"); |
| 46 ScopedJavaLocalRef<jobject> tmp(env, | 102 ScopedJavaLocalRef<jobject> tmp(env, |
| 47 env->NewObject(j_media_player_class_.obj(), constructor)); | 103 env->NewObject(j_media_player_class_.obj(), constructor)); |
| 48 j_media_player_.Reset(tmp); | 104 j_media_player_.Reset(tmp); |
| 49 | 105 |
| 50 ScopedJavaLocalRef<jobject> j_listener( | 106 ScopedJavaLocalRef<jobject> j_listener( |
| 51 Java_MediaPlayerListener_create(env, | 107 listener_->CreateMediaPlayerListener()); |
| 52 reinterpret_cast<intptr_t>(this))); | |
| 53 DCHECK(!j_listener.is_null()); | |
| 54 | 108 |
| 55 // Set it as the various listeners. | 109 // Set it as the various listeners. |
| 56 const char* listeners[] = { | 110 const char* listeners[] = { |
| 57 "OnBufferingUpdateListener", | 111 "OnBufferingUpdateListener", |
| 58 "OnCompletionListener", | 112 "OnCompletionListener", |
| 59 "OnErrorListener", | 113 "OnErrorListener", |
| 60 "OnInfoListener", | |
| 61 "OnPreparedListener", | 114 "OnPreparedListener", |
| 62 "OnSeekCompleteListener", | 115 "OnSeekCompleteListener", |
| 63 "OnVideoSizeChangedListener", | 116 "OnVideoSizeChangedListener", |
| 64 }; | 117 }; |
| 65 for (unsigned int i = 0; i < arraysize(listeners); ++i) { | 118 for (unsigned int i = 0; i < arraysize(listeners); ++i) { |
| 66 std::string signature = StringPrintf("(Landroid/media/MediaPlayer$%s;)V", | 119 std::string signature = StringPrintf("(Landroid/media/MediaPlayer$%s;)V", |
| 67 listeners[i]); | 120 listeners[i]); |
| 68 std::string method_name = StringPrintf("set%s", listeners[i]); | 121 std::string method_name = StringPrintf("set%s", listeners[i]); |
| 69 jmethodID method = GetMethodID(env, | 122 jmethodID method = GetMethodID(env, |
| 70 j_media_player_class_, | 123 j_media_player_class_, |
| 71 method_name.c_str(), | 124 method_name.c_str(), |
| 72 signature.c_str()); | 125 signature.c_str()); |
| 73 env->CallVoidMethod(j_media_player_.obj(), method, j_listener.obj()); | 126 env->CallVoidMethod(j_media_player_.obj(), method, j_listener.obj()); |
| 74 CheckException(env); | 127 CheckException(env); |
| 75 } | 128 } |
| 129 | |
| 130 jobject j_context = base::android::GetApplicationContext(); | |
| 131 DCHECK(j_context); | |
| 132 jmethodID method = GetMethodID(env, j_media_player_class_, | |
| 133 "setWakeMode", "(Landroid/content/Context;I)V"); | |
| 134 env->CallVoidMethod(j_media_player_.obj(), method, j_context, | |
| 135 kAndroidFullWakeLock); | |
| 136 CheckException(env); | |
| 76 } | 137 } |
| 77 | 138 |
| 78 MediaPlayerBridge::~MediaPlayerBridge() { | 139 void MediaPlayerBridge::SetVideoSurface(jobject surface) { |
| 79 SetVideoSurface(NULL); | 140 if (j_media_player_.is_null() && surface != NULL) |
| 80 CallVoidMethod("release"); | 141 Prepare(); |
| 142 | |
| 143 JNIEnv* env = AttachCurrentThread(); | |
| 144 CHECK(env); | |
| 145 | |
| 146 jmethodID method = GetMethodID(env, | |
| 147 j_media_player_class_, | |
| 148 "setSurface", | |
| 149 "(Landroid/view/Surface;)V"); | |
| 150 env->CallVoidMethod(j_media_player_.obj(), method, surface); | |
| 151 CheckException(env); | |
| 81 } | 152 } |
| 82 | 153 |
| 83 void MediaPlayerBridge::SetDataSource( | 154 void MediaPlayerBridge::Prepare() { |
| 84 const std::string& url, | 155 if (j_media_player_.is_null()) |
| 85 const std::string& cookies, | 156 InitializePlayer(); |
| 86 bool hide_url_log) { | 157 |
| 158 if (has_cookies_) { | |
| 159 GetCookiesCallback(cookies_); | |
| 160 } else { | |
| 161 cookie_getter_->GetCookies(url_, first_party_for_cookies_, base::Bind( | |
| 162 &MediaPlayerBridge::GetCookiesCallback, weak_this_.GetWeakPtr())); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 void MediaPlayerBridge::GetCookiesCallback(std::string cookies) { | |
| 167 cookies_ = cookies; | |
| 168 has_cookies_ = true; | |
| 169 | |
| 87 JNIEnv* env = AttachCurrentThread(); | 170 JNIEnv* env = AttachCurrentThread(); |
| 88 CHECK(env); | 171 CHECK(env); |
| 89 | 172 |
| 90 // Create a Java String for the URL. | 173 // Create a Java String for the URL. |
| 91 ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url); | 174 ScopedJavaLocalRef<jstring> j_url_string = |
| 175 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
| |
| 92 | 176 |
| 93 // Create the android.net.Uri object. | 177 // Create the android.net.Uri object. |
| 94 ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/net/Uri")); | 178 ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/net/Uri")); |
| 95 jmethodID method = GetStaticMethodID(env, cls, | 179 jmethodID method = GetStaticMethodID(env, cls, |
| 96 "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); | 180 "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); |
| 97 ScopedJavaLocalRef<jobject> j_uri(env, | 181 ScopedJavaLocalRef<jobject> j_uri(env, |
| 98 env->CallStaticObjectMethod(cls.obj(), method, j_url_string.obj())); | 182 env->CallStaticObjectMethod(cls.obj(), method, j_url_string.obj())); |
| 99 | 183 |
| 100 // Create the java.util.Map. | 184 // Create the java.util.Map. |
| 101 cls.Reset(GetClass(env, "java/util/HashMap")); | 185 cls.Reset(GetClass(env, "java/util/HashMap")); |
| 102 jmethodID constructor = GetMethodID(env, cls, "<init>", "()V"); | 186 jmethodID constructor = GetMethodID(env, cls, "<init>", "()V"); |
| 103 ScopedJavaLocalRef<jobject> j_map(env, | 187 ScopedJavaLocalRef<jobject> j_map(env, |
| 104 env->NewObject(cls.obj(), constructor)); | 188 env->NewObject(cls.obj(), constructor)); |
| 105 jmethodID put_method = GetMethodID(env, cls, "put", | 189 jmethodID put_method = GetMethodID(env, cls, "put", |
| 106 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); | 190 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| 107 | 191 |
| 108 // Construct headers that needs to be sent with the url. | 192 // Construct headers that needs to be sent with the url. |
| 109 HeadersMap headers; | 193 HeadersMap headers; |
| 110 // For incognito mode, we need a header to hide url log. | 194 // For incognito mode, we need a header to hide url log. |
| 111 if (hide_url_log) | 195 if (hide_url_log_) |
| 112 headers.insert(std::make_pair("x-hide-urls-from-log", "true")); | 196 headers.insert(std::make_pair("x-hide-urls-from-log", "true")); |
| 113 // If cookies are present, add them in the header. | 197 // If cookies are present, add them in the header. |
| 114 if (!cookies.empty()) | 198 if (!cookies_.empty()) |
| 115 headers.insert(std::make_pair("Cookie", cookies)); | 199 headers.insert(std::make_pair("Cookie", cookies_)); |
| 116 | 200 |
| 117 // Fill the Map with the headers. | 201 // Fill the Map with the headers. |
| 118 for (HeadersMap::const_iterator iter = headers.begin(); | 202 for (HeadersMap::const_iterator iter = headers.begin(); |
| 119 iter != headers.end(); ++iter) { | 203 iter != headers.end(); ++iter) { |
| 120 ScopedJavaLocalRef<jstring> key = ConvertUTF8ToJavaString(env, iter->first); | 204 ScopedJavaLocalRef<jstring> key = ConvertUTF8ToJavaString(env, iter->first); |
| 121 ScopedJavaLocalRef<jstring> value = | 205 ScopedJavaLocalRef<jstring> value = |
| 122 ConvertUTF8ToJavaString(env, iter->second); | 206 ConvertUTF8ToJavaString(env, iter->second); |
| 123 ScopedJavaLocalRef<jobject> result(env, | 207 ScopedJavaLocalRef<jobject> result(env, |
| 124 env->CallObjectMethod(j_map.obj(), put_method, key.obj(), value.obj())); | 208 env->CallObjectMethod(j_map.obj(), put_method, key.obj(), value.obj())); |
| 125 } | 209 } |
| 126 | 210 |
| 127 jobject j_context = base::android::GetApplicationContext(); | 211 jobject j_context = base::android::GetApplicationContext(); |
| 128 DCHECK(j_context); | 212 DCHECK(j_context); |
| 129 | 213 |
| 130 // Finally- Call the setDataSource method. | 214 // Finally- Call the setDataSource method. |
| 131 jmethodID set_data_source = | 215 jmethodID set_data_source = |
| 132 GetMethodID(env, j_media_player_class_, "setDataSource", | 216 GetMethodID(env, j_media_player_class_, "setDataSource", |
| 133 "(Landroid/content/Context;Landroid/net/Uri;Ljava/util/Map;)V"); | 217 "(Landroid/content/Context;Landroid/net/Uri;Ljava/util/Map;)V"); |
| 134 DCHECK(set_data_source); | |
| 135 env->CallVoidMethod(j_media_player_.obj(), set_data_source, j_context, | 218 env->CallVoidMethod(j_media_player_.obj(), set_data_source, j_context, |
| 136 j_uri.obj(), j_map.obj()); | 219 j_uri.obj(), j_map.obj()); |
| 137 CheckException(env); | 220 bool is_data_source_set_ = !base::android::ClearException(env); |
| 221 | |
| 222 if (is_data_source_set_) { | |
| 223 if (manager_) | |
| 224 manager_->RequestMediaResources(this); | |
| 225 CallVoidMethod("prepareAsync"); | |
| 226 } else { | |
| 227 media_error_cb_.Run(player_id_, MEDIA_ERROR_UNKNOWN); | |
| 228 } | |
| 138 } | 229 } |
| 139 | 230 |
| 140 void MediaPlayerBridge::SetVideoSurface(jobject surface) { | 231 void MediaPlayerBridge::Start() { |
| 232 if (j_media_player_.is_null()) { | |
| 233 pending_play_ = true; | |
| 234 Prepare(); | |
| 235 } else { | |
| 236 if (prepared_) | |
| 237 StartInternal(); | |
| 238 else | |
| 239 pending_play_ = true; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 void MediaPlayerBridge::Pause() { | |
| 244 if (j_media_player_.is_null()) { | |
| 245 pending_play_ = false; | |
| 246 } else { | |
| 247 if (prepared_ && IsPlaying()) | |
| 248 PauseInternal(); | |
| 249 else | |
| 250 pending_play_ = false; | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 | |
| 255 bool MediaPlayerBridge::IsPlaying() { | |
| 256 if (!prepared_) | |
| 257 return pending_play_; | |
| 258 | |
| 141 JNIEnv* env = AttachCurrentThread(); | 259 JNIEnv* env = AttachCurrentThread(); |
| 142 CHECK(env); | 260 CHECK(env); |
| 143 | 261 |
| 144 jmethodID method = GetMethodID(env, | 262 jmethodID method = GetMethodID(env, j_media_player_class_, "isPlaying", |
| 145 j_media_player_class_, | |
| 146 "setSurface", | |
| 147 "(Landroid/view/Surface;)V"); | |
| 148 env->CallVoidMethod(j_media_player_.obj(), method, surface); | |
| 149 CheckException(env); | |
| 150 } | |
| 151 | |
| 152 void MediaPlayerBridge::Prepare(const MediaInfoCB& media_info_cb, | |
| 153 const MediaErrorCB& media_error_cb, | |
| 154 const VideoSizeChangedCB& video_size_changed_cb, | |
| 155 const BufferingUpdateCB& buffering_update_cb, | |
| 156 const base::Closure& media_prepared_cb) { | |
| 157 media_info_cb_ = media_info_cb; | |
| 158 media_error_cb_ = media_error_cb, | |
| 159 video_size_changed_cb_ = video_size_changed_cb; | |
| 160 buffering_update_cb_ = buffering_update_cb; | |
| 161 media_prepared_cb_ = media_prepared_cb; | |
| 162 CallVoidMethod("prepareAsync"); | |
| 163 } | |
| 164 | |
| 165 void MediaPlayerBridge::Start(const base::Closure& playback_complete_cb) { | |
| 166 playback_complete_cb_ = playback_complete_cb; | |
| 167 CallVoidMethod("start"); | |
| 168 } | |
| 169 | |
| 170 void MediaPlayerBridge::Pause() { | |
| 171 CallVoidMethod("pause"); | |
| 172 } | |
| 173 | |
| 174 void MediaPlayerBridge::Stop() { | |
| 175 CallVoidMethod("stop"); | |
| 176 } | |
| 177 | |
| 178 bool MediaPlayerBridge::IsPlaying() { | |
| 179 JNIEnv* env = AttachCurrentThread(); | |
| 180 CHECK(env); | |
| 181 | |
| 182 jmethodID method = GetMethodID(env, | |
| 183 j_media_player_class_, | |
| 184 "isPlaying", | |
| 185 "()Z"); | 263 "()Z"); |
| 186 jint result = env->CallBooleanMethod(j_media_player_.obj(), method); | 264 jboolean result = env->CallBooleanMethod(j_media_player_.obj(), method); |
| 187 CheckException(env); | 265 CheckException(env); |
| 188 | 266 |
| 189 return result; | 267 return result; |
| 190 } | 268 } |
| 191 | 269 |
| 192 int MediaPlayerBridge::GetVideoWidth() { | 270 int MediaPlayerBridge::GetVideoWidth() { |
| 271 if (!prepared_) | |
| 272 return width_; | |
| 193 return CallIntMethod("getVideoWidth"); | 273 return CallIntMethod("getVideoWidth"); |
| 194 } | 274 } |
| 195 | 275 |
| 196 int MediaPlayerBridge::GetVideoHeight() { | 276 int MediaPlayerBridge::GetVideoHeight() { |
| 277 if (!prepared_) | |
| 278 return height_; | |
| 197 return CallIntMethod("getVideoHeight"); | 279 return CallIntMethod("getVideoHeight"); |
| 198 } | 280 } |
| 199 | 281 |
| 200 void MediaPlayerBridge::SeekTo(base::TimeDelta time, | 282 void MediaPlayerBridge::SeekTo(base::TimeDelta time) { |
| 201 const base::Closure& seek_complete_cb) { | 283 // Record the time to seek when OnMediaPrepared() is called. |
| 202 seek_complete_cb_ = seek_complete_cb; | 284 pending_seek_ = time; |
| 203 JNIEnv* env = AttachCurrentThread(); | |
| 204 CHECK(env); | |
| 205 | 285 |
| 206 jmethodID method = GetMethodID(env, j_media_player_class_, "seekTo", "(I)V"); | 286 if (j_media_player_.is_null()) |
| 207 DCHECK(method); | 287 Prepare(); |
| 208 int time_msec = static_cast<int>(time.InMilliseconds()); | 288 else if (prepared_) |
| 209 DCHECK_EQ(time.InMilliseconds(), static_cast<int64>(time_msec)); | 289 SeekInternal(time); |
| 210 env->CallVoidMethod(j_media_player_.obj(), | |
| 211 method, | |
| 212 time_msec); | |
| 213 CheckException(env); | |
| 214 } | 290 } |
| 215 | 291 |
| 216 base::TimeDelta MediaPlayerBridge::GetCurrentTime() { | 292 base::TimeDelta MediaPlayerBridge::GetCurrentTime() { |
| 293 if (!prepared_) | |
| 294 return pending_seek_; | |
| 217 return base::TimeDelta::FromMilliseconds(CallIntMethod("getCurrentPosition")); | 295 return base::TimeDelta::FromMilliseconds(CallIntMethod("getCurrentPosition")); |
| 218 } | 296 } |
| 219 | 297 |
| 220 base::TimeDelta MediaPlayerBridge::GetDuration() { | 298 base::TimeDelta MediaPlayerBridge::GetDuration() { |
| 299 if (!prepared_) | |
| 300 return duration_; | |
| 221 return base::TimeDelta::FromMilliseconds(CallIntMethod("getDuration")); | 301 return base::TimeDelta::FromMilliseconds(CallIntMethod("getDuration")); |
| 222 } | 302 } |
| 223 | 303 |
| 224 void MediaPlayerBridge::Reset() { | 304 void MediaPlayerBridge::Release() { |
| 225 CallVoidMethod("reset"); | 305 if (j_media_player_.is_null()) |
| 306 return; | |
| 307 | |
| 308 time_update_timer_.Stop(); | |
| 309 if (prepared_) | |
| 310 pending_seek_ = GetCurrentTime(); | |
| 311 if (manager_) | |
| 312 manager_->ReleaseMediaResources(this); | |
| 313 prepared_ = false; | |
| 314 pending_play_ = false; | |
| 315 SetVideoSurface(NULL); | |
| 316 CallVoidMethod("release"); | |
| 317 j_media_player_.Reset(); | |
| 226 } | 318 } |
| 227 | 319 |
| 228 void MediaPlayerBridge::SetVolume(float left_volume, float right_volume) { | 320 void MediaPlayerBridge::SetVolume(float left_volume, float right_volume) { |
| 321 if (j_media_player_.is_null()) | |
| 322 return; | |
| 323 | |
| 229 JNIEnv* env = AttachCurrentThread(); | 324 JNIEnv* env = AttachCurrentThread(); |
| 230 CHECK(env); | 325 CHECK(env); |
| 231 | 326 |
| 232 jmethodID method = GetMethodID(env, | 327 jmethodID method = GetMethodID(env, |
| 233 j_media_player_class_, | 328 j_media_player_class_, |
| 234 "setVolume", | 329 "setVolume", |
| 235 "(FF)V"); | 330 "(FF)V"); |
| 236 DCHECK(method); | 331 DCHECK(method); |
| 237 env->CallVoidMethod(j_media_player_.obj(), method, | 332 env->CallVoidMethod(j_media_player_.obj(), method, |
| 238 left_volume, right_volume); | 333 left_volume, right_volume); |
| 239 CheckException(env); | 334 CheckException(env); |
| 240 } | 335 } |
| 241 | 336 |
| 242 void MediaPlayerBridge::SetStayAwakeWhilePlaying() { | 337 void MediaPlayerBridge::DoTimeUpdate() { |
| 338 base::TimeDelta current = GetCurrentTime(); | |
| 339 time_update_cb_.Run(player_id_, current); | |
| 340 } | |
| 341 | |
| 342 void MediaPlayerBridge::OnMediaError(int error_type) { | |
| 343 media_error_cb_.Run(player_id_, error_type); | |
| 344 } | |
| 345 | |
| 346 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) { | |
| 347 width_ = width; | |
| 348 height_ = height_; | |
| 349 video_size_changed_cb_.Run(player_id_, width, height); | |
| 350 } | |
| 351 | |
| 352 void MediaPlayerBridge::OnBufferingUpdate(int percent) { | |
| 353 buffering_update_cb_.Run(player_id_, percent); | |
| 354 } | |
| 355 | |
| 356 void MediaPlayerBridge::OnPlaybackComplete() { | |
| 357 time_update_timer_.Stop(); | |
| 358 playback_complete_cb_.Run(player_id_); | |
| 359 } | |
| 360 | |
| 361 void MediaPlayerBridge::OnSeekComplete() { | |
| 362 seek_complete_cb_.Run(player_id_, GetCurrentTime()); | |
| 363 } | |
| 364 | |
| 365 void MediaPlayerBridge::OnMediaPrepared() { | |
| 366 if (j_media_player_.is_null()) | |
| 367 return; | |
| 368 | |
| 369 prepared_ = true; | |
| 370 | |
| 371 base::TimeDelta dur = duration_; | |
| 372 duration_ = GetDuration(); | |
| 373 | |
| 374 if (duration_ != dur && 0 != dur.InMilliseconds()) { | |
| 375 // Scale the |pending_seek_| according to the new duration. | |
| 376 pending_seek_ = base::TimeDelta::FromSeconds( | |
| 377 pending_seek_.InSecondsF() * duration_.InSecondsF() / dur.InSecondsF()); | |
| 378 } | |
| 379 | |
| 380 // If media player was recovered from a saved state, consume all the pending | |
| 381 // events. | |
| 382 SeekInternal(pending_seek_); | |
| 383 | |
| 384 if (pending_play_) { | |
| 385 StartInternal(); | |
| 386 pending_play_ = false; | |
| 387 } | |
| 388 | |
| 389 GetMetadata(); | |
| 390 | |
| 391 media_prepared_cb_.Run(player_id_, duration_); | |
| 392 } | |
| 393 | |
| 394 void MediaPlayerBridge::GetMetadata() { | |
| 243 JNIEnv* env = AttachCurrentThread(); | 395 JNIEnv* env = AttachCurrentThread(); |
| 244 CHECK(env); | 396 CHECK(env); |
| 245 | 397 |
| 246 jobject j_context = base::android::GetApplicationContext(); | |
| 247 DCHECK(j_context); | |
| 248 jint j_mode = kAndroidFullWakeLock; | |
| 249 jmethodID method = GetMethodID(env, j_media_player_class_, | |
| 250 "setWakeMode", "(Landroid/content/Context;I)V"); | |
| 251 env->CallVoidMethod(j_media_player_.obj(), method, j_context, j_mode); | |
| 252 CheckException(env); | |
| 253 } | |
| 254 | |
| 255 void MediaPlayerBridge::OnMediaError(JNIEnv* /* env */, | |
| 256 jobject /* obj */, | |
| 257 jint error_type) { | |
| 258 media_error_cb_.Run(error_type); | |
| 259 } | |
| 260 | |
| 261 void MediaPlayerBridge::OnMediaInfo(JNIEnv* /* env */, | |
| 262 jobject /* obj */, | |
| 263 jint info_type) { | |
| 264 media_info_cb_.Run(info_type); | |
| 265 } | |
| 266 | |
| 267 void MediaPlayerBridge::OnVideoSizeChanged(JNIEnv* /* env */, | |
| 268 jobject /* obj */, | |
| 269 jint width, | |
| 270 jint height) { | |
| 271 video_size_changed_cb_.Run(width, height); | |
| 272 } | |
| 273 | |
| 274 void MediaPlayerBridge::OnBufferingUpdate(JNIEnv* /* env */, | |
| 275 jobject /* obj */, | |
| 276 jint percent) { | |
| 277 buffering_update_cb_.Run(percent); | |
| 278 } | |
| 279 | |
| 280 void MediaPlayerBridge::OnPlaybackComplete(JNIEnv* /* env */, | |
| 281 jobject /* obj */) { | |
| 282 playback_complete_cb_.Run(); | |
| 283 } | |
| 284 | |
| 285 void MediaPlayerBridge::OnSeekComplete(JNIEnv* /* env */, | |
| 286 jobject /* obj */) { | |
| 287 seek_complete_cb_.Run(); | |
| 288 } | |
| 289 | |
| 290 void MediaPlayerBridge::OnMediaPrepared(JNIEnv* /* env */, | |
| 291 jobject /* obj */) { | |
| 292 media_prepared_cb_.Run(); | |
| 293 } | |
| 294 | |
| 295 void MediaPlayerBridge::GetMetadata(bool* can_pause, | |
| 296 bool* can_seek_forward, | |
| 297 bool* can_seek_backward) { | |
| 298 JNIEnv* env = AttachCurrentThread(); | |
| 299 CHECK(env); | |
| 300 | |
| 301 jmethodID method = GetMethodID(env, | 398 jmethodID method = GetMethodID(env, |
| 302 j_media_player_class_, | 399 j_media_player_class_, |
| 303 "getMetadata", | 400 "getMetadata", |
| 304 "(ZZ)Landroid/media/Metadata;"); | 401 "(ZZ)Landroid/media/Metadata;"); |
| 305 ScopedJavaLocalRef<jobject> j_metadata(env, | 402 ScopedJavaLocalRef<jobject> j_metadata(env, |
| 306 env->CallObjectMethod(j_media_player_.obj(), | 403 env->CallObjectMethod(j_media_player_.obj(), |
| 307 method, JNI_FALSE, JNI_FALSE)); | 404 method, JNI_FALSE, JNI_FALSE)); |
| 308 CheckException(env); | 405 CheckException(env); |
| 309 if (j_metadata.is_null()) | 406 if (j_metadata.is_null()) |
| 310 return; | 407 return; |
| 311 | 408 |
| 312 ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/media/Metadata")); | 409 ScopedJavaLocalRef<jclass> cls(GetClass(env, "android/media/Metadata")); |
| 313 jmethodID get_boolean = GetMethodID(env, cls, "getBoolean", "(I)Z"); | 410 jmethodID get_boolean = GetMethodID(env, cls, "getBoolean", "(I)Z"); |
| 314 *can_pause = env->CallBooleanMethod(j_metadata.obj(), | 411 can_pause_ = env->CallBooleanMethod(j_metadata.obj(), |
| 315 get_boolean, | 412 get_boolean, |
| 316 kPauseAvailable); | 413 kPauseAvailable); |
| 317 CheckException(env); | 414 CheckException(env); |
| 318 *can_seek_backward = env->CallBooleanMethod(j_metadata.obj(), | 415 can_seek_forward_ = env->CallBooleanMethod(j_metadata.obj(), |
| 416 get_boolean, | |
| 417 kSeekBackwardAvailable); | |
| 418 CheckException(env); | |
| 419 can_seek_backward_ = env->CallBooleanMethod(j_metadata.obj(), | |
| 319 get_boolean, | 420 get_boolean, |
| 320 kSeekBackwardAvailable); | 421 kSeekForwardAvailable); |
| 321 CheckException(env); | |
| 322 *can_seek_forward = env->CallBooleanMethod(j_metadata.obj(), | |
| 323 get_boolean, | |
| 324 kSeekForwardAvailable); | |
| 325 CheckException(env); | 422 CheckException(env); |
| 326 } | 423 } |
| 327 | 424 |
| 425 void MediaPlayerBridge::StartInternal() { | |
| 426 CallVoidMethod("start"); | |
| 427 if (!time_update_timer_.IsRunning()) { | |
| 428 time_update_timer_.Start( | |
| 429 FROM_HERE, | |
| 430 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval), | |
| 431 this, &MediaPlayerBridge::DoTimeUpdate); | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 void MediaPlayerBridge::PauseInternal() { | |
| 436 CallVoidMethod("pause"); | |
| 437 time_update_timer_.Stop(); | |
| 438 } | |
| 439 | |
| 440 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { | |
| 441 JNIEnv* env = AttachCurrentThread(); | |
| 442 CHECK(env); | |
| 443 | |
| 444 jmethodID method = GetMethodID(env, j_media_player_class_, "seekTo", "(I)V"); | |
| 445 DCHECK(method); | |
| 446 int time_msec = static_cast<int>(time.InMilliseconds()); | |
| 447 DCHECK_EQ(time.InMilliseconds(), static_cast<int64>(time_msec)); | |
| 448 env->CallVoidMethod(j_media_player_.obj(), | |
| 449 method, | |
| 450 time_msec); | |
| 451 CheckException(env); | |
| 452 } | |
| 453 | |
| 328 // ---- JNI Helpers for repeated call patterns. ---- | 454 // ---- JNI Helpers for repeated call patterns. ---- |
| 329 | 455 |
| 330 void MediaPlayerBridge::CallVoidMethod(std::string method_name) { | 456 void MediaPlayerBridge::CallVoidMethod(std::string method_name) { |
| 331 JNIEnv* env = AttachCurrentThread(); | 457 JNIEnv* env = AttachCurrentThread(); |
| 332 CHECK(env); | 458 CHECK(env); |
| 333 | 459 |
| 334 jmethodID method = GetMethodID(env, | 460 jmethodID method = GetMethodID(env, |
| 335 j_media_player_class_, | 461 j_media_player_class_, |
| 336 method_name.c_str(), | 462 method_name.c_str(), |
| 337 "()V"); | 463 "()V"); |
| 338 env->CallVoidMethod(j_media_player_.obj(), method); | 464 env->CallVoidMethod(j_media_player_.obj(), method); |
| 339 CheckException(env); | 465 CheckException(env); |
| 340 } | 466 } |
| 341 | 467 |
| 342 int MediaPlayerBridge::CallIntMethod(std::string method_name) { | 468 int MediaPlayerBridge::CallIntMethod(std::string method_name) { |
| 343 JNIEnv* env = AttachCurrentThread(); | 469 JNIEnv* env = AttachCurrentThread(); |
| 344 CHECK(env); | 470 CHECK(env); |
| 345 | 471 |
| 346 jmethodID method = GetMethodID(env, | 472 jmethodID method = GetMethodID(env, |
| 347 j_media_player_class_, | 473 j_media_player_class_, |
| 348 method_name.c_str(), | 474 method_name.c_str(), |
| 349 "()I"); | 475 "()I"); |
| 350 jint j_result = env->CallIntMethod(j_media_player_.obj(), method); | 476 jint j_result = env->CallIntMethod(j_media_player_.obj(), method); |
| 351 CheckException(env); | 477 CheckException(env); |
| 352 return j_result; | 478 return j_result; |
| 353 } | 479 } |
| 354 | 480 |
| 355 bool MediaPlayerBridge::RegisterMediaPlayerListener(JNIEnv* env) { | |
| 356 bool ret = RegisterNativesImpl(env); | |
| 357 DCHECK(g_MediaPlayerListener_clazz); | |
| 358 return ret; | |
| 359 } | |
| 360 | |
| 361 } // namespace media | 481 } // namespace media |
| OLD | NEW |