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