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 3 // found in the LICENSE file. 4 5 #include "media/base/android/media_player_bridge.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/basictypes.h" 10 #include "base/logging.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/strings/string_util.h" 13 #include "jni/MediaPlayerBridge_jni.h" 14 #include "media/base/android/media_player_manager.h" 15 #include "media/base/android/media_resource_getter.h" 16 17 using base::android::ConvertUTF8ToJavaString; 18 using base::android::ScopedJavaLocalRef; 19 20 // Time update happens every 250ms. 21 static const int kTimeUpdateInterval = 250; 22 23 namespace media { 24 25 MediaPlayerBridge::MediaPlayerBridge( 26 int player_id, 27 const GURL& url, 28 const GURL& first_party_for_cookies, 29 bool hide_url_log, 30 MediaPlayerManager* manager) 31 : MediaPlayerAndroid(player_id, 32 manager), 33 prepared_(false), 34 pending_play_(false), 35 url_(url), 36 first_party_for_cookies_(first_party_for_cookies), 37 hide_url_log_(hide_url_log), 38 width_(0), 39 height_(0), 40 can_pause_(true), 41 can_seek_forward_(true), 42 can_seek_backward_(true), 43 weak_this_(this), 44 listener_(base::MessageLoopProxy::current(), 45 weak_this_.GetWeakPtr()) { 46 } 47 48 MediaPlayerBridge::~MediaPlayerBridge() { 49 if (!j_media_player_bridge_.is_null()) { 50 JNIEnv* env = base::android::AttachCurrentThread(); 51 CHECK(env); 52 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj()); 53 } 54 Release(); 55 } 56 57 void MediaPlayerBridge::Initialize() { 58 if (url_.SchemeIsFile()) { 59 cookies_.clear(); 60 ExtractMediaMetadata(url_.spec()); 61 return; 62 } 63 64 media::MediaResourceGetter* resource_getter = 65 manager()->GetMediaResourceGetter(); 66 if (url_.SchemeIsFileSystem()) { 67 cookies_.clear(); 68 resource_getter->GetPlatformPathFromFileSystemURL(url_, base::Bind( 69 &MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr())); 70 return; 71 } 72 73 resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind( 74 &MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr())); 75 } 76 77 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() { 78 JNIEnv* env = base::android::AttachCurrentThread(); 79 CHECK(env); 80 81 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create( 82 env, reinterpret_cast<intptr_t>(this))); 83 84 SetMediaPlayerListener(); 85 } 86 87 void MediaPlayerBridge::SetJavaMediaPlayerBridge( 88 jobject j_media_player_bridge) { 89 JNIEnv* env = base::android::AttachCurrentThread(); 90 CHECK(env); 91 92 j_media_player_bridge_.Reset(env, j_media_player_bridge); 93 } 94 95 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge:: 96 GetJavaMediaPlayerBridge() { 97 base::android::ScopedJavaLocalRef<jobject> j_bridge( 98 j_media_player_bridge_); 99 return j_bridge; 100 } 101 102 void MediaPlayerBridge::SetMediaPlayerListener() { 103 jobject j_context = base::android::GetApplicationContext(); 104 DCHECK(j_context); 105 106 listener_.CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj()); 107 } 108 109 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) { 110 duration_ = duration; 111 } 112 113 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) { 114 if (j_media_player_bridge_.is_null()) { 115 if (surface.IsEmpty()) 116 return; 117 Prepare(); 118 } 119 120 JNIEnv* env = base::android::AttachCurrentThread(); 121 CHECK(env); 122 123 Java_MediaPlayerBridge_setSurface( 124 env, j_media_player_bridge_.obj(), surface.j_surface().obj()); 125 } 126 127 void MediaPlayerBridge::Prepare() { 128 DCHECK(j_media_player_bridge_.is_null()); 129 CreateJavaMediaPlayerBridge(); 130 if (url_.SchemeIsFileSystem()) { 131 manager()->GetMediaResourceGetter()->GetPlatformPathFromFileSystemURL( 132 url_, base::Bind(&MediaPlayerBridge::SetDataSource, 133 weak_this_.GetWeakPtr())); 134 } else { 135 SetDataSource(url_.spec()); 136 } 137 } 138 139 void MediaPlayerBridge::SetDataSource(const std::string& url) { 140 if (j_media_player_bridge_.is_null()) 141 return; 142 143 JNIEnv* env = base::android::AttachCurrentThread(); 144 CHECK(env); 145 146 // Create a Java String for the URL. 147 ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url); 148 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString( 149 env, cookies_); 150 151 jobject j_context = base::android::GetApplicationContext(); 152 DCHECK(j_context); 153 154 const std::string data_uri_prefix("data:"); 155 if (StartsWithASCII(url, data_uri_prefix, true)) { 156 if (!Java_MediaPlayerBridge_setDataUriDataSource( 157 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) { 158 OnMediaError(MEDIA_ERROR_FORMAT); 159 } 160 return; 161 } 162 163 if (!Java_MediaPlayerBridge_setDataSource( 164 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(), 165 j_cookies.obj(), hide_url_log_)) { 166 OnMediaError(MEDIA_ERROR_FORMAT); 167 return; 168 } 169 170 manager()->RequestMediaResources(player_id()); 171 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj())) 172 OnMediaError(MEDIA_ERROR_FORMAT); 173 } 174 175 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj, 176 jboolean success) { 177 if (!success) { 178 OnMediaError(MEDIA_ERROR_FORMAT); 179 return; 180 } 181 182 manager()->RequestMediaResources(player_id()); 183 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj())) 184 OnMediaError(MEDIA_ERROR_FORMAT); 185 } 186 187 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) { 188 cookies_ = cookies; 189 ExtractMediaMetadata(url_.spec()); 190 } 191 192 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) { 193 manager()->GetMediaResourceGetter()->ExtractMediaMetadata( 194 url, cookies_, base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted, 195 weak_this_.GetWeakPtr())); 196 } 197 198 void MediaPlayerBridge::OnMediaMetadataExtracted( 199 base::TimeDelta duration, int width, int height, bool success) { 200 if (success) { 201 duration_ = duration; 202 width_ = width; 203 height_ = height; 204 } 205 manager()->OnMediaMetadataChanged( 206 player_id(), duration_, width_, height_, success); 207 } 208 209 void MediaPlayerBridge::Start() { 210 if (j_media_player_bridge_.is_null()) { 211 pending_play_ = true; 212 Prepare(); 213 } else { 214 if (prepared_) 215 StartInternal(); 216 else 217 pending_play_ = true; 218 } 219 } 220 221 void MediaPlayerBridge::Pause(bool is_media_related_action) { 222 if (j_media_player_bridge_.is_null()) { 223 pending_play_ = false; 224 } else { 225 if (prepared_ && IsPlaying()) 226 PauseInternal(); 227 else 228 pending_play_ = false; 229 } 230 } 231 232 bool MediaPlayerBridge::IsPlaying() { 233 if (!prepared_) 234 return pending_play_; 235 236 JNIEnv* env = base::android::AttachCurrentThread(); 237 CHECK(env); 238 jboolean result = Java_MediaPlayerBridge_isPlaying( 239 env, j_media_player_bridge_.obj()); 240 return result; 241 } 242 243 int MediaPlayerBridge::GetVideoWidth() { 244 if (!prepared_) 245 return width_; 246 JNIEnv* env = base::android::AttachCurrentThread(); 247 return Java_MediaPlayerBridge_getVideoWidth( 248 env, j_media_player_bridge_.obj()); 249 } 250 251 int MediaPlayerBridge::GetVideoHeight() { 252 if (!prepared_) 253 return height_; 254 JNIEnv* env = base::android::AttachCurrentThread(); 255 return Java_MediaPlayerBridge_getVideoHeight( 256 env, j_media_player_bridge_.obj()); 257 } 258 259 void MediaPlayerBridge::SeekTo(const base::TimeDelta& timestamp) { 260 // Record the time to seek when OnMediaPrepared() is called. 261 pending_seek_ = timestamp; 262 263 if (j_media_player_bridge_.is_null()) 264 Prepare(); 265 else if (prepared_) 266 SeekInternal(timestamp); 267 } 268 269 base::TimeDelta MediaPlayerBridge::GetCurrentTime() { 270 if (!prepared_) 271 return pending_seek_; 272 JNIEnv* env = base::android::AttachCurrentThread(); 273 return base::TimeDelta::FromMilliseconds( 274 Java_MediaPlayerBridge_getCurrentPosition( 275 env, j_media_player_bridge_.obj())); 276 } 277 278 base::TimeDelta MediaPlayerBridge::GetDuration() { 279 if (!prepared_) 280 return duration_; 281 JNIEnv* env = base::android::AttachCurrentThread(); 282 return base::TimeDelta::FromMilliseconds( 283 Java_MediaPlayerBridge_getDuration( 284 env, j_media_player_bridge_.obj())); 285 } 286 287 void MediaPlayerBridge::Release() { 288 if (j_media_player_bridge_.is_null()) 289 return; 290 291 time_update_timer_.Stop(); 292 if (prepared_) 293 pending_seek_ = GetCurrentTime(); 294 prepared_ = false; 295 pending_play_ = false; 296 SetVideoSurface(gfx::ScopedJavaSurface()); 297 298 JNIEnv* env = base::android::AttachCurrentThread(); 299 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj()); 300 j_media_player_bridge_.Reset(); 301 manager()->ReleaseMediaResources(player_id()); 302 listener_.ReleaseMediaPlayerListenerResources(); 303 } 304 305 void MediaPlayerBridge::SetVolume(double volume) { 306 if (j_media_player_bridge_.is_null()) 307 return; 308 309 JNIEnv* env = base::android::AttachCurrentThread(); 310 CHECK(env); 311 Java_MediaPlayerBridge_setVolume( 312 env, j_media_player_bridge_.obj(), volume); 313 } 314 315 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) { 316 width_ = width; 317 height_ = height; 318 manager()->OnVideoSizeChanged(player_id(), width, height); 319 } 320 321 void MediaPlayerBridge::OnMediaError(int error_type) { 322 manager()->OnError(player_id(), error_type); 323 } 324 325 void MediaPlayerBridge::OnBufferingUpdate(int percent) { 326 manager()->OnBufferingUpdate(player_id(), percent); 327 } 328 329 void MediaPlayerBridge::OnPlaybackComplete() { 330 time_update_timer_.Stop(); 331 manager()->OnPlaybackComplete(player_id()); 332 } 333 334 void MediaPlayerBridge::OnMediaInterrupted() { 335 time_update_timer_.Stop(); 336 manager()->OnMediaInterrupted(player_id()); 337 } 338 339 void MediaPlayerBridge::OnSeekComplete() { 340 manager()->OnSeekComplete(player_id(), GetCurrentTime()); 341 } 342 343 void MediaPlayerBridge::OnMediaPrepared() { 344 if (j_media_player_bridge_.is_null()) 345 return; 346 347 prepared_ = true; 348 duration_ = GetDuration(); 349 350 // If media player was recovered from a saved state, consume all the pending 351 // events. 352 PendingSeekInternal(pending_seek_); 353 354 if (pending_play_) { 355 StartInternal(); 356 pending_play_ = false; 357 } 358 359 UpdateAllowedOperations(); 360 manager()->OnMediaMetadataChanged( 361 player_id(), duration_, width_, height_, true); 362 } 363 364 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() { 365 JNIEnv* env = base::android::AttachCurrentThread(); 366 CHECK(env); 367 368 return Java_MediaPlayerBridge_getAllowedOperations( 369 env, j_media_player_bridge_.obj()); 370 } 371 372 void MediaPlayerBridge::UpdateAllowedOperations() { 373 JNIEnv* env = base::android::AttachCurrentThread(); 374 CHECK(env); 375 376 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations(); 377 378 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj()); 379 can_seek_forward_ = Java_AllowedOperations_canSeekForward( 380 env, allowedOperations.obj()); 381 can_seek_backward_ = Java_AllowedOperations_canSeekBackward( 382 env, allowedOperations.obj()); 383 } 384 385 void MediaPlayerBridge::StartInternal() { 386 JNIEnv* env = base::android::AttachCurrentThread(); 387 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj()); 388 if (!time_update_timer_.IsRunning()) { 389 time_update_timer_.Start( 390 FROM_HERE, 391 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval), 392 this, &MediaPlayerBridge::OnTimeUpdateTimerFired); 393 } 394 } 395 396 void MediaPlayerBridge::PauseInternal() { 397 JNIEnv* env = base::android::AttachCurrentThread(); 398 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj()); 399 time_update_timer_.Stop(); 400 } 401 402 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) { 403 SeekInternal(time); 404 } 405 406 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { 407 if (time > duration_) 408 time = duration_; 409 410 // Seeking to an invalid position may cause media player to stuck in an 411 // error state. 412 if (time < base::TimeDelta()) { 413 DCHECK_EQ(-1.0, time.InMillisecondsF()); 414 return; 415 } 416 417 JNIEnv* env = base::android::AttachCurrentThread(); 418 CHECK(env); 419 int time_msec = static_cast<int>(time.InMilliseconds()); 420 Java_MediaPlayerBridge_seekTo( 421 env, j_media_player_bridge_.obj(), time_msec); 422 } 423 424 void MediaPlayerBridge::OnTimeUpdateTimerFired() { 425 manager()->OnTimeUpdate(player_id(), GetCurrentTime()); 426 } 427 428 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) { 429 bool ret = RegisterNativesImpl(env); 430 DCHECK(g_MediaPlayerBridge_clazz); 431 return ret; 432 } 433 434 bool MediaPlayerBridge::CanPause() { 435 return can_pause_; 436 } 437 438 bool MediaPlayerBridge::CanSeekForward() { 439 return can_seek_forward_; 440 } 441 442 bool MediaPlayerBridge::CanSeekBackward() { 443 return can_seek_backward_; 444 } 445 446 bool MediaPlayerBridge::IsPlayerReady() { 447 return prepared_; 448 } 449 450 GURL MediaPlayerBridge::GetUrl() { 451 return url_; 452 } 453 454 GURL MediaPlayerBridge::GetFirstPartyForCookies() { 455 return first_party_for_cookies_; 456 } 457 458 } // namespace media 459