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_common_android.h" 15 #include "media/base/android/media_player_manager.h" 16 #include "media/base/android/media_resource_getter.h" 17 #include "media/base/android/media_url_interceptor.h" 18 19 using base::android::ConvertUTF8ToJavaString; 20 using base::android::ScopedJavaLocalRef; 21 22 namespace media { 23 24 MediaPlayerBridge::MediaPlayerBridge( 25 int player_id, 26 const GURL& url, 27 const GURL& first_party_for_cookies, 28 const std::string& user_agent, 29 bool hide_url_log, 30 MediaPlayerManager* manager, 31 const RequestMediaResourcesCB& request_media_resources_cb, 32 const GURL& frame_url, 33 bool allow_credentials) 34 : MediaPlayerAndroid(player_id, 35 manager, 36 request_media_resources_cb, 37 frame_url), 38 prepared_(false), 39 pending_play_(false), 40 should_seek_on_prepare_(false), 41 url_(url), 42 first_party_for_cookies_(first_party_for_cookies), 43 user_agent_(user_agent), 44 hide_url_log_(hide_url_log), 45 width_(0), 46 height_(0), 47 can_pause_(true), 48 can_seek_forward_(true), 49 can_seek_backward_(true), 50 volume_(-1.0), 51 allow_credentials_(allow_credentials), 52 weak_factory_(this) { 53 } 54 55 MediaPlayerBridge::~MediaPlayerBridge() { 56 if (!j_media_player_bridge_.is_null()) { 57 JNIEnv* env = base::android::AttachCurrentThread(); 58 CHECK(env); 59 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj()); 60 } 61 Release(); 62 } 63 64 void MediaPlayerBridge::Initialize() { 65 cookies_.clear(); 66 if (url_.SchemeIsFile()) { 67 ExtractMediaMetadata(url_.spec()); 68 return; 69 } 70 71 media::MediaResourceGetter* resource_getter = 72 manager()->GetMediaResourceGetter(); 73 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) { 74 resource_getter->GetPlatformPathFromURL( 75 url_, 76 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata, 77 weak_factory_.GetWeakPtr())); 78 return; 79 } 80 81 // Start extracting the metadata immediately if the request is anonymous. 82 // Otherwise, wait for user credentials to be retrieved first. 83 if (!allow_credentials_) { 84 ExtractMediaMetadata(url_.spec()); 85 return; 86 } 87 88 resource_getter->GetCookies(url_, 89 first_party_for_cookies_, 90 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved, 91 weak_factory_.GetWeakPtr())); 92 } 93 94 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() { 95 JNIEnv* env = base::android::AttachCurrentThread(); 96 CHECK(env); 97 98 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create( 99 env, reinterpret_cast<intptr_t>(this))); 100 101 if (volume_ >= 0) 102 SetVolume(volume_); 103 104 AttachListener(j_media_player_bridge_.obj()); 105 } 106 107 void MediaPlayerBridge::SetJavaMediaPlayerBridge( 108 jobject j_media_player_bridge) { 109 JNIEnv* env = base::android::AttachCurrentThread(); 110 CHECK(env); 111 112 j_media_player_bridge_.Reset(env, j_media_player_bridge); 113 } 114 115 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge:: 116 GetJavaMediaPlayerBridge() { 117 base::android::ScopedJavaLocalRef<jobject> j_bridge( 118 j_media_player_bridge_); 119 return j_bridge; 120 } 121 122 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) { 123 duration_ = duration; 124 } 125 126 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) { 127 if (j_media_player_bridge_.is_null()) { 128 if (surface.IsEmpty()) 129 return; 130 Prepare(); 131 } 132 133 JNIEnv* env = base::android::AttachCurrentThread(); 134 CHECK(env); 135 Java_MediaPlayerBridge_setSurface( 136 env, j_media_player_bridge_.obj(), surface.j_surface().obj()); 137 } 138 139 void MediaPlayerBridge::Prepare() { 140 DCHECK(j_media_player_bridge_.is_null()); 141 CreateJavaMediaPlayerBridge(); 142 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) { 143 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL( 144 url_, 145 base::Bind(&MediaPlayerBridge::SetDataSource, 146 weak_factory_.GetWeakPtr())); 147 return; 148 } 149 150 SetDataSource(url_.spec()); 151 } 152 153 void MediaPlayerBridge::SetDataSource(const std::string& url) { 154 if (j_media_player_bridge_.is_null()) 155 return; 156 157 JNIEnv* env = base::android::AttachCurrentThread(); 158 CHECK(env); 159 160 int fd; 161 int64 offset; 162 int64 size; 163 if (InterceptMediaUrl(url, &fd, &offset, &size)) { 164 if (!Java_MediaPlayerBridge_setDataSourceFromFd( 165 env, j_media_player_bridge_.obj(), fd, offset, size)) { 166 OnMediaError(MEDIA_ERROR_FORMAT); 167 return; 168 } 169 } else { 170 // Create a Java String for the URL. 171 ScopedJavaLocalRef<jstring> j_url_string = 172 ConvertUTF8ToJavaString(env, url); 173 174 jobject j_context = base::android::GetApplicationContext(); 175 DCHECK(j_context); 176 177 const std::string data_uri_prefix("data:"); 178 if (StartsWithASCII(url, data_uri_prefix, true)) { 179 if (!Java_MediaPlayerBridge_setDataUriDataSource( 180 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) { 181 OnMediaError(MEDIA_ERROR_FORMAT); 182 } 183 return; 184 } 185 186 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString( 187 env, cookies_); 188 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString( 189 env, user_agent_); 190 191 if (!Java_MediaPlayerBridge_setDataSource( 192 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(), 193 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) { 194 OnMediaError(MEDIA_ERROR_FORMAT); 195 return; 196 } 197 } 198 199 request_media_resources_cb_.Run(player_id()); 200 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj())) 201 OnMediaError(MEDIA_ERROR_FORMAT); 202 } 203 204 bool MediaPlayerBridge::InterceptMediaUrl( 205 const std::string& url, int* fd, int64* offset, int64* size) { 206 // Sentinel value to check whether the output arguments have been set. 207 const int kUnsetValue = -1; 208 209 *fd = kUnsetValue; 210 *offset = kUnsetValue; 211 *size = kUnsetValue; 212 media::MediaUrlInterceptor* url_interceptor = 213 manager()->GetMediaUrlInterceptor(); 214 if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) { 215 DCHECK_NE(kUnsetValue, *fd); 216 DCHECK_NE(kUnsetValue, *offset); 217 DCHECK_NE(kUnsetValue, *size); 218 return true; 219 } 220 return false; 221 } 222 223 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj, 224 jboolean success) { 225 if (!success) { 226 OnMediaError(MEDIA_ERROR_FORMAT); 227 return; 228 } 229 230 request_media_resources_cb_.Run(player_id()); 231 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj())) 232 OnMediaError(MEDIA_ERROR_FORMAT); 233 } 234 235 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) { 236 cookies_ = cookies; 237 manager()->GetMediaResourceGetter()->GetAuthCredentials( 238 url_, 239 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved, 240 weak_factory_.GetWeakPtr())); 241 } 242 243 void MediaPlayerBridge::OnAuthCredentialsRetrieved( 244 const base::string16& username, const base::string16& password) { 245 GURL::ReplacementsW replacements; 246 if (!username.empty()) { 247 replacements.SetUsernameStr(username); 248 if (!password.empty()) 249 replacements.SetPasswordStr(password); 250 url_ = url_.ReplaceComponents(replacements); 251 } 252 ExtractMediaMetadata(url_.spec()); 253 } 254 255 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) { 256 if (url.empty()) { 257 OnMediaError(MEDIA_ERROR_FORMAT); 258 return; 259 } 260 261 int fd; 262 int64 offset; 263 int64 size; 264 if (InterceptMediaUrl(url, &fd, &offset, &size)) { 265 manager()->GetMediaResourceGetter()->ExtractMediaMetadata( 266 fd, offset, size, 267 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted, 268 weak_factory_.GetWeakPtr())); 269 } else { 270 manager()->GetMediaResourceGetter()->ExtractMediaMetadata( 271 url, cookies_, user_agent_, 272 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted, 273 weak_factory_.GetWeakPtr())); 274 } 275 } 276 277 void MediaPlayerBridge::OnMediaMetadataExtracted( 278 base::TimeDelta duration, int width, int height, bool success) { 279 if (success) { 280 duration_ = duration; 281 width_ = width; 282 height_ = height; 283 } 284 manager()->OnMediaMetadataChanged( 285 player_id(), duration_, width_, height_, success); 286 } 287 288 void MediaPlayerBridge::Start() { 289 if (j_media_player_bridge_.is_null()) { 290 pending_play_ = true; 291 Prepare(); 292 } else { 293 if (prepared_) 294 StartInternal(); 295 else 296 pending_play_ = true; 297 } 298 } 299 300 void MediaPlayerBridge::Pause(bool is_media_related_action) { 301 if (j_media_player_bridge_.is_null()) { 302 pending_play_ = false; 303 } else { 304 if (prepared_ && IsPlaying()) 305 PauseInternal(); 306 else 307 pending_play_ = false; 308 } 309 } 310 311 bool MediaPlayerBridge::IsPlaying() { 312 if (!prepared_) 313 return pending_play_; 314 315 JNIEnv* env = base::android::AttachCurrentThread(); 316 CHECK(env); 317 jboolean result = Java_MediaPlayerBridge_isPlaying( 318 env, j_media_player_bridge_.obj()); 319 return result; 320 } 321 322 int MediaPlayerBridge::GetVideoWidth() { 323 if (!prepared_) 324 return width_; 325 JNIEnv* env = base::android::AttachCurrentThread(); 326 return Java_MediaPlayerBridge_getVideoWidth( 327 env, j_media_player_bridge_.obj()); 328 } 329 330 int MediaPlayerBridge::GetVideoHeight() { 331 if (!prepared_) 332 return height_; 333 JNIEnv* env = base::android::AttachCurrentThread(); 334 return Java_MediaPlayerBridge_getVideoHeight( 335 env, j_media_player_bridge_.obj()); 336 } 337 338 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) { 339 // Record the time to seek when OnMediaPrepared() is called. 340 pending_seek_ = timestamp; 341 should_seek_on_prepare_ = true; 342 343 if (j_media_player_bridge_.is_null()) 344 Prepare(); 345 else if (prepared_) 346 SeekInternal(timestamp); 347 } 348 349 base::TimeDelta MediaPlayerBridge::GetCurrentTime() { 350 if (!prepared_) 351 return pending_seek_; 352 JNIEnv* env = base::android::AttachCurrentThread(); 353 return base::TimeDelta::FromMilliseconds( 354 Java_MediaPlayerBridge_getCurrentPosition( 355 env, j_media_player_bridge_.obj())); 356 } 357 358 base::TimeDelta MediaPlayerBridge::GetDuration() { 359 if (!prepared_) 360 return duration_; 361 JNIEnv* env = base::android::AttachCurrentThread(); 362 return base::TimeDelta::FromMilliseconds( 363 Java_MediaPlayerBridge_getDuration( 364 env, j_media_player_bridge_.obj())); 365 } 366 367 void MediaPlayerBridge::Release() { 368 if (j_media_player_bridge_.is_null()) 369 return; 370 371 time_update_timer_.Stop(); 372 if (prepared_) { 373 pending_seek_ = GetCurrentTime(); 374 should_seek_on_prepare_ = true; 375 } 376 377 prepared_ = false; 378 pending_play_ = false; 379 SetVideoSurface(gfx::ScopedJavaSurface()); 380 JNIEnv* env = base::android::AttachCurrentThread(); 381 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj()); 382 j_media_player_bridge_.Reset(); 383 DetachListener(); 384 } 385 386 void MediaPlayerBridge::SetVolume(double volume) { 387 if (j_media_player_bridge_.is_null()) { 388 volume_ = volume; 389 return; 390 } 391 392 JNIEnv* env = base::android::AttachCurrentThread(); 393 CHECK(env); 394 Java_MediaPlayerBridge_setVolume( 395 env, j_media_player_bridge_.obj(), volume); 396 } 397 398 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) { 399 width_ = width; 400 height_ = height; 401 MediaPlayerAndroid::OnVideoSizeChanged(width, height); 402 } 403 404 void MediaPlayerBridge::OnPlaybackComplete() { 405 time_update_timer_.Stop(); 406 MediaPlayerAndroid::OnPlaybackComplete(); 407 } 408 409 void MediaPlayerBridge::OnMediaInterrupted() { 410 time_update_timer_.Stop(); 411 MediaPlayerAndroid::OnMediaInterrupted(); 412 } 413 414 void MediaPlayerBridge::OnMediaPrepared() { 415 if (j_media_player_bridge_.is_null()) 416 return; 417 418 prepared_ = true; 419 duration_ = GetDuration(); 420 421 // If media player was recovered from a saved state, consume all the pending 422 // events. 423 if (should_seek_on_prepare_) { 424 PendingSeekInternal(pending_seek_); 425 pending_seek_ = base::TimeDelta::FromMilliseconds(0); 426 should_seek_on_prepare_ = false; 427 } 428 429 if (pending_play_) { 430 StartInternal(); 431 pending_play_ = false; 432 } 433 434 UpdateAllowedOperations(); 435 manager()->OnMediaMetadataChanged( 436 player_id(), duration_, width_, height_, true); 437 } 438 439 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() { 440 JNIEnv* env = base::android::AttachCurrentThread(); 441 CHECK(env); 442 443 return Java_MediaPlayerBridge_getAllowedOperations( 444 env, j_media_player_bridge_.obj()); 445 } 446 447 void MediaPlayerBridge::UpdateAllowedOperations() { 448 JNIEnv* env = base::android::AttachCurrentThread(); 449 CHECK(env); 450 451 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations(); 452 453 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj()); 454 can_seek_forward_ = Java_AllowedOperations_canSeekForward( 455 env, allowedOperations.obj()); 456 can_seek_backward_ = Java_AllowedOperations_canSeekBackward( 457 env, allowedOperations.obj()); 458 } 459 460 void MediaPlayerBridge::StartInternal() { 461 JNIEnv* env = base::android::AttachCurrentThread(); 462 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj()); 463 if (!time_update_timer_.IsRunning()) { 464 time_update_timer_.Start( 465 FROM_HERE, 466 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval), 467 this, &MediaPlayerBridge::OnTimeUpdateTimerFired); 468 } 469 } 470 471 void MediaPlayerBridge::PauseInternal() { 472 JNIEnv* env = base::android::AttachCurrentThread(); 473 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj()); 474 time_update_timer_.Stop(); 475 } 476 477 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) { 478 SeekInternal(time); 479 } 480 481 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { 482 if (time > duration_) 483 time = duration_; 484 485 // Seeking to an invalid position may cause media player to stuck in an 486 // error state. 487 if (time < base::TimeDelta()) { 488 DCHECK_EQ(-1.0, time.InMillisecondsF()); 489 return; 490 } 491 492 JNIEnv* env = base::android::AttachCurrentThread(); 493 CHECK(env); 494 int time_msec = static_cast<int>(time.InMilliseconds()); 495 Java_MediaPlayerBridge_seekTo( 496 env, j_media_player_bridge_.obj(), time_msec); 497 } 498 499 void MediaPlayerBridge::OnTimeUpdateTimerFired() { 500 manager()->OnTimeUpdate( 501 player_id(), GetCurrentTime(), base::TimeTicks::Now()); 502 } 503 504 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) { 505 bool ret = RegisterNativesImpl(env); 506 DCHECK(g_MediaPlayerBridge_clazz); 507 return ret; 508 } 509 510 bool MediaPlayerBridge::CanPause() { 511 return can_pause_; 512 } 513 514 bool MediaPlayerBridge::CanSeekForward() { 515 return can_seek_forward_; 516 } 517 518 bool MediaPlayerBridge::CanSeekBackward() { 519 return can_seek_backward_; 520 } 521 522 bool MediaPlayerBridge::IsPlayerReady() { 523 return prepared_; 524 } 525 526 GURL MediaPlayerBridge::GetUrl() { 527 return url_; 528 } 529 530 GURL MediaPlayerBridge::GetFirstPartyForCookies() { 531 return first_party_for_cookies_; 532 } 533 534 } // namespace media 535