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 "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/files/file_path.h" 10 #include "base/logging.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "cc/layers/video_layer.h" 14 #include "content/renderer/media/android/proxy_media_keys.h" 15 #include "content/renderer/media/android/renderer_media_player_manager.h" 16 #include "content/renderer/media/android/webmediaplayer_proxy_android.h" 17 #include "content/renderer/media/crypto/key_systems.h" 18 #include "content/renderer/media/webmediaplayer_delegate.h" 19 #include "content/renderer/media/webmediaplayer_util.h" 20 #include "gpu/GLES2/gl2extchromium.h" 21 #include "media/base/android/media_player_android.h" 22 #include "media/base/bind_to_loop.h" 23 #include "media/base/media_switches.h" 24 #include "media/base/video_frame.h" 25 #include "net/base/mime_util.h" 26 #include "third_party/WebKit/public/platform/WebString.h" 27 #include "third_party/WebKit/public/web/WebDocument.h" 28 #include "third_party/WebKit/public/web/WebFrame.h" 29 #include "third_party/WebKit/public/web/WebMediaPlayerClient.h" 30 #include "third_party/WebKit/public/web/WebMediaSource.h" 31 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" 32 #include "third_party/WebKit/public/web/WebView.h" 33 #include "webkit/renderer/compositor_bindings/web_layer_impl.h" 34 35 #if defined(GOOGLE_TV) 36 #include "content/renderer/media/media_stream_audio_renderer.h" 37 #include "content/renderer/media/media_stream_client.h" 38 #endif 39 40 static const uint32 kGLTextureExternalOES = 0x8D65; 41 42 using WebKit::WebMediaPlayer; 43 using WebKit::WebMediaSource; 44 using WebKit::WebSize; 45 using WebKit::WebString; 46 using WebKit::WebTimeRanges; 47 using WebKit::WebURL; 48 using media::MediaPlayerAndroid; 49 using media::VideoFrame; 50 51 namespace { 52 // Prefix for histograms related to Encrypted Media Extensions. 53 const char* kMediaEme = "Media.EME."; 54 } // namespace 55 56 namespace content { 57 58 #define BIND_TO_RENDER_LOOP(function) \ 59 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr())) 60 61 WebMediaPlayerAndroid::WebMediaPlayerAndroid( 62 WebKit::WebFrame* frame, 63 WebKit::WebMediaPlayerClient* client, 64 base::WeakPtr<WebMediaPlayerDelegate> delegate, 65 RendererMediaPlayerManager* manager, 66 WebMediaPlayerProxyAndroid* proxy, 67 StreamTextureFactory* factory, 68 const scoped_refptr<base::MessageLoopProxy>& media_loop, 69 media::MediaLog* media_log) 70 : frame_(frame), 71 client_(client), 72 delegate_(delegate), 73 buffered_(1u), 74 main_loop_(base::MessageLoopProxy::current()), 75 media_loop_(media_loop), 76 ignore_metadata_duration_change_(false), 77 pending_seek_(0), 78 seeking_(false), 79 did_loading_progress_(false), 80 manager_(manager), 81 network_state_(WebMediaPlayer::NetworkStateEmpty), 82 ready_state_(WebMediaPlayer::ReadyStateHaveNothing), 83 texture_id_(0), 84 texture_mailbox_sync_point_(0), 85 stream_id_(0), 86 is_playing_(false), 87 needs_establish_peer_(true), 88 stream_texture_proxy_initialized_(false), 89 has_size_info_(false), 90 has_media_metadata_(false), 91 has_media_info_(false), 92 stream_texture_factory_(factory), 93 needs_external_surface_(false), 94 video_frame_provider_client_(NULL), 95 #if defined(GOOGLE_TV) 96 external_surface_threshold_(-1), 97 demuxer_(NULL), 98 media_stream_client_(NULL), 99 #endif // defined(GOOGLE_TV) 100 source_type_(MediaPlayerAndroid::SOURCE_TYPE_URL), 101 proxy_(proxy), 102 current_time_(0), 103 media_log_(media_log) { 104 DCHECK(proxy_); 105 DCHECK(manager_); 106 107 // We want to be notified of |main_loop_| destruction. 108 base::MessageLoop::current()->AddDestructionObserver(this); 109 110 player_id_ = manager_->RegisterMediaPlayer(this); 111 112 #if defined(GOOGLE_TV) 113 if (CommandLine::ForCurrentProcess()->HasSwitch( 114 switches::kUseExternalVideoSurfaceThresholdInPixels)) { 115 if (!base::StringToInt( 116 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 117 switches::kUseExternalVideoSurfaceThresholdInPixels), 118 &external_surface_threshold_)) { 119 external_surface_threshold_ = -1; 120 } 121 } 122 123 // Defer stream texture creation until we are sure it's necessary. 124 stream_id_ = 0; 125 needs_establish_peer_ = false; 126 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1)); 127 #endif 128 TryCreateStreamTextureProxyIfNeeded(); 129 130 if (WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { 131 // TODO(xhwang): Report an error when there is encrypted stream but EME is 132 // not enabled. Currently the player just doesn't start and waits for ever. 133 decryptor_.reset(new ProxyDecryptor( 134 #if defined(ENABLE_PEPPER_CDMS) 135 client, 136 frame, 137 #else 138 proxy_, 139 player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are 140 // separated from WebMediaPlayer. 141 #endif // defined(ENABLE_PEPPER_CDMS) 142 // |decryptor_| is owned, so Unretained() is safe here. 143 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, base::Unretained(this)), 144 base::Bind(&WebMediaPlayerAndroid::OnKeyError, base::Unretained(this)), 145 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage, 146 base::Unretained(this)))); 147 } 148 } 149 150 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { 151 SetVideoFrameProviderClient(NULL); 152 client_->setWebLayer(NULL); 153 154 if (proxy_) 155 proxy_->DestroyPlayer(player_id_); 156 157 if (stream_id_) 158 stream_texture_factory_->DestroyStreamTexture(texture_id_); 159 160 if (manager_) 161 manager_->UnregisterMediaPlayer(player_id_); 162 163 if (base::MessageLoop::current()) 164 base::MessageLoop::current()->RemoveDestructionObserver(this); 165 166 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE && delegate_) 167 delegate_->PlayerGone(this); 168 169 #if defined(GOOGLE_TV) 170 if (audio_renderer_) { 171 if (audio_renderer_->IsLocalRenderer()) { 172 audio_renderer_->Stop(); 173 } else if (!paused()) { 174 // The |audio_renderer_| can be shared by multiple remote streams, and 175 // it will be stopped when WebRtcAudioDeviceImpl goes away. So we simply 176 // pause the |audio_renderer_| here to avoid re-creating the 177 // |audio_renderer_|. 178 audio_renderer_->Pause(); 179 } 180 } 181 if (demuxer_ && !destroy_demuxer_cb_.is_null()) { 182 media_source_delegate_.reset(); 183 destroy_demuxer_cb_.Run(); 184 } 185 #endif 186 } 187 188 void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { 189 load(url, NULL, cors_mode); 190 } 191 192 void WebMediaPlayerAndroid::load(const WebURL& url, 193 WebMediaSource* media_source, 194 CORSMode cors_mode) { 195 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_URL; 196 has_media_metadata_ = false; 197 has_media_info_ = false; 198 199 if (media_source) 200 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_MSE; 201 #if defined(GOOGLE_TV) 202 if (media_stream_client_) { 203 DCHECK(!media_source); 204 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_STREAM; 205 } 206 #endif 207 208 media::SetDecryptorReadyCB set_decryptor_ready_cb; 209 if (decryptor_) { // |decryptor_| can be NULL is EME if not enabled. 210 set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB, 211 base::Unretained(decryptor_.get())); 212 } 213 214 if (source_type_ != MediaPlayerAndroid::SOURCE_TYPE_URL) { 215 has_media_info_ = true; 216 media_source_delegate_.reset( 217 new MediaSourceDelegate(proxy_, player_id_, media_loop_, media_log_)); 218 // |media_source_delegate_| is owned, so Unretained() is safe here. 219 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE) { 220 media_source_delegate_->InitializeMediaSource( 221 media_source, 222 base::Bind(&WebMediaPlayerAndroid::OnNeedKey, base::Unretained(this)), 223 set_decryptor_ready_cb, 224 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, 225 base::Unretained(this)), 226 BIND_TO_RENDER_LOOP(&WebMediaPlayerAndroid::OnDurationChange)); 227 } 228 #if defined(GOOGLE_TV) 229 // TODO(xhwang): Pass set_decryptor_ready_cb in InitializeMediaStream() to 230 // enable ClearKey support for Google TV. 231 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_STREAM) { 232 media_source_delegate_->InitializeMediaStream( 233 demuxer_, 234 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, 235 base::Unretained(this))); 236 audio_renderer_ = media_stream_client_->GetAudioRenderer(url); 237 if (audio_renderer_) 238 audio_renderer_->Start(); 239 } 240 #endif 241 } else { 242 info_loader_.reset( 243 new MediaInfoLoader( 244 url, 245 cors_mode, 246 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo, 247 base::Unretained(this)))); 248 info_loader_->Start(frame_); 249 } 250 251 InitializeMediaPlayer(url); 252 } 253 254 void WebMediaPlayerAndroid::InitializeMediaPlayer(const WebURL& url) { 255 url_ = url; 256 GURL first_party_url = frame_->document().firstPartyForCookies(); 257 proxy_->Initialize(player_id_, url, source_type_, first_party_url); 258 if (manager_->IsInFullscreen(frame_)) 259 proxy_->EnterFullscreen(player_id_); 260 261 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading); 262 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); 263 } 264 265 void WebMediaPlayerAndroid::DidLoadMediaInfo( 266 MediaInfoLoader::Status status) { 267 DCHECK(!media_source_delegate_); 268 if (status == MediaInfoLoader::kFailed) { 269 info_loader_.reset(); 270 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError); 271 return; 272 } 273 274 has_media_info_ = true; 275 if (has_media_metadata_ && 276 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { 277 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); 278 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); 279 } 280 } 281 282 void WebMediaPlayerAndroid::play() { 283 #if defined(GOOGLE_TV) 284 if (hasVideo() && needs_external_surface_ && 285 !manager_->IsInFullscreen(frame_)) { 286 DCHECK(!needs_establish_peer_); 287 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); 288 } 289 if (audio_renderer_ && paused()) 290 audio_renderer_->Play(); 291 #endif 292 293 TryCreateStreamTextureProxyIfNeeded(); 294 if (hasVideo() && needs_establish_peer_) 295 EstablishSurfaceTexturePeer(); 296 297 if (paused()) 298 proxy_->Start(player_id_); 299 UpdatePlayingState(true); 300 } 301 302 void WebMediaPlayerAndroid::pause() { 303 #if defined(GOOGLE_TV) 304 if (audio_renderer_ && !paused()) 305 audio_renderer_->Pause(); 306 #endif 307 proxy_->Pause(player_id_); 308 UpdatePlayingState(false); 309 } 310 311 void WebMediaPlayerAndroid::seek(double seconds) { 312 pending_seek_ = seconds; 313 seeking_ = true; 314 315 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); 316 proxy_->Seek(player_id_, seek_time); 317 } 318 319 bool WebMediaPlayerAndroid::supportsFullscreen() const { 320 return true; 321 } 322 323 bool WebMediaPlayerAndroid::supportsSave() const { 324 return false; 325 } 326 327 void WebMediaPlayerAndroid::setRate(double rate) { 328 NOTIMPLEMENTED(); 329 } 330 331 void WebMediaPlayerAndroid::setVolume(double volume) { 332 proxy_->SetVolume(player_id_, volume); 333 } 334 335 bool WebMediaPlayerAndroid::hasVideo() const { 336 // If we have obtained video size information before, use it. 337 if (has_size_info_) 338 return !natural_size_.isEmpty(); 339 340 // TODO(qinmin): need a better method to determine whether the current media 341 // content contains video. Android does not provide any function to do 342 // this. 343 // We don't know whether the current media content has video unless 344 // the player is prepared. If the player is not prepared, we fall back 345 // to the mime-type. There may be no mime-type on a redirect URL. 346 // In that case, we conservatively assume it contains video so that 347 // enterfullscreen call will not fail. 348 if (!url_.has_path()) 349 return false; 350 std::string mime; 351 if(!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) 352 return true; 353 return mime.find("audio/") == std::string::npos; 354 } 355 356 bool WebMediaPlayerAndroid::hasAudio() const { 357 // TODO(hclam): Query status of audio and return the actual value. 358 return true; 359 } 360 361 bool WebMediaPlayerAndroid::paused() const { 362 return !is_playing_; 363 } 364 365 bool WebMediaPlayerAndroid::seeking() const { 366 return seeking_; 367 } 368 369 double WebMediaPlayerAndroid::duration() const { 370 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING 371 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) 372 return std::numeric_limits<double>::quiet_NaN(); 373 374 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer 375 // considers unseekable, including kInfiniteDuration(). 376 // See http://crbug.com/248396 377 return duration_.InSecondsF(); 378 } 379 380 double WebMediaPlayerAndroid::currentTime() const { 381 // If the player is pending for a seek, return the seek time. 382 if (seeking()) 383 return pending_seek_; 384 return current_time_; 385 } 386 387 WebSize WebMediaPlayerAndroid::naturalSize() const { 388 return natural_size_; 389 } 390 391 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const { 392 return network_state_; 393 } 394 395 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { 396 return ready_state_; 397 } 398 399 const WebTimeRanges& WebMediaPlayerAndroid::buffered() { 400 if (media_source_delegate_) 401 return media_source_delegate_->Buffered(); 402 return buffered_; 403 } 404 405 double WebMediaPlayerAndroid::maxTimeSeekable() const { 406 // If we haven't even gotten to ReadyStateHaveMetadata yet then just 407 // return 0 so that the seekable range is empty. 408 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata) 409 return 0.0; 410 411 // TODO(hclam): If this stream is not seekable this should return 0. 412 return duration(); 413 } 414 415 bool WebMediaPlayerAndroid::didLoadingProgress() const { 416 bool ret = did_loading_progress_; 417 did_loading_progress_ = false; 418 return ret; 419 } 420 421 void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas, 422 const WebKit::WebRect& rect, 423 unsigned char alpha) { 424 NOTIMPLEMENTED(); 425 } 426 427 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( 428 WebKit::WebGraphicsContext3D* web_graphics_context, 429 unsigned int texture, 430 unsigned int level, 431 unsigned int internal_format, 432 unsigned int type, 433 bool premultiply_alpha, 434 bool flip_y) { 435 if (!texture_id_) 436 return false; 437 438 // For hidden video element (with style "display:none"), ensure the texture 439 // size is set. 440 if (cached_stream_texture_size_.width != natural_size_.width || 441 cached_stream_texture_size_.height != natural_size_.height) { 442 stream_texture_factory_->SetStreamTextureSize( 443 stream_id_, gfx::Size(natural_size_.width, natural_size_.height)); 444 cached_stream_texture_size_ = natural_size_; 445 } 446 447 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise 448 // an invalid texture target may be used for copy texture. 449 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_); 450 451 // The video is stored in an unmultiplied format, so premultiply if 452 // necessary. 453 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, 454 premultiply_alpha); 455 456 // Application itself needs to take care of setting the right flip_y 457 // value down to get the expected result. 458 // flip_y==true means to reverse the video orientation while 459 // flip_y==false means to keep the intrinsic orientation. 460 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); 461 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, texture_id_, 462 texture, level, internal_format, 463 type); 464 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); 465 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, 466 false); 467 468 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0); 469 return true; 470 } 471 472 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const { 473 if (info_loader_) 474 return info_loader_->HasSingleOrigin(); 475 // The info loader may have failed. 476 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_URL) 477 return false; 478 return true; 479 } 480 481 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { 482 if (info_loader_) 483 return info_loader_->DidPassCORSAccessCheck(); 484 return false; 485 } 486 487 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { 488 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); 489 } 490 491 unsigned WebMediaPlayerAndroid::decodedFrameCount() const { 492 if (media_source_delegate_) 493 return media_source_delegate_->DecodedFrameCount(); 494 NOTIMPLEMENTED(); 495 return 0; 496 } 497 498 unsigned WebMediaPlayerAndroid::droppedFrameCount() const { 499 if (media_source_delegate_) 500 return media_source_delegate_->DroppedFrameCount(); 501 NOTIMPLEMENTED(); 502 return 0; 503 } 504 505 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { 506 if (media_source_delegate_) 507 return media_source_delegate_->AudioDecodedByteCount(); 508 NOTIMPLEMENTED(); 509 return 0; 510 } 511 512 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { 513 if (media_source_delegate_) 514 return media_source_delegate_->VideoDecodedByteCount(); 515 NOTIMPLEMENTED(); 516 return 0; 517 } 518 519 void WebMediaPlayerAndroid::OnMediaMetadataChanged( 520 base::TimeDelta duration, int width, int height, bool success) { 521 bool need_to_signal_duration_changed = false; 522 523 if (url_.SchemeIs("file")) 524 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded); 525 526 // Update duration, if necessary, prior to ready state updates that may 527 // cause duration() query. 528 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer 529 // considers unseekable, including kInfiniteDuration(). 530 // See http://crbug.com/248396 531 if (!ignore_metadata_duration_change_ && duration_ != duration) { 532 duration_ = duration; 533 534 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA 535 // already triggers a durationchanged event. If this is a different 536 // transition, remember to signal durationchanged. 537 // Do not ever signal durationchanged on metadata change in MSE case 538 // because OnDurationChange() handles this. 539 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing && 540 source_type_ != MediaPlayerAndroid::SOURCE_TYPE_MSE) { 541 need_to_signal_duration_changed = true; 542 } 543 } 544 545 has_media_metadata_ = true; 546 if (has_media_info_ && 547 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { 548 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); 549 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); 550 } 551 552 // TODO(wolenetz): Should we just abort early and set network state to an 553 // error if success == false? See http://crbug.com/248399 554 if (success) 555 OnVideoSizeChanged(width, height); 556 557 if (hasVideo() && !video_weblayer_ && client_->needsWebLayerForVideo()) { 558 video_weblayer_.reset( 559 new webkit::WebLayerImpl(cc::VideoLayer::Create(this))); 560 client_->setWebLayer(video_weblayer_.get()); 561 } 562 563 if (need_to_signal_duration_changed) 564 client_->durationChanged(); 565 } 566 567 void WebMediaPlayerAndroid::OnPlaybackComplete() { 568 // When playback is about to finish, android media player often stops 569 // at a time which is smaller than the duration. This makes webkit never 570 // know that the playback has finished. To solve this, we set the 571 // current time to media duration when OnPlaybackComplete() get called. 572 OnTimeUpdate(duration_); 573 client_->timeChanged(); 574 } 575 576 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) { 577 buffered_[0].end = duration() * percentage / 100; 578 did_loading_progress_ = true; 579 } 580 581 void WebMediaPlayerAndroid::OnSeekComplete(base::TimeDelta current_time) { 582 seeking_ = false; 583 584 OnTimeUpdate(current_time); 585 586 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); 587 588 client_->timeChanged(); 589 } 590 591 void WebMediaPlayerAndroid::OnMediaError(int error_type) { 592 switch (error_type) { 593 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT: 594 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); 595 break; 596 case MediaPlayerAndroid::MEDIA_ERROR_DECODE: 597 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError); 598 break; 599 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: 600 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); 601 break; 602 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE: 603 break; 604 } 605 client_->repaint(); 606 } 607 608 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { 609 has_size_info_ = true; 610 if (natural_size_.width == width && natural_size_.height == height) 611 return; 612 613 #if defined(GOOGLE_TV) 614 if ((external_surface_threshold_ >= 0 && 615 external_surface_threshold_ <= width * height) || 616 // Use H/W surface for MSE as the content is protected. 617 media_source_delegate_) { 618 needs_external_surface_ = true; 619 if (!paused() && !manager_->IsInFullscreen(frame_)) 620 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); 621 } else if (stream_texture_factory_ && !stream_texture_proxy_) { 622 // Do deferred stream texture creation finally. 623 if (paused()) { 624 DoCreateStreamTexture(); 625 SetNeedsEstablishPeer(true); 626 } else { 627 EstablishSurfaceTexturePeer(); 628 } 629 } 630 #endif 631 632 natural_size_.width = width; 633 natural_size_.height = height; 634 ReallocateVideoFrame(); 635 } 636 637 void WebMediaPlayerAndroid::OnTimeUpdate(base::TimeDelta current_time) { 638 current_time_ = current_time.InSecondsF(); 639 } 640 641 void WebMediaPlayerAndroid::OnDidEnterFullscreen() { 642 if (!manager_->IsInFullscreen(frame_)) { 643 frame_->view()->willEnterFullScreen(); 644 frame_->view()->didEnterFullScreen(); 645 manager_->DidEnterFullscreen(frame_); 646 } 647 } 648 649 void WebMediaPlayerAndroid::OnDidExitFullscreen() { 650 // |needs_external_surface_| is always false on non-TV devices. 651 if (!needs_external_surface_) 652 SetNeedsEstablishPeer(true); 653 // We had the fullscreen surface connected to Android MediaPlayer, 654 // so reconnect our surface texture for embedded playback. 655 if (!paused() && needs_establish_peer_) 656 EstablishSurfaceTexturePeer(); 657 658 #if defined(GOOGLE_TV) 659 if (!paused() && needs_external_surface_) 660 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); 661 #endif 662 663 frame_->view()->willExitFullScreen(); 664 frame_->view()->didExitFullScreen(); 665 manager_->DidExitFullscreen(); 666 client_->repaint(); 667 } 668 669 void WebMediaPlayerAndroid::OnMediaPlayerPlay() { 670 UpdatePlayingState(true); 671 client_->playbackStateChanged(); 672 } 673 674 void WebMediaPlayerAndroid::OnMediaPlayerPause() { 675 UpdatePlayingState(false); 676 client_->playbackStateChanged(); 677 } 678 679 void WebMediaPlayerAndroid::OnMediaSeekRequest(base::TimeDelta time_to_seek, 680 unsigned seek_request_id) { 681 if (!media_source_delegate_) 682 return; 683 684 media_source_delegate_->Seek(time_to_seek, seek_request_id); 685 OnTimeUpdate(time_to_seek); 686 } 687 688 void WebMediaPlayerAndroid::OnMediaConfigRequest() { 689 if (!media_source_delegate_) 690 return; 691 692 media_source_delegate_->OnMediaConfigRequest(); 693 } 694 695 void WebMediaPlayerAndroid::OnDurationChange(const base::TimeDelta& duration) { 696 // Only MSE |source_type_| registers this callback. 697 DCHECK(source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE); 698 699 // Cache the new duration value and trust it over any subsequent duration 700 // values received in OnMediaMetadataChanged(). 701 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer 702 // considers unseekable, including kInfiniteDuration(). 703 // See http://crbug.com/248396 704 duration_ = duration; 705 ignore_metadata_duration_change_ = true; 706 707 // Send message to Android MediaSourcePlayer to update duration. 708 if (proxy_) 709 proxy_->DurationChanged(player_id_, duration_); 710 711 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING. 712 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing) 713 client_->durationChanged(); 714 } 715 716 void WebMediaPlayerAndroid::UpdateNetworkState( 717 WebMediaPlayer::NetworkState state) { 718 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing && 719 (state == WebMediaPlayer::NetworkStateNetworkError || 720 state == WebMediaPlayer::NetworkStateDecodeError)) { 721 // Any error that occurs before reaching ReadyStateHaveMetadata should 722 // be considered a format error. 723 network_state_ = WebMediaPlayer::NetworkStateFormatError; 724 } else { 725 network_state_ = state; 726 } 727 client_->networkStateChanged(); 728 } 729 730 void WebMediaPlayerAndroid::UpdateReadyState( 731 WebMediaPlayer::ReadyState state) { 732 ready_state_ = state; 733 client_->readyStateChanged(); 734 } 735 736 void WebMediaPlayerAndroid::OnPlayerReleased() { 737 // |needs_external_surface_| is always false on non-TV devices. 738 if (!needs_external_surface_) 739 needs_establish_peer_ = true; 740 741 #if defined(GOOGLE_TV) 742 last_computed_rect_ = gfx::RectF(); 743 #endif 744 } 745 746 void WebMediaPlayerAndroid::ReleaseMediaResources() { 747 switch (network_state_) { 748 // Pause the media player and inform WebKit if the player is in a good 749 // shape. 750 case WebMediaPlayer::NetworkStateIdle: 751 case WebMediaPlayer::NetworkStateLoading: 752 case WebMediaPlayer::NetworkStateLoaded: 753 pause(); 754 client_->playbackStateChanged(); 755 break; 756 // If a WebMediaPlayer instance has entered into one of these states, 757 // the internal network state in HTMLMediaElement could be set to empty. 758 // And calling playbackStateChanged() could get this object deleted. 759 case WebMediaPlayer::NetworkStateEmpty: 760 case WebMediaPlayer::NetworkStateFormatError: 761 case WebMediaPlayer::NetworkStateNetworkError: 762 case WebMediaPlayer::NetworkStateDecodeError: 763 break; 764 } 765 proxy_->ReleaseResources(player_id_); 766 OnPlayerReleased(); 767 } 768 769 void WebMediaPlayerAndroid::WillDestroyCurrentMessageLoop() { 770 if (manager_) 771 manager_->UnregisterMediaPlayer(player_id_); 772 Detach(); 773 } 774 775 void WebMediaPlayerAndroid::Detach() { 776 if (stream_id_) { 777 stream_texture_factory_->DestroyStreamTexture(texture_id_); 778 stream_id_ = 0; 779 } 780 781 media_source_delegate_.reset(); 782 current_frame_ = NULL; 783 manager_ = NULL; 784 proxy_ = NULL; 785 } 786 787 void WebMediaPlayerAndroid::ReallocateVideoFrame() { 788 if (needs_external_surface_) { 789 // VideoFrame::CreateHoleFrame is only defined under GOOGLE_TV. 790 #if defined(GOOGLE_TV) 791 if (!natural_size_.isEmpty()) { 792 current_frame_ = VideoFrame::CreateHoleFrame(natural_size_); 793 // Force the client to grab the hole frame. 794 client_->repaint(); 795 } 796 #else 797 NOTIMPLEMENTED() << "Hole punching not supported outside of Google TV"; 798 #endif 799 } else if (texture_id_) { 800 current_frame_ = VideoFrame::WrapNativeTexture( 801 new VideoFrame::MailboxHolder( 802 texture_mailbox_, 803 texture_mailbox_sync_point_, 804 VideoFrame::MailboxHolder::TextureNoLongerNeededCallback()), 805 kGLTextureExternalOES, natural_size_, 806 gfx::Rect(natural_size_), natural_size_, base::TimeDelta(), 807 VideoFrame::ReadPixelsCB(), 808 base::Closure()); 809 } 810 } 811 812 void WebMediaPlayerAndroid::SetVideoFrameProviderClient( 813 cc::VideoFrameProvider::Client* client) { 814 // This is called from both the main renderer thread and the compositor 815 // thread (when the main thread is blocked). 816 if (video_frame_provider_client_) 817 video_frame_provider_client_->StopUsingProvider(); 818 video_frame_provider_client_ = client; 819 820 // Set the callback target when a frame is produced. 821 if (stream_texture_proxy_) 822 stream_texture_proxy_->SetClient(client); 823 } 824 825 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() { 826 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ && 827 stream_id_ && !needs_external_surface_) { 828 gfx::Size natural_size = current_frame_->natural_size(); 829 stream_texture_proxy_->BindToCurrentThread(stream_id_); 830 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size); 831 stream_texture_proxy_initialized_ = true; 832 cached_stream_texture_size_ = natural_size; 833 } 834 return current_frame_; 835 } 836 837 void WebMediaPlayerAndroid::PutCurrentFrame( 838 const scoped_refptr<media::VideoFrame>& frame) { 839 } 840 841 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() { 842 // Already created. 843 if (stream_texture_proxy_) 844 return; 845 846 // No factory to create proxy. 847 if (!stream_texture_factory_) 848 return; 849 850 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy()); 851 if (needs_establish_peer_ && stream_texture_proxy_) { 852 DoCreateStreamTexture(); 853 ReallocateVideoFrame(); 854 } 855 856 if (stream_texture_proxy_ && video_frame_provider_client_) 857 stream_texture_proxy_->SetClient(video_frame_provider_client_); 858 } 859 860 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() { 861 if (!stream_texture_proxy_) 862 return; 863 864 if (media_source_delegate_ && stream_texture_factory_) { 865 // MediaCodec will release the old surface when it goes away, we need to 866 // recreate a new one each time this is called. 867 stream_texture_factory_->DestroyStreamTexture(texture_id_); 868 stream_id_ = 0; 869 texture_id_ = 0; 870 texture_mailbox_ = gpu::Mailbox(); 871 texture_mailbox_sync_point_ = 0; 872 DoCreateStreamTexture(); 873 ReallocateVideoFrame(); 874 stream_texture_proxy_initialized_ = false; 875 } 876 if (stream_texture_factory_.get() && stream_id_) 877 stream_texture_factory_->EstablishPeer(stream_id_, player_id_); 878 needs_establish_peer_ = false; 879 } 880 881 void WebMediaPlayerAndroid::DoCreateStreamTexture() { 882 DCHECK(!stream_id_); 883 DCHECK(!texture_id_); 884 DCHECK(!texture_mailbox_sync_point_); 885 stream_id_ = stream_texture_factory_->CreateStreamTexture( 886 kGLTextureExternalOES, 887 &texture_id_, 888 &texture_mailbox_, 889 &texture_mailbox_sync_point_); 890 } 891 892 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) { 893 needs_establish_peer_ = needs_establish_peer; 894 } 895 896 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) { 897 is_playing_ = is_playing; 898 if (!delegate_) 899 return; 900 if (is_playing) 901 delegate_->DidPlay(this); 902 else 903 delegate_->DidPause(this); 904 } 905 906 #if defined(GOOGLE_TV) 907 bool WebMediaPlayerAndroid::RetrieveGeometryChange(gfx::RectF* rect) { 908 if (!video_weblayer_) 909 return false; 910 911 // Compute the geometry of video frame layer. 912 cc::Layer* layer = video_weblayer_->layer(); 913 rect->set_size(layer->bounds()); 914 while (layer) { 915 rect->Offset(layer->position().OffsetFromOrigin()); 916 layer = layer->parent(); 917 } 918 919 // Return false when the geometry hasn't been changed from the last time. 920 if (last_computed_rect_ == *rect) 921 return false; 922 923 // Store the changed geometry information when it is actually changed. 924 last_computed_rect_ = *rect; 925 return true; 926 } 927 #endif 928 929 // The following EME related code is copied from WebMediaPlayerImpl. 930 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and 931 // WebMediaPlayerImpl. 932 // TODO(kjyoun): Update Google TV EME implementation to use IPC. 933 934 // Helper functions to report media EME related stats to UMA. They follow the 935 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and 936 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is 937 // that UMA_* macros require the names to be constant throughout the process' 938 // lifetime. 939 static void EmeUMAHistogramEnumeration(const WebKit::WebString& key_system, 940 const std::string& method, 941 int sample, 942 int boundary_value) { 943 base::LinearHistogram::FactoryGet( 944 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, 945 1, boundary_value, boundary_value + 1, 946 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); 947 } 948 949 static void EmeUMAHistogramCounts(const WebKit::WebString& key_system, 950 const std::string& method, 951 int sample) { 952 // Use the same parameters as UMA_HISTOGRAM_COUNTS. 953 base::Histogram::FactoryGet( 954 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, 955 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); 956 } 957 958 // Helper enum for reporting generateKeyRequest/addKey histograms. 959 enum MediaKeyException { 960 kUnknownResultId, 961 kSuccess, 962 kKeySystemNotSupported, 963 kInvalidPlayerState, 964 kMaxMediaKeyException 965 }; 966 967 static MediaKeyException MediaKeyExceptionForUMA( 968 WebMediaPlayer::MediaKeyException e) { 969 switch (e) { 970 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: 971 return kKeySystemNotSupported; 972 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: 973 return kInvalidPlayerState; 974 case WebMediaPlayer::MediaKeyExceptionNoError: 975 return kSuccess; 976 default: 977 return kUnknownResultId; 978 } 979 } 980 981 // Helper for converting |key_system| name and exception |e| to a pair of enum 982 // values from above, for reporting to UMA. 983 static void ReportMediaKeyExceptionToUMA( 984 const std::string& method, 985 const WebString& key_system, 986 WebMediaPlayer::MediaKeyException e) { 987 MediaKeyException result_id = MediaKeyExceptionForUMA(e); 988 DCHECK_NE(result_id, kUnknownResultId) << e; 989 EmeUMAHistogramEnumeration( 990 key_system, method, result_id, kMaxMediaKeyException); 991 } 992 993 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest( 994 const WebString& key_system, 995 const unsigned char* init_data, 996 unsigned init_data_length) { 997 WebMediaPlayer::MediaKeyException e = 998 GenerateKeyRequestInternal(key_system, init_data, init_data_length); 999 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e); 1000 return e; 1001 } 1002 1003 WebMediaPlayer::MediaKeyException 1004 WebMediaPlayerAndroid::GenerateKeyRequestInternal( 1005 const WebString& key_system, 1006 const unsigned char* init_data, 1007 unsigned init_data_length) { 1008 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": " 1009 << std::string(reinterpret_cast<const char*>(init_data), 1010 static_cast<size_t>(init_data_length)); 1011 1012 if (!IsSupportedKeySystem(key_system)) 1013 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1014 1015 // We do not support run-time switching between key systems for now. 1016 if (current_key_system_.isEmpty()) { 1017 if (!decryptor_->InitializeCDM(key_system.utf8())) 1018 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1019 current_key_system_ = key_system; 1020 } else if (key_system != current_key_system_) { 1021 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1022 } 1023 1024 // TODO(xhwang): We assume all streams are from the same container (thus have 1025 // the same "type") for now. In the future, the "type" should be passed down 1026 // from the application. 1027 if (!decryptor_->GenerateKeyRequest(init_data_type_, 1028 init_data, init_data_length)) { 1029 current_key_system_.reset(); 1030 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1031 } 1032 1033 return WebMediaPlayer::MediaKeyExceptionNoError; 1034 } 1035 1036 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey( 1037 const WebString& key_system, 1038 const unsigned char* key, 1039 unsigned key_length, 1040 const unsigned char* init_data, 1041 unsigned init_data_length, 1042 const WebString& session_id) { 1043 WebMediaPlayer::MediaKeyException e = AddKeyInternal( 1044 key_system, key, key_length, init_data, init_data_length, session_id); 1045 ReportMediaKeyExceptionToUMA("addKey", key_system, e); 1046 return e; 1047 } 1048 1049 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal( 1050 const WebString& key_system, 1051 const unsigned char* key, 1052 unsigned key_length, 1053 const unsigned char* init_data, 1054 unsigned init_data_length, 1055 const WebString& session_id) { 1056 DCHECK(key); 1057 DCHECK_GT(key_length, 0u); 1058 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": " 1059 << std::string(reinterpret_cast<const char*>(key), 1060 static_cast<size_t>(key_length)) << ", " 1061 << std::string(reinterpret_cast<const char*>(init_data), 1062 static_cast<size_t>(init_data_length)) 1063 << " [" << session_id.utf8().data() << "]"; 1064 1065 if (!IsSupportedKeySystem(key_system)) 1066 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1067 1068 if (current_key_system_.isEmpty() || key_system != current_key_system_) 1069 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1070 1071 decryptor_->AddKey(key, key_length, init_data, init_data_length, 1072 session_id.utf8()); 1073 return WebMediaPlayer::MediaKeyExceptionNoError; 1074 } 1075 1076 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest( 1077 const WebString& key_system, 1078 const WebString& session_id) { 1079 WebMediaPlayer::MediaKeyException e = 1080 CancelKeyRequestInternal(key_system, session_id); 1081 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e); 1082 return e; 1083 } 1084 1085 WebMediaPlayer::MediaKeyException 1086 WebMediaPlayerAndroid::CancelKeyRequestInternal( 1087 const WebString& key_system, 1088 const WebString& session_id) { 1089 if (!IsSupportedKeySystem(key_system)) 1090 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 1091 1092 if (current_key_system_.isEmpty() || key_system != current_key_system_) 1093 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; 1094 1095 decryptor_->CancelKeyRequest(session_id.utf8()); 1096 return WebMediaPlayer::MediaKeyExceptionNoError; 1097 } 1098 1099 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { 1100 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); 1101 1102 if (media_source_delegate_) 1103 media_source_delegate_->NotifyKeyAdded(current_key_system_.utf8()); 1104 1105 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id)); 1106 } 1107 1108 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id, 1109 media::MediaKeys::KeyError error_code, 1110 int system_code) { 1111 EmeUMAHistogramEnumeration(current_key_system_, "KeyError", 1112 error_code, media::MediaKeys::kMaxKeyError); 1113 1114 client_->keyError( 1115 current_key_system_, 1116 WebString::fromUTF8(session_id), 1117 static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), 1118 system_code); 1119 } 1120 1121 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id, 1122 const std::vector<uint8>& message, 1123 const std::string& destination_url) { 1124 const GURL destination_url_gurl(destination_url); 1125 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid()) 1126 << "Invalid URL in destination_url: " << destination_url; 1127 1128 client_->keyMessage(current_key_system_, 1129 WebString::fromUTF8(session_id), 1130 message.empty() ? NULL : &message[0], 1131 message.size(), 1132 destination_url_gurl); 1133 } 1134 1135 void WebMediaPlayerAndroid::OnNeedKey(const std::string& session_id, 1136 const std::string& type, 1137 scoped_ptr<uint8[]> init_data, 1138 int init_data_size) { 1139 // Do not fire NeedKey event if encrypted media is not enabled. 1140 if (!WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled() && 1141 !WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { 1142 return; 1143 } 1144 1145 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); 1146 1147 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); 1148 if (init_data_type_.empty()) 1149 init_data_type_ = type; 1150 1151 client_->keyNeeded(WebString(), 1152 WebString::fromUTF8(session_id), 1153 init_data.get(), 1154 init_data_size); 1155 } 1156 1157 #if defined(GOOGLE_TV) 1158 bool WebMediaPlayerAndroid::InjectMediaStream( 1159 MediaStreamClient* media_stream_client, 1160 media::Demuxer* demuxer, 1161 const base::Closure& destroy_demuxer_cb) { 1162 DCHECK(!demuxer); 1163 media_stream_client_ = media_stream_client; 1164 demuxer_ = demuxer; 1165 destroy_demuxer_cb_ = destroy_demuxer_cb; 1166 return true; 1167 } 1168 #endif 1169 1170 void WebMediaPlayerAndroid::OnReadFromDemuxer(media::DemuxerStream::Type type) { 1171 if (media_source_delegate_) 1172 media_source_delegate_->OnReadFromDemuxer(type); 1173 else 1174 NOTIMPLEMENTED(); 1175 } 1176 1177 void WebMediaPlayerAndroid::enterFullscreen() { 1178 if (manager_->CanEnterFullscreen(frame_)) { 1179 proxy_->EnterFullscreen(player_id_); 1180 SetNeedsEstablishPeer(false); 1181 } 1182 } 1183 1184 void WebMediaPlayerAndroid::exitFullscreen() { 1185 proxy_->ExitFullscreen(player_id_); 1186 } 1187 1188 bool WebMediaPlayerAndroid::canEnterFullscreen() const { 1189 return manager_->CanEnterFullscreen(frame_); 1190 } 1191 1192 } // namespace content 1193