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