1 // Copyright 2013 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 "content/renderer/media/android/webmediaplayer_android.h" 6 7 #include <limits> 8 9 #include "base/android/build_info.h" 10 #include "base/bind.h" 11 #include "base/callback_helpers.h" 12 #include "base/command_line.h" 13 #include "base/files/file_path.h" 14 #include "base/logging.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/metrics/histogram.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "cc/layers/video_layer.h" 20 #include "content/public/common/content_client.h" 21 #include "content/public/common/content_switches.h" 22 #include "content/public/renderer/render_frame.h" 23 #include "content/renderer/compositor_bindings/web_layer_impl.h" 24 #include "content/renderer/media/android/renderer_demuxer_android.h" 25 #include "content/renderer/media/android/renderer_media_player_manager.h" 26 #include "content/renderer/media/crypto/key_systems.h" 27 #include "content/renderer/media/crypto/renderer_cdm_manager.h" 28 #include "content/renderer/media/webcontentdecryptionmodule_impl.h" 29 #include "content/renderer/media/webmediaplayer_delegate.h" 30 #include "content/renderer/media/webmediaplayer_util.h" 31 #include "content/renderer/render_frame_impl.h" 32 #include "content/renderer/render_thread_impl.h" 33 #include "gpu/GLES2/gl2extchromium.h" 34 #include "gpu/command_buffer/client/gles2_interface.h" 35 #include "gpu/command_buffer/common/mailbox_holder.h" 36 #include "media/base/android/media_player_android.h" 37 #include "media/base/bind_to_current_loop.h" 38 // TODO(xhwang): Remove when we remove prefixed EME implementation. 39 #include "media/base/media_keys.h" 40 #include "media/base/media_switches.h" 41 #include "media/base/video_frame.h" 42 #include "net/base/mime_util.h" 43 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h" 44 #include "third_party/WebKit/public/platform/WebString.h" 45 #include "third_party/WebKit/public/platform/WebURL.h" 46 #include "third_party/WebKit/public/web/WebDocument.h" 47 #include "third_party/WebKit/public/web/WebFrame.h" 48 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" 49 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 50 #include "third_party/WebKit/public/web/WebView.h" 51 #include "third_party/skia/include/core/SkBitmap.h" 52 #include "third_party/skia/include/core/SkCanvas.h" 53 #include "third_party/skia/include/core/SkPaint.h" 54 #include "third_party/skia/include/core/SkTypeface.h" 55 #include "ui/gfx/image/image.h" 56 57 static const uint32 kGLTextureExternalOES = 0x8D65; 58 static const int kSDKVersionToSupportSecurityOriginCheck = 20; 59 60 using blink::WebMediaPlayer; 61 using blink::WebSize; 62 using blink::WebString; 63 using blink::WebTimeRanges; 64 using blink::WebURL; 65 using gpu::gles2::GLES2Interface; 66 using media::MediaPlayerAndroid; 67 using media::VideoFrame; 68 69 namespace { 70 // Prefix for histograms related to Encrypted Media Extensions. 71 const char* kMediaEme = "Media.EME."; 72 73 // File-static function is to allow it to run even after WMPA is deleted. 74 void OnReleaseTexture( 75 const scoped_refptr<content::StreamTextureFactory>& factories, 76 uint32 texture_id, 77 const std::vector<uint32>& release_sync_points) { 78 GLES2Interface* gl = factories->ContextGL(); 79 for (size_t i = 0; i < release_sync_points.size(); i++) 80 gl->WaitSyncPointCHROMIUM(release_sync_points[i]); 81 gl->DeleteTextures(1, &texture_id); 82 } 83 } // namespace 84 85 namespace content { 86 87 WebMediaPlayerAndroid::WebMediaPlayerAndroid( 88 blink::WebFrame* frame, 89 blink::WebMediaPlayerClient* client, 90 base::WeakPtr<WebMediaPlayerDelegate> delegate, 91 RendererMediaPlayerManager* player_manager, 92 RendererCdmManager* cdm_manager, 93 scoped_refptr<StreamTextureFactory> factory, 94 const scoped_refptr<base::MessageLoopProxy>& media_loop, 95 media::MediaLog* media_log) 96 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)), 97 frame_(frame), 98 client_(client), 99 delegate_(delegate), 100 buffered_(static_cast<size_t>(1)), 101 media_loop_(media_loop), 102 ignore_metadata_duration_change_(false), 103 pending_seek_(false), 104 seeking_(false), 105 did_loading_progress_(false), 106 player_manager_(player_manager), 107 cdm_manager_(cdm_manager), 108 network_state_(WebMediaPlayer::NetworkStateEmpty), 109 ready_state_(WebMediaPlayer::ReadyStateHaveNothing), 110 texture_id_(0), 111 stream_id_(0), 112 is_playing_(false), 113 needs_establish_peer_(true), 114 has_size_info_(false), 115 compositor_loop_( 116 RenderThreadImpl::current()->compositor_message_loop_proxy()), 117 stream_texture_factory_(factory), 118 needs_external_surface_(false), 119 video_frame_provider_client_(NULL), 120 pending_playback_(false), 121 player_type_(MEDIA_PLAYER_TYPE_URL), 122 current_time_(0), 123 is_remote_(false), 124 media_log_(media_log), 125 web_cdm_(NULL), 126 allow_stored_credentials_(false), 127 weak_factory_(this) { 128 DCHECK(player_manager_); 129 DCHECK(cdm_manager_); 130 131 DCHECK(main_thread_checker_.CalledOnValidThread()); 132 stream_texture_factory_->AddObserver(this); 133 134 player_id_ = player_manager_->RegisterMediaPlayer(this); 135 136 #if defined(VIDEO_HOLE) 137 force_use_overlay_embedded_video_ = CommandLine::ForCurrentProcess()-> 138 HasSwitch(switches::kForceUseOverlayEmbeddedVideo); 139 if (force_use_overlay_embedded_video_ || 140 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo()) { 141 // Defer stream texture creation until we are sure it's necessary. 142 needs_establish_peer_ = false; 143 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1)); 144 } 145 #endif // defined(VIDEO_HOLE) 146 TryCreateStreamTextureProxyIfNeeded(); 147 } 148 149 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { 150 SetVideoFrameProviderClient(NULL); 151 client_->setWebLayer(NULL); 152 153 if (player_manager_) { 154 player_manager_->DestroyPlayer(player_id_); 155 player_manager_->UnregisterMediaPlayer(player_id_); 156 } 157 158 if (stream_id_) { 159 GLES2Interface* gl = stream_texture_factory_->ContextGL(); 160 gl->DeleteTextures(1, &texture_id_); 161 texture_id_ = 0; 162 texture_mailbox_ = gpu::Mailbox(); 163 stream_id_ = 0; 164 } 165 166 { 167 base::AutoLock auto_lock(current_frame_lock_); 168 current_frame_ = NULL; 169 } 170 171 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_) 172 delegate_->PlayerGone(this); 173 174 stream_texture_factory_->RemoveObserver(this); 175 } 176 177 void WebMediaPlayerAndroid::load(LoadType load_type, 178 const blink::WebURL& url, 179 CORSMode cors_mode) { 180 ReportMediaSchemeUma(GURL(url)); 181 182 switch (load_type) { 183 case LoadTypeURL: 184 player_type_ = MEDIA_PLAYER_TYPE_URL; 185 break; 186 187 case LoadTypeMediaSource: 188 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE; 189 break; 190 191 case LoadTypeMediaStream: 192 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on " 193 "this platform"; 194 return; 195 } 196 197 url_ = url; 198 int demuxer_client_id = 0; 199 if (player_type_ != MEDIA_PLAYER_TYPE_URL) { 200 RendererDemuxerAndroid* demuxer = 201 RenderThreadImpl::current()->renderer_demuxer(); 202 demuxer_client_id = demuxer->GetNextDemuxerClientID(); 203 204 media_source_delegate_.reset(new MediaSourceDelegate( 205 demuxer, demuxer_client_id, media_loop_, media_log_)); 206 207 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) { 208 media::SetDecryptorReadyCB set_decryptor_ready_cb = 209 media::BindToCurrentLoop( 210 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB, 211 weak_factory_.GetWeakPtr())); 212 213 media_source_delegate_->InitializeMediaSource( 214 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened, 215 weak_factory_.GetWeakPtr()), 216 base::Bind(&WebMediaPlayerAndroid::OnNeedKey, 217 weak_factory_.GetWeakPtr()), 218 set_decryptor_ready_cb, 219 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, 220 weak_factory_.GetWeakPtr()), 221 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged, 222 weak_factory_.GetWeakPtr())); 223 InitializePlayer(url_, frame_->document().firstPartyForCookies(), 224 true, demuxer_client_id); 225 } 226 } else { 227 info_loader_.reset( 228 new MediaInfoLoader( 229 url, 230 cors_mode, 231 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo, 232 weak_factory_.GetWeakPtr()))); 233 info_loader_->Start(frame_); 234 } 235 236 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading); 237 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); 238 } 239 240 void WebMediaPlayerAndroid::DidLoadMediaInfo( 241 MediaInfoLoader::Status status, 242 const GURL& redirected_url, 243 const GURL& first_party_for_cookies, 244 bool allow_stored_credentials) { 245 DCHECK(!media_source_delegate_); 246 if (status == MediaInfoLoader::kFailed) { 247 info_loader_.reset(); 248 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError); 249 return; 250 } 251 252 InitializePlayer( 253 redirected_url, first_party_for_cookies, allow_stored_credentials, 0); 254 255 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle); 256 } 257 258 void WebMediaPlayerAndroid::play() { 259 #if defined(VIDEO_HOLE) 260 if (hasVideo() && needs_external_surface_ && 261 !player_manager_->IsInFullscreen(frame_)) { 262 DCHECK(!needs_establish_peer_); 263 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_); 264 } 265 #endif // defined(VIDEO_HOLE) 266 267 TryCreateStreamTextureProxyIfNeeded(); 268 // There is no need to establish the surface texture peer for fullscreen 269 // video. 270 if (hasVideo() && needs_establish_peer_ && 271 !player_manager_->IsInFullscreen(frame_)) { 272 EstablishSurfaceTexturePeer(); 273 } 274 275 if (paused()) 276 player_manager_->Start(player_id_); 277 UpdatePlayingState(true); 278 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading); 279 } 280 281 void WebMediaPlayerAndroid::pause() { 282 Pause(true); 283 } 284 285 void WebMediaPlayerAndroid::seek(double seconds) { 286 DCHECK(main_thread_checker_.CalledOnValidThread()); 287 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")"; 288 289 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds); 290 291 if (seeking_) { 292 if (new_seek_time == seek_time_) { 293 if (media_source_delegate_) { 294 if (!pending_seek_) { 295 // If using media source demuxer, only suppress redundant seeks if 296 // there is no pending seek. This enforces that any pending seek that 297 // results in a demuxer seek is preceded by matching 298 // CancelPendingSeek() and StartWaitingForSeek() calls. 299 return; 300 } 301 } else { 302 // Suppress all redundant seeks if unrestricted by media source 303 // demuxer API. 304 pending_seek_ = false; 305 return; 306 } 307 } 308 309 pending_seek_ = true; 310 pending_seek_time_ = new_seek_time; 311 312 if (media_source_delegate_) 313 media_source_delegate_->CancelPendingSeek(pending_seek_time_); 314 315 // Later, OnSeekComplete will trigger the pending seek. 316 return; 317 } 318 319 seeking_ = true; 320 seek_time_ = new_seek_time; 321 322 if (media_source_delegate_) 323 media_source_delegate_->StartWaitingForSeek(seek_time_); 324 325 // Kick off the asynchronous seek! 326 player_manager_->Seek(player_id_, seek_time_); 327 } 328 329 bool WebMediaPlayerAndroid::supportsSave() const { 330 return false; 331 } 332 333 void WebMediaPlayerAndroid::setRate(double rate) { 334 NOTIMPLEMENTED(); 335 } 336 337 void WebMediaPlayerAndroid::setVolume(double volume) { 338 player_manager_->SetVolume(player_id_, volume); 339 } 340 341 bool WebMediaPlayerAndroid::hasVideo() const { 342 // If we have obtained video size information before, use it. 343 if (has_size_info_) 344 return !natural_size_.isEmpty(); 345 346 // TODO(qinmin): need a better method to determine whether the current media 347 // content contains video. Android does not provide any function to do 348 // this. 349 // We don't know whether the current media content has video unless 350 // the player is prepared. If the player is not prepared, we fall back 351 // to the mime-type. There may be no mime-type on a redirect URL. 352 // In that case, we conservatively assume it contains video so that 353 // enterfullscreen call will not fail. 354 if (!url_.has_path()) 355 return false; 356 std::string mime; 357 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) 358 return true; 359 return mime.find("audio/") == std::string::npos; 360 } 361 362 bool WebMediaPlayerAndroid::hasAudio() const { 363 if (!url_.has_path()) 364 return false; 365 std::string mime; 366 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) 367 return true; 368 369 if (mime.find("audio/") != std::string::npos || 370 mime.find("video/") != std::string::npos || 371 mime.find("application/ogg") != std::string::npos) { 372 return true; 373 } 374 return false; 375 } 376 377 bool WebMediaPlayerAndroid::paused() const { 378 return !is_playing_; 379 } 380 381 bool WebMediaPlayerAndroid::seeking() const { 382 return seeking_; 383 } 384 385 double WebMediaPlayerAndroid::duration() const { 386 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING 387 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) 388 return std::numeric_limits<double>::quiet_NaN(); 389 390 if (duration_ == media::kInfiniteDuration()) 391 return std::numeric_limits<double>::infinity(); 392 393 return duration_.InSecondsF(); 394 } 395 396 double WebMediaPlayerAndroid::timelineOffset() const { 397 base::Time timeline_offset; 398 if (media_source_delegate_) 399 timeline_offset = media_source_delegate_->GetTimelineOffset(); 400 401 if (timeline_offset.is_null()) 402 return std::numeric_limits<double>::quiet_NaN(); 403 404 return timeline_offset.ToJsTime(); 405 } 406 407 double WebMediaPlayerAndroid::currentTime() const { 408 // If the player is processing a seek, return the seek time. 409 // Blink may still query us if updatePlaybackState() occurs while seeking. 410 if (seeking()) { 411 return pending_seek_ ? 412 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF(); 413 } 414 415 return current_time_; 416 } 417 418 WebSize WebMediaPlayerAndroid::naturalSize() const { 419 return natural_size_; 420 } 421 422 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const { 423 return network_state_; 424 } 425 426 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { 427 return ready_state_; 428 } 429 430 WebTimeRanges WebMediaPlayerAndroid::buffered() const { 431 if (media_source_delegate_) 432 return media_source_delegate_->Buffered(); 433 return buffered_; 434 } 435 436 double WebMediaPlayerAndroid::maxTimeSeekable() const { 437 // If we haven't even gotten to ReadyStateHaveMetadata yet then just 438 // return 0 so that the seekable range is empty. 439 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata) 440 return 0.0; 441 442 return duration(); 443 } 444 445 bool WebMediaPlayerAndroid::didLoadingProgress() { 446 bool ret = did_loading_progress_; 447 did_loading_progress_ = false; 448 return ret; 449 } 450 451 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas, 452 const blink::WebRect& rect, 453 unsigned char alpha) { 454 NOTIMPLEMENTED(); 455 } 456 457 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( 458 blink::WebGraphicsContext3D* web_graphics_context, 459 unsigned int texture, 460 unsigned int level, 461 unsigned int internal_format, 462 unsigned int type, 463 bool premultiply_alpha, 464 bool flip_y) { 465 // Don't allow clients to copy an encrypted video frame. 466 if (needs_external_surface_) 467 return false; 468 469 scoped_refptr<VideoFrame> video_frame; 470 { 471 base::AutoLock auto_lock(current_frame_lock_); 472 video_frame = current_frame_; 473 } 474 475 if (!video_frame || 476 video_frame->format() != media::VideoFrame::NATIVE_TEXTURE) 477 return false; 478 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder(); 479 DCHECK((!is_remote_ && 480 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) || 481 (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D)); 482 483 // For hidden video element (with style "display:none"), ensure the texture 484 // size is set. 485 if (!is_remote_ && 486 (cached_stream_texture_size_.width != natural_size_.width || 487 cached_stream_texture_size_.height != natural_size_.height)) { 488 stream_texture_factory_->SetStreamTextureSize( 489 stream_id_, gfx::Size(natural_size_.width, natural_size_.height)); 490 cached_stream_texture_size_ = natural_size_; 491 } 492 493 uint32 source_texture = web_graphics_context->createTexture(); 494 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point); 495 496 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise 497 // an invalid texture target may be used for copy texture. 498 web_graphics_context->bindTexture(mailbox_holder->texture_target, 499 source_texture); 500 web_graphics_context->consumeTextureCHROMIUM(mailbox_holder->texture_target, 501 mailbox_holder->mailbox.name); 502 503 // The video is stored in an unmultiplied format, so premultiply if 504 // necessary. 505 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, 506 premultiply_alpha); 507 508 // Application itself needs to take care of setting the right flip_y 509 // value down to get the expected result. 510 // flip_y==true means to reverse the video orientation while 511 // flip_y==false means to keep the intrinsic orientation. 512 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); 513 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, 514 texture, level, internal_format, 515 type); 516 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); 517 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, 518 false); 519 520 if (mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) 521 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0); 522 else 523 web_graphics_context->bindTexture(GL_TEXTURE_2D, texture); 524 web_graphics_context->deleteTexture(source_texture); 525 web_graphics_context->flush(); 526 video_frame->AppendReleaseSyncPoint(web_graphics_context->insertSyncPoint()); 527 return true; 528 } 529 530 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const { 531 if (player_type_ != MEDIA_PLAYER_TYPE_URL) 532 return true; 533 534 if (!info_loader_ || !info_loader_->HasSingleOrigin()) 535 return false; 536 537 // TODO(qinmin): The url might be redirected when android media player 538 // requests the stream. As a result, we cannot guarantee there is only 539 // a single origin. Only if the HTTP request was made without credentials, 540 // we will honor the return value from HasSingleSecurityOriginInternal() 541 // in pre-L android versions. 542 // Check http://crbug.com/334204. 543 if (!allow_stored_credentials_) 544 return true; 545 546 return base::android::BuildInfo::GetInstance()->sdk_int() >= 547 kSDKVersionToSupportSecurityOriginCheck; 548 } 549 550 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { 551 if (info_loader_) 552 return info_loader_->DidPassCORSAccessCheck(); 553 return false; 554 } 555 556 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { 557 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); 558 } 559 560 unsigned WebMediaPlayerAndroid::decodedFrameCount() const { 561 if (media_source_delegate_) 562 return media_source_delegate_->DecodedFrameCount(); 563 NOTIMPLEMENTED(); 564 return 0; 565 } 566 567 unsigned WebMediaPlayerAndroid::droppedFrameCount() const { 568 if (media_source_delegate_) 569 return media_source_delegate_->DroppedFrameCount(); 570 NOTIMPLEMENTED(); 571 return 0; 572 } 573 574 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { 575 if (media_source_delegate_) 576 return media_source_delegate_->AudioDecodedByteCount(); 577 NOTIMPLEMENTED(); 578 return 0; 579 } 580 581 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { 582 if (media_source_delegate_) 583 return media_source_delegate_->VideoDecodedByteCount(); 584 NOTIMPLEMENTED(); 585 return 0; 586 } 587 588 void WebMediaPlayerAndroid::OnMediaMetadataChanged( 589 const base::TimeDelta& duration, int width, int height, bool success) { 590 bool need_to_signal_duration_changed = false; 591 592 if (url_.SchemeIs("file")) 593 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded); 594 595 // Update duration, if necessary, prior to ready state updates that may 596 // cause duration() query. 597 if (!ignore_metadata_duration_change_ && duration_ != duration) { 598 duration_ = duration; 599 600 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA 601 // already triggers a durationchanged event. If this is a different 602 // transition, remember to signal durationchanged. 603 // Do not ever signal durationchanged on metadata change in MSE case 604 // because OnDurationChanged() handles this. 605 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing && 606 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) { 607 need_to_signal_duration_changed = true; 608 } 609 } 610 611 if (ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { 612 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); 613 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); 614 } 615 616 // TODO(wolenetz): Should we just abort early and set network state to an 617 // error if success == false? See http://crbug.com/248399 618 if (success) 619 OnVideoSizeChanged(width, height); 620 621 if (need_to_signal_duration_changed) 622 client_->durationChanged(); 623 } 624 625 void WebMediaPlayerAndroid::OnPlaybackComplete() { 626 // When playback is about to finish, android media player often stops 627 // at a time which is smaller than the duration. This makes webkit never 628 // know that the playback has finished. To solve this, we set the 629 // current time to media duration when OnPlaybackComplete() get called. 630 OnTimeUpdate(duration_); 631 client_->timeChanged(); 632 633 // if the loop attribute is set, timeChanged() will update the current time 634 // to 0. It will perform a seek to 0. As the requests to the renderer 635 // process are sequential, the OnSeekComplete() will only occur 636 // once OnPlaybackComplete() is done. As the playback can only be executed 637 // upon completion of OnSeekComplete(), the request needs to be saved. 638 is_playing_ = false; 639 if (seeking_ && seek_time_ == base::TimeDelta()) 640 pending_playback_ = true; 641 } 642 643 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) { 644 buffered_[0].end = duration() * percentage / 100; 645 did_loading_progress_ = true; 646 } 647 648 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) { 649 DCHECK(main_thread_checker_.CalledOnValidThread()); 650 client_->requestSeek(time_to_seek.InSecondsF()); 651 } 652 653 void WebMediaPlayerAndroid::OnSeekComplete( 654 const base::TimeDelta& current_time) { 655 DCHECK(main_thread_checker_.CalledOnValidThread()); 656 seeking_ = false; 657 if (pending_seek_) { 658 pending_seek_ = false; 659 seek(pending_seek_time_.InSecondsF()); 660 return; 661 } 662 663 OnTimeUpdate(current_time); 664 665 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); 666 667 client_->timeChanged(); 668 669 if (pending_playback_) { 670 play(); 671 pending_playback_ = false; 672 } 673 } 674 675 void WebMediaPlayerAndroid::OnMediaError(int error_type) { 676 switch (error_type) { 677 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT: 678 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); 679 break; 680 case MediaPlayerAndroid::MEDIA_ERROR_DECODE: 681 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError); 682 break; 683 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: 684 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); 685 break; 686 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE: 687 break; 688 } 689 client_->repaint(); 690 } 691 692 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { 693 has_size_info_ = true; 694 if (natural_size_.width == width && natural_size_.height == height) 695 return; 696 697 #if defined(VIDEO_HOLE) 698 // Use H/W surface for encrypted video. 699 // TODO(qinmin): Change this so that only EME needs the H/W surface 700 if (force_use_overlay_embedded_video_ || 701 (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted() && 702 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo())) { 703 needs_external_surface_ = true; 704 if (!paused() && !player_manager_->IsInFullscreen(frame_)) 705 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_); 706 } else if (stream_texture_proxy_ && !stream_id_) { 707 // Do deferred stream texture creation finally. 708 DoCreateStreamTexture(); 709 SetNeedsEstablishPeer(true); 710 } 711 #endif // defined(VIDEO_HOLE) 712 // When play() gets called, |natural_size_| may still be empty and 713 // EstablishSurfaceTexturePeer() will not get called. As a result, the video 714 // may play without a surface texture. When we finally get the valid video 715 // size here, we should call EstablishSurfaceTexturePeer() if it has not been 716 // previously called. 717 if (!paused() && needs_establish_peer_) 718 EstablishSurfaceTexturePeer(); 719 720 natural_size_.width = width; 721 natural_size_.height = height; 722 ReallocateVideoFrame(); 723 724 // Lazily allocate compositing layer. 725 if (!video_weblayer_) { 726 video_weblayer_.reset(new WebLayerImpl(cc::VideoLayer::Create(this))); 727 client_->setWebLayer(video_weblayer_.get()); 728 } 729 730 // TODO(qinmin): This is a hack. We need the media element to stop showing the 731 // poster image by forcing it to call setDisplayMode(video). Should move the 732 // logic into HTMLMediaElement.cpp. 733 client_->timeChanged(); 734 } 735 736 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) { 737 DCHECK(main_thread_checker_.CalledOnValidThread()); 738 current_time_ = current_time.InSecondsF(); 739 } 740 741 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice( 742 const std::string& remote_playback_message) { 743 DCHECK(main_thread_checker_.CalledOnValidThread()); 744 DCHECK(!media_source_delegate_); 745 DrawRemotePlaybackText(remote_playback_message); 746 is_remote_ = true; 747 SetNeedsEstablishPeer(false); 748 } 749 750 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() { 751 DCHECK(main_thread_checker_.CalledOnValidThread()); 752 DCHECK(!media_source_delegate_); 753 SetNeedsEstablishPeer(true); 754 if (!paused()) 755 EstablishSurfaceTexturePeer(); 756 is_remote_ = false; 757 ReallocateVideoFrame(); 758 } 759 760 void WebMediaPlayerAndroid::OnDidEnterFullscreen() { 761 if (!player_manager_->IsInFullscreen(frame_)) 762 player_manager_->DidEnterFullscreen(frame_); 763 } 764 765 void WebMediaPlayerAndroid::OnDidExitFullscreen() { 766 // |needs_external_surface_| is always false on non-TV devices. 767 if (!needs_external_surface_) 768 SetNeedsEstablishPeer(true); 769 // We had the fullscreen surface connected to Android MediaPlayer, 770 // so reconnect our surface texture for embedded playback. 771 if (!paused() && needs_establish_peer_) 772 EstablishSurfaceTexturePeer(); 773 774 #if defined(VIDEO_HOLE) 775 if (!paused() && needs_external_surface_) 776 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_); 777 #endif // defined(VIDEO_HOLE) 778 779 player_manager_->DidExitFullscreen(); 780 client_->repaint(); 781 } 782 783 void WebMediaPlayerAndroid::OnMediaPlayerPlay() { 784 UpdatePlayingState(true); 785 client_->playbackStateChanged(); 786 } 787 788 void WebMediaPlayerAndroid::OnMediaPlayerPause() { 789 UpdatePlayingState(false); 790 client_->playbackStateChanged(); 791 } 792 793 void WebMediaPlayerAndroid::OnRequestFullscreen() { 794 client_->requestFullscreen(); 795 } 796 797 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) { 798 DCHECK(main_thread_checker_.CalledOnValidThread()); 799 // Only MSE |player_type_| registers this callback. 800 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE); 801 802 // Cache the new duration value and trust it over any subsequent duration 803 // values received in OnMediaMetadataChanged(). 804 duration_ = duration; 805 ignore_metadata_duration_change_ = true; 806 807 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING. 808 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing) 809 client_->durationChanged(); 810 } 811 812 void WebMediaPlayerAndroid::UpdateNetworkState( 813 WebMediaPlayer::NetworkState state) { 814 DCHECK(main_thread_checker_.CalledOnValidThread()); 815 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing && 816 (state == WebMediaPlayer::NetworkStateNetworkError || 817 state == WebMediaPlayer::NetworkStateDecodeError)) { 818 // Any error that occurs before reaching ReadyStateHaveMetadata should 819 // be considered a format error. 820 network_state_ = WebMediaPlayer::NetworkStateFormatError; 821 } else { 822 network_state_ = state; 823 } 824 client_->networkStateChanged(); 825 } 826 827 void WebMediaPlayerAndroid::UpdateReadyState( 828 WebMediaPlayer::ReadyState state) { 829 ready_state_ = state; 830 client_->readyStateChanged(); 831 } 832 833 void WebMediaPlayerAndroid::OnPlayerReleased() { 834 // |needs_external_surface_| is always false on non-TV devices. 835 if (!needs_external_surface_) 836 needs_establish_peer_ = true; 837 838 if (is_playing_) 839 OnMediaPlayerPause(); 840 841 #if defined(VIDEO_HOLE) 842 last_computed_rect_ = gfx::RectF(); 843 #endif // defined(VIDEO_HOLE) 844 } 845 846 void WebMediaPlayerAndroid::ReleaseMediaResources() { 847 switch (network_state_) { 848 // Pause the media player and inform WebKit if the player is in a good 849 // shape. 850 case WebMediaPlayer::NetworkStateIdle: 851 case WebMediaPlayer::NetworkStateLoading: 852 case WebMediaPlayer::NetworkStateLoaded: 853 Pause(false); 854 client_->playbackStateChanged(); 855 break; 856 // If a WebMediaPlayer instance has entered into one of these states, 857 // the internal network state in HTMLMediaElement could be set to empty. 858 // And calling playbackStateChanged() could get this object deleted. 859 case WebMediaPlayer::NetworkStateEmpty: 860 case WebMediaPlayer::NetworkStateFormatError: 861 case WebMediaPlayer::NetworkStateNetworkError: 862 case WebMediaPlayer::NetworkStateDecodeError: 863 break; 864 } 865 player_manager_->ReleaseResources(player_id_); 866 OnPlayerReleased(); 867 } 868 869 void WebMediaPlayerAndroid::OnDestruct() { 870 NOTREACHED() << "WebMediaPlayer should be destroyed before any " 871 "RenderFrameObserver::OnDestruct() gets called when " 872 "the RenderFrame goes away."; 873 } 874 875 void WebMediaPlayerAndroid::InitializePlayer( 876 const GURL& url, 877 const GURL& first_party_for_cookies, 878 bool allow_stored_credentials, 879 int demuxer_client_id) { 880 allow_stored_credentials_ = allow_stored_credentials; 881 player_manager_->Initialize( 882 player_type_, player_id_, url, first_party_for_cookies, demuxer_client_id, 883 frame_->document().url(), allow_stored_credentials); 884 if (player_manager_->ShouldEnterFullscreen(frame_)) 885 player_manager_->EnterFullscreen(player_id_, frame_); 886 } 887 888 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) { 889 player_manager_->Pause(player_id_, is_media_related_action); 890 UpdatePlayingState(false); 891 } 892 893 void WebMediaPlayerAndroid::DrawRemotePlaybackText( 894 const std::string& remote_playback_message) { 895 896 DCHECK(main_thread_checker_.CalledOnValidThread()); 897 if (!video_weblayer_) 898 return; 899 900 // TODO(johnme): Should redraw this frame if the layer bounds change; but 901 // there seems no easy way to listen for the layer resizing (as opposed to 902 // OnVideoSizeChanged, which is when the frame sizes of the video file 903 // change). Perhaps have to poll (on main thread of course)? 904 gfx::Size video_size_css_px = video_weblayer_->bounds(); 905 float device_scale_factor = frame_->view()->deviceScaleFactor(); 906 // canvas_size will be the size in device pixels when pageScaleFactor == 1 907 gfx::Size canvas_size( 908 static_cast<int>(video_size_css_px.width() * device_scale_factor), 909 static_cast<int>(video_size_css_px.height() * device_scale_factor)); 910 911 SkBitmap bitmap; 912 bitmap.setConfig( 913 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height()); 914 bitmap.allocPixels(); 915 916 // Create the canvas and draw the "Casting to <Chromecast>" text on it. 917 SkCanvas canvas(bitmap); 918 canvas.drawColor(SK_ColorBLACK); 919 920 const SkScalar kTextSize(40); 921 const SkScalar kMinPadding(40); 922 923 SkPaint paint; 924 paint.setAntiAlias(true); 925 paint.setFilterLevel(SkPaint::kHigh_FilterLevel); 926 paint.setColor(SK_ColorWHITE); 927 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold)); 928 paint.setTextSize(kTextSize); 929 930 // Calculate the vertical margin from the top 931 SkPaint::FontMetrics font_metrics; 932 paint.getFontMetrics(&font_metrics); 933 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent; 934 935 // Measure the width of the entire text to display 936 size_t display_text_width = paint.measureText( 937 remote_playback_message.c_str(), remote_playback_message.size()); 938 std::string display_text(remote_playback_message); 939 940 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) { 941 // The text is too long to fit in one line, truncate it and append ellipsis 942 // to the end. 943 944 // First, figure out how much of the canvas the '...' will take up. 945 const std::string kTruncationEllipsis("\xE2\x80\xA6"); 946 SkScalar sk_ellipse_width = paint.measureText( 947 kTruncationEllipsis.c_str(), kTruncationEllipsis.size()); 948 949 // Then calculate how much of the text can be drawn with the '...' appended 950 // to the end of the string. 951 SkScalar sk_max_original_text_width( 952 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width); 953 size_t sk_max_original_text_length = paint.breakText( 954 remote_playback_message.c_str(), 955 remote_playback_message.size(), 956 sk_max_original_text_width); 957 958 // Remove the part of the string that doesn't fit and append '...'. 959 display_text.erase(sk_max_original_text_length, 960 remote_playback_message.size() - sk_max_original_text_length); 961 display_text.append(kTruncationEllipsis); 962 display_text_width = paint.measureText( 963 display_text.c_str(), display_text.size()); 964 } 965 966 // Center the text horizontally. 967 SkScalar sk_horizontal_margin = 968 (canvas_size.width() - display_text_width) / 2.0; 969 canvas.drawText(display_text.c_str(), 970 display_text.size(), 971 sk_horizontal_margin, 972 sk_vertical_margin, 973 paint); 974 975 GLES2Interface* gl = stream_texture_factory_->ContextGL(); 976 GLuint remote_playback_texture_id = 0; 977 gl->GenTextures(1, &remote_playback_texture_id); 978 GLuint texture_target = GL_TEXTURE_2D; 979 gl->BindTexture(texture_target, remote_playback_texture_id); 980 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 981 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 982 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 983 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 984 985 { 986 SkAutoLockPixels lock(bitmap); 987 gl->TexImage2D(texture_target, 988 0 /* level */, 989 GL_RGBA /* internalformat */, 990 bitmap.width(), 991 bitmap.height(), 992 0 /* border */, 993 GL_RGBA /* format */, 994 GL_UNSIGNED_BYTE /* type */, 995 bitmap.getPixels()); 996 } 997 998 gpu::Mailbox texture_mailbox; 999 gl->GenMailboxCHROMIUM(texture_mailbox.name); 1000 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name); 1001 gl->Flush(); 1002 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM(); 1003 1004 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture( 1005 make_scoped_ptr(new gpu::MailboxHolder( 1006 texture_mailbox, texture_target, texture_mailbox_sync_point)), 1007 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture, 1008 stream_texture_factory_, 1009 remote_playback_texture_id)), 1010 canvas_size /* coded_size */, 1011 gfx::Rect(canvas_size) /* visible_rect */, 1012 canvas_size /* natural_size */, 1013 base::TimeDelta() /* timestamp */, 1014 VideoFrame::ReadPixelsCB()); 1015 SetCurrentFrameInternal(new_frame); 1016 } 1017 1018 void WebMediaPlayerAndroid::ReallocateVideoFrame() { 1019 if (needs_external_surface_) { 1020 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE. 1021 #if defined(VIDEO_HOLE) 1022 if (!natural_size_.isEmpty()) { 1023 scoped_refptr<VideoFrame> new_frame = 1024 VideoFrame::CreateHoleFrame(natural_size_); 1025 SetCurrentFrameInternal(new_frame); 1026 // Force the client to grab the hole frame. 1027 client_->repaint(); 1028 } 1029 #else 1030 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag"; 1031 #endif // defined(VIDEO_HOLE) 1032 } else if (!is_remote_ && texture_id_) { 1033 GLES2Interface* gl = stream_texture_factory_->ContextGL(); 1034 GLuint texture_id_ref = 0; 1035 gl->GenTextures(1, &texture_id_ref); 1036 GLuint texture_target = kGLTextureExternalOES; 1037 gl->BindTexture(texture_target, texture_id_ref); 1038 gl->ConsumeTextureCHROMIUM(texture_target, texture_mailbox_.name); 1039 gl->Flush(); 1040 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM(); 1041 1042 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture( 1043 make_scoped_ptr(new gpu::MailboxHolder( 1044 texture_mailbox_, texture_target, texture_mailbox_sync_point)), 1045 media::BindToCurrentLoop(base::Bind( 1046 &OnReleaseTexture, stream_texture_factory_, texture_id_ref)), 1047 natural_size_, 1048 gfx::Rect(natural_size_), 1049 natural_size_, 1050 base::TimeDelta(), 1051 VideoFrame::ReadPixelsCB()); 1052 SetCurrentFrameInternal(new_frame); 1053 } 1054 } 1055 1056 void WebMediaPlayerAndroid::SetVideoFrameProviderClient( 1057 cc::VideoFrameProvider::Client* client) { 1058 // This is called from both the main renderer thread and the compositor 1059 // thread (when the main thread is blocked). 1060 1061 // Set the callback target when a frame is produced. Need to do this before 1062 // StopUsingProvider to ensure we really stop using the client. 1063 if (stream_texture_proxy_) 1064 stream_texture_proxy_->BindToLoop(stream_id_, client, compositor_loop_); 1065 1066 if (video_frame_provider_client_ && video_frame_provider_client_ != client) 1067 video_frame_provider_client_->StopUsingProvider(); 1068 video_frame_provider_client_ = client; 1069 } 1070 1071 void WebMediaPlayerAndroid::SetCurrentFrameInternal( 1072 scoped_refptr<media::VideoFrame>& video_frame) { 1073 base::AutoLock auto_lock(current_frame_lock_); 1074 current_frame_ = video_frame; 1075 } 1076 1077 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() { 1078 scoped_refptr<VideoFrame> video_frame; 1079 { 1080 base::AutoLock auto_lock(current_frame_lock_); 1081 video_frame = current_frame_; 1082 } 1083 1084 if (stream_texture_proxy_ && 1085 stream_id_ && !needs_external_surface_ && !is_remote_) { 1086 gfx::Size natural_size = video_frame->natural_size(); 1087 // TODO(sievers): These variables are accessed on the wrong thread here. 1088 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size); 1089 cached_stream_texture_size_ = natural_size; 1090 } 1091 1092 return video_frame; 1093 } 1094 1095 void WebMediaPlayerAndroid::PutCurrentFrame( 1096 const scoped_refptr<media::VideoFrame>& frame) { 1097 } 1098 1099 void WebMediaPlayerAndroid::ResetStreamTextureProxy() { 1100 DCHECK(main_thread_checker_.CalledOnValidThread()); 1101 1102 if (stream_id_) { 1103 GLES2Interface* gl = stream_texture_factory_->ContextGL(); 1104 gl->DeleteTextures(1, &texture_id_); 1105 texture_id_ = 0; 1106 texture_mailbox_ = gpu::Mailbox(); 1107 stream_id_ = 0; 1108 } 1109 stream_texture_proxy_.reset(); 1110 needs_establish_peer_ = !needs_external_surface_ && !is_remote_ && 1111 !player_manager_->IsInFullscreen(frame_) && 1112 hasVideo(); 1113 1114 TryCreateStreamTextureProxyIfNeeded(); 1115 if (needs_establish_peer_ && is_playing_) 1116 EstablishSurfaceTexturePeer(); 1117 } 1118 1119 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() { 1120 // Already created. 1121 if (stream_texture_proxy_) 1122 return; 1123 1124 // No factory to create proxy. 1125 if (!stream_texture_factory_) 1126 return; 1127 1128 // Not needed for hole punching. 1129 if (!needs_establish_peer_) 1130 return; 1131 1132 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy()); 1133 if (stream_texture_proxy_) { 1134 DoCreateStreamTexture(); 1135 ReallocateVideoFrame(); 1136 if (video_frame_provider_client_) { 1137 stream_texture_proxy_->BindToLoop( 1138 stream_id_, video_frame_provider_client_, compositor_loop_); 1139 } 1140 } 1141 } 1142 1143 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() { 1144 if (!stream_texture_proxy_) 1145 return; 1146 1147 if (stream_texture_factory_.get() && stream_id_) 1148 stream_texture_factory_->EstablishPeer(stream_id_, player_id_); 1149 needs_establish_peer_ = false; 1150 } 1151 1152 void WebMediaPlayerAndroid::DoCreateStreamTexture() { 1153 DCHECK(!stream_id_); 1154 DCHECK(!texture_id_); 1155 stream_id_ = stream_texture_factory_->CreateStreamTexture( 1156 kGLTextureExternalOES, &texture_id_, &texture_mailbox_); 1157 } 1158 1159 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) { 1160 needs_establish_peer_ = needs_establish_peer; 1161 } 1162 1163 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) { 1164 player_manager_->SetPoster(player_id_, poster); 1165 } 1166 1167 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) { 1168 is_playing_ = is_playing; 1169 if (!delegate_) 1170 return; 1171 if (is_playing) 1172 delegate_->DidPlay(this); 1173 else 1174 delegate_->DidPause(this); 1175 } 1176 1177 #if defined(VIDEO_HOLE) 1178 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() { 1179 if (!video_weblayer_) 1180 return false; 1181 1182 // Compute the geometry of video frame layer. 1183 cc::Layer* layer = video_weblayer_->layer(); 1184 gfx::RectF rect(layer->bounds()); 1185 while (layer) { 1186 rect.Offset(layer->position().OffsetFromOrigin()); 1187 layer = layer->parent(); 1188 } 1189 1190 // Return false when the geometry hasn't been changed from the last time. 1191 if (last_computed_rect_ == rect) 1192 return false; 1193 1194 // Store the changed geometry information when it is actually changed. 1195 last_computed_rect_ = rect; 1196 return true; 1197 } 1198 1199 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() { 1200 return last_computed_rect_; 1201 } 1202 #endif 1203 1204 // The following EME related code is copied from WebMediaPlayerImpl. 1205 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and 1206 // WebMediaPlayerImpl. 1207 1208 // Convert a WebString to ASCII, falling back on an empty string in the case 1209 // of a non-ASCII string. 1210 static std::string ToASCIIOrEmpty(const blink::WebString& string) { 1211 return base::IsStringASCII(string) ? base::UTF16ToASCII(string) 1212 : std::string(); 1213 } 1214 1215 // Helper functions to report media EME related stats to UMA. They follow the 1216 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and 1217 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is 1218 // that UMA_* macros require the names to be constant throughout the process' 1219 // lifetime. 1220 1221 static void EmeUMAHistogramEnumeration(const std::string& key_system, 1222 const std::string& method, 1223 int sample, 1224 int boundary_value) { 1225 base::LinearHistogram::FactoryGet( 1226 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, 1227 1, boundary_value, boundary_value + 1, 1228 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); 1229 } 1230 1231 static void EmeUMAHistogramCounts(const std::string& key_system, 1232 const std::string& method, 1233 int sample) { 1234 // Use the same parameters as UMA_HISTOGRAM_COUNTS. 1235 base::Histogram::FactoryGet( 1236 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, 1237 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); 1238 } 1239 1240 // Helper enum for reporting generateKeyRequest/addKey histograms. 1241 enum MediaKeyException { 1242 kUnknownResultId, 1243 kSuccess, 1244 kKeySystemNotSupported, 1245 kInvalidPlayerState, 1246 kMaxMediaKeyException 1247 }; 1248 1249 static MediaKeyException MediaKeyExceptionForUMA( 1250 WebMediaPlayer::MediaKeyException e) { 1251 switch (e) { 1252 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: 1253 return kKeySystemNotSupported; 1254 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: 1255 return kInvalidPlayerState; 1256 case WebMediaPlayer::MediaKeyExceptionNoError: 1257 return kSuccess; 1258 default: 1259 return kUnknownResultId; 1260 } 1261 } 1262 1263 // Helper for converting |key_system| name and exception |e| to a pair of enum 1264 // values from above, for reporting to UMA. 1265 static void ReportMediaKeyExceptionToUMA(const std::string& method, 1266 const std::string& key_system, 1267 WebMediaPlayer::MediaKeyException e) { 1268 MediaKeyException result_id = MediaKeyExceptionForUMA(e); 1269 DCHECK_NE(result_id, kUnknownResultId) << e; 1270 EmeUMAHistogramEnumeration( 1271 key_system, method, result_id, kMaxMediaKeyException); 1272 } 1273 1274 bool WebMediaPlayerAndroid::IsKeySystemSupported( 1275 const std::string& key_system) { 1276 // On Android, EME only works with MSE. 1277 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && 1278 IsConcreteSupportedKeySystem(key_system); 1279 } 1280 1281 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest( 1282 const WebString& key_system, 1283 const unsigned char* init_data, 1284 unsigned init_data_length) { 1285 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": " 1286 << std::string(reinterpret_cast<const char*>(init_data), 1287 static_cast<size_t>(init_data_length)); 1288 1289 std::string ascii_key_system = 1290 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); 1291 1292 WebMediaPlayer::MediaKeyException e = 1293 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length); 1294 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e); 1295 return e; 1296 } 1297 1298 // Guess the type of |init_data|. This is only used to handle some corner cases 1299 // so we keep it as simple as possible without breaking major use cases. 1300 static std::string GuessInitDataType(const unsigned char* init_data, 1301 unsigned init_data_length) { 1302 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes. 1303 if (init_data_length == 16) 1304 return "video/webm"; 1305 1306 return "video/mp4"; 1307 } 1308 1309 // TODO(xhwang): Report an error when there is encrypted stream but EME is 1310 // not enabled. Currently the player just doesn't start and waits for 1311 // ever. 1312 WebMediaPlayer::MediaKeyException 1313 WebMediaPlayerAndroid::GenerateKeyRequestInternal( 1314 const std::string& key_system, 1315 const unsigned char* init_data, 1316 unsigned init_data_length) { 1317 if (!IsKeySystemSupported(key_system)) 1318 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1319 1320 // We do not support run-time switching between key systems for now. 1321 if (current_key_system_.empty()) { 1322 if (!proxy_decryptor_) { 1323 proxy_decryptor_.reset(new ProxyDecryptor( 1324 cdm_manager_, 1325 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, 1326 weak_factory_.GetWeakPtr()), 1327 base::Bind(&WebMediaPlayerAndroid::OnKeyError, 1328 weak_factory_.GetWeakPtr()), 1329 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage, 1330 weak_factory_.GetWeakPtr()))); 1331 } 1332 1333 GURL security_origin(frame_->document().securityOrigin().toString()); 1334 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin)) 1335 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1336 1337 if (!decryptor_ready_cb_.is_null()) { 1338 base::ResetAndReturn(&decryptor_ready_cb_) 1339 .Run(proxy_decryptor_->GetDecryptor()); 1340 } 1341 1342 // Only browser CDMs have CDM ID. Render side CDMs (e.g. ClearKey CDM) do 1343 // not have a CDM ID and there is no need to call player_manager_->SetCdm(). 1344 if (proxy_decryptor_->GetCdmId() != RendererCdmManager::kInvalidCdmId) 1345 player_manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId()); 1346 1347 current_key_system_ = key_system; 1348 } else if (key_system != current_key_system_) { 1349 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1350 } 1351 1352 std::string init_data_type = init_data_type_; 1353 if (init_data_type.empty()) 1354 init_data_type = GuessInitDataType(init_data, init_data_length); 1355 1356 // TODO(xhwang): We assume all streams are from the same container (thus have 1357 // the same "type") for now. In the future, the "type" should be passed down 1358 // from the application. 1359 if (!proxy_decryptor_->GenerateKeyRequest( 1360 init_data_type, init_data, init_data_length)) { 1361 current_key_system_.clear(); 1362 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1363 } 1364 1365 return WebMediaPlayer::MediaKeyExceptionNoError; 1366 } 1367 1368 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey( 1369 const WebString& key_system, 1370 const unsigned char* key, 1371 unsigned key_length, 1372 const unsigned char* init_data, 1373 unsigned init_data_length, 1374 const WebString& session_id) { 1375 DVLOG(1) << "addKey: " << base::string16(key_system) << ": " 1376 << std::string(reinterpret_cast<const char*>(key), 1377 static_cast<size_t>(key_length)) << ", " 1378 << std::string(reinterpret_cast<const char*>(init_data), 1379 static_cast<size_t>(init_data_length)) << " [" 1380 << base::string16(session_id) << "]"; 1381 1382 std::string ascii_key_system = 1383 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); 1384 std::string ascii_session_id = ToASCIIOrEmpty(session_id); 1385 1386 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system, 1387 key, 1388 key_length, 1389 init_data, 1390 init_data_length, 1391 ascii_session_id); 1392 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e); 1393 return e; 1394 } 1395 1396 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal( 1397 const std::string& key_system, 1398 const unsigned char* key, 1399 unsigned key_length, 1400 const unsigned char* init_data, 1401 unsigned init_data_length, 1402 const std::string& session_id) { 1403 DCHECK(key); 1404 DCHECK_GT(key_length, 0u); 1405 1406 if (!IsKeySystemSupported(key_system)) 1407 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1408 1409 if (current_key_system_.empty() || key_system != current_key_system_) 1410 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1411 1412 proxy_decryptor_->AddKey( 1413 key, key_length, init_data, init_data_length, session_id); 1414 return WebMediaPlayer::MediaKeyExceptionNoError; 1415 } 1416 1417 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest( 1418 const WebString& key_system, 1419 const WebString& session_id) { 1420 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": " 1421 << " [" << base::string16(session_id) << "]"; 1422 1423 std::string ascii_key_system = 1424 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); 1425 std::string ascii_session_id = ToASCIIOrEmpty(session_id); 1426 1427 WebMediaPlayer::MediaKeyException e = 1428 CancelKeyRequestInternal(ascii_key_system, ascii_session_id); 1429 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e); 1430 return e; 1431 } 1432 1433 WebMediaPlayer::MediaKeyException 1434 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system, 1435 const std::string& session_id) { 1436 if (!IsKeySystemSupported(key_system)) 1437 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1438 1439 if (current_key_system_.empty() || key_system != current_key_system_) 1440 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1441 1442 proxy_decryptor_->CancelKeyRequest(session_id); 1443 return WebMediaPlayer::MediaKeyExceptionNoError; 1444 } 1445 1446 void WebMediaPlayerAndroid::setContentDecryptionModule( 1447 blink::WebContentDecryptionModule* cdm) { 1448 DCHECK(main_thread_checker_.CalledOnValidThread()); 1449 1450 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 1451 if (!cdm) 1452 return; 1453 1454 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); 1455 if (!web_cdm_) 1456 return; 1457 1458 if (!decryptor_ready_cb_.is_null()) 1459 base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor()); 1460 1461 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId) 1462 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId()); 1463 } 1464 1465 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { 1466 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); 1467 1468 client_->keyAdded( 1469 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), 1470 WebString::fromUTF8(session_id)); 1471 } 1472 1473 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id, 1474 media::MediaKeys::KeyError error_code, 1475 uint32 system_code) { 1476 EmeUMAHistogramEnumeration(current_key_system_, "KeyError", 1477 error_code, media::MediaKeys::kMaxKeyError); 1478 1479 unsigned short short_system_code = 0; 1480 if (system_code > std::numeric_limits<unsigned short>::max()) { 1481 LOG(WARNING) << "system_code exceeds unsigned short limit."; 1482 short_system_code = std::numeric_limits<unsigned short>::max(); 1483 } else { 1484 short_system_code = static_cast<unsigned short>(system_code); 1485 } 1486 1487 client_->keyError( 1488 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), 1489 WebString::fromUTF8(session_id), 1490 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), 1491 short_system_code); 1492 } 1493 1494 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id, 1495 const std::vector<uint8>& message, 1496 const GURL& destination_url) { 1497 DCHECK(destination_url.is_empty() || destination_url.is_valid()); 1498 1499 client_->keyMessage( 1500 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), 1501 WebString::fromUTF8(session_id), 1502 message.empty() ? NULL : &message[0], 1503 message.size(), 1504 destination_url); 1505 } 1506 1507 void WebMediaPlayerAndroid::OnMediaSourceOpened( 1508 blink::WebMediaSource* web_media_source) { 1509 client_->mediaSourceOpened(web_media_source); 1510 } 1511 1512 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type, 1513 const std::vector<uint8>& init_data) { 1514 DCHECK(main_thread_checker_.CalledOnValidThread()); 1515 1516 // Do not fire NeedKey event if encrypted media is not enabled. 1517 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() && 1518 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) { 1519 return; 1520 } 1521 1522 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); 1523 1524 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); 1525 if (init_data_type_.empty()) 1526 init_data_type_ = type; 1527 1528 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0]; 1529 client_->keyNeeded( 1530 WebString::fromUTF8(type), init_data_ptr, init_data.size()); 1531 } 1532 1533 void WebMediaPlayerAndroid::SetDecryptorReadyCB( 1534 const media::DecryptorReadyCB& decryptor_ready_cb) { 1535 DCHECK(main_thread_checker_.CalledOnValidThread()); 1536 1537 // Cancels the previous decryptor request. 1538 if (decryptor_ready_cb.is_null()) { 1539 if (!decryptor_ready_cb_.is_null()) 1540 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL); 1541 return; 1542 } 1543 1544 // TODO(xhwang): Support multiple decryptor notification request (e.g. from 1545 // video and audio). The current implementation is okay for the current 1546 // media pipeline since we initialize audio and video decoders in sequence. 1547 // But WebMediaPlayerImpl should not depend on media pipeline's implementation 1548 // detail. 1549 DCHECK(decryptor_ready_cb_.is_null()); 1550 1551 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink. 1552 DCHECK(!proxy_decryptor_ || !web_cdm_); 1553 1554 if (proxy_decryptor_) { 1555 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor()); 1556 return; 1557 } 1558 1559 if (web_cdm_) { 1560 decryptor_ready_cb.Run(web_cdm_->GetDecryptor()); 1561 return; 1562 } 1563 1564 decryptor_ready_cb_ = decryptor_ready_cb; 1565 } 1566 1567 void WebMediaPlayerAndroid::enterFullscreen() { 1568 if (player_manager_->CanEnterFullscreen(frame_)) { 1569 player_manager_->EnterFullscreen(player_id_, frame_); 1570 SetNeedsEstablishPeer(false); 1571 } 1572 } 1573 1574 void WebMediaPlayerAndroid::exitFullscreen() { 1575 player_manager_->ExitFullscreen(player_id_); 1576 } 1577 1578 bool WebMediaPlayerAndroid::canEnterFullscreen() const { 1579 return player_manager_->CanEnterFullscreen(frame_); 1580 } 1581 1582 } // namespace content 1583