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