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 40static const uint32 kGLTextureExternalOES = 0x8D65; 41 42using WebKit::WebMediaPlayer; 43using WebKit::WebMediaSource; 44using WebKit::WebSize; 45using WebKit::WebString; 46using WebKit::WebTimeRanges; 47using WebKit::WebURL; 48using media::MediaPlayerAndroid; 49using media::VideoFrame; 50 51namespace { 52// Prefix for histograms related to Encrypted Media Extensions. 53const char* kMediaEme = "Media.EME."; 54} // namespace 55 56namespace content { 57 58#define BIND_TO_RENDER_LOOP(function) \ 59 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr())) 60 61WebMediaPlayerAndroid::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 150WebMediaPlayerAndroid::~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 188void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { 189 load(url, NULL, cors_mode); 190} 191 192void 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 254void 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 265void 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 282void 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 302void 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 311void 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 319bool WebMediaPlayerAndroid::supportsFullscreen() const { 320 return true; 321} 322 323bool WebMediaPlayerAndroid::supportsSave() const { 324 return false; 325} 326 327void WebMediaPlayerAndroid::setRate(double rate) { 328 NOTIMPLEMENTED(); 329} 330 331void WebMediaPlayerAndroid::setVolume(double volume) { 332 proxy_->SetVolume(player_id_, volume); 333} 334 335bool 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 356bool WebMediaPlayerAndroid::hasAudio() const { 357 // TODO(hclam): Query status of audio and return the actual value. 358 return true; 359} 360 361bool WebMediaPlayerAndroid::paused() const { 362 return !is_playing_; 363} 364 365bool WebMediaPlayerAndroid::seeking() const { 366 return seeking_; 367} 368 369double 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 380double 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 387WebSize WebMediaPlayerAndroid::naturalSize() const { 388 return natural_size_; 389} 390 391WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const { 392 return network_state_; 393} 394 395WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { 396 return ready_state_; 397} 398 399const WebTimeRanges& WebMediaPlayerAndroid::buffered() { 400 if (media_source_delegate_) 401 return media_source_delegate_->Buffered(); 402 return buffered_; 403} 404 405double 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 415bool WebMediaPlayerAndroid::didLoadingProgress() const { 416 bool ret = did_loading_progress_; 417 did_loading_progress_ = false; 418 return ret; 419} 420 421void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas, 422 const WebKit::WebRect& rect, 423 unsigned char alpha) { 424 NOTIMPLEMENTED(); 425} 426 427bool 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 472bool 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 481bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { 482 if (info_loader_) 483 return info_loader_->DidPassCORSAccessCheck(); 484 return false; 485} 486 487double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { 488 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); 489} 490 491unsigned WebMediaPlayerAndroid::decodedFrameCount() const { 492 if (media_source_delegate_) 493 return media_source_delegate_->DecodedFrameCount(); 494 NOTIMPLEMENTED(); 495 return 0; 496} 497 498unsigned WebMediaPlayerAndroid::droppedFrameCount() const { 499 if (media_source_delegate_) 500 return media_source_delegate_->DroppedFrameCount(); 501 NOTIMPLEMENTED(); 502 return 0; 503} 504 505unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { 506 if (media_source_delegate_) 507 return media_source_delegate_->AudioDecodedByteCount(); 508 NOTIMPLEMENTED(); 509 return 0; 510} 511 512unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { 513 if (media_source_delegate_) 514 return media_source_delegate_->VideoDecodedByteCount(); 515 NOTIMPLEMENTED(); 516 return 0; 517} 518 519void 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 567void 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 576void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) { 577 buffered_[0].end = duration() * percentage / 100; 578 did_loading_progress_ = true; 579} 580 581void 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 591void 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 608void 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 637void WebMediaPlayerAndroid::OnTimeUpdate(base::TimeDelta current_time) { 638 current_time_ = current_time.InSecondsF(); 639} 640 641void WebMediaPlayerAndroid::OnDidEnterFullscreen() { 642 if (!manager_->IsInFullscreen(frame_)) { 643 frame_->view()->willEnterFullScreen(); 644 frame_->view()->didEnterFullScreen(); 645 manager_->DidEnterFullscreen(frame_); 646 } 647} 648 649void 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 669void WebMediaPlayerAndroid::OnMediaPlayerPlay() { 670 UpdatePlayingState(true); 671 client_->playbackStateChanged(); 672} 673 674void WebMediaPlayerAndroid::OnMediaPlayerPause() { 675 UpdatePlayingState(false); 676 client_->playbackStateChanged(); 677} 678 679void 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 688void WebMediaPlayerAndroid::OnMediaConfigRequest() { 689 if (!media_source_delegate_) 690 return; 691 692 media_source_delegate_->OnMediaConfigRequest(); 693} 694 695void 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 716void 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 730void WebMediaPlayerAndroid::UpdateReadyState( 731 WebMediaPlayer::ReadyState state) { 732 ready_state_ = state; 733 client_->readyStateChanged(); 734} 735 736void 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 746void 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 769void WebMediaPlayerAndroid::WillDestroyCurrentMessageLoop() { 770 if (manager_) 771 manager_->UnregisterMediaPlayer(player_id_); 772 Detach(); 773} 774 775void 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 787void 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 812void 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 825scoped_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 837void WebMediaPlayerAndroid::PutCurrentFrame( 838 const scoped_refptr<media::VideoFrame>& frame) { 839} 840 841void 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 860void 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 881void 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 892void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) { 893 needs_establish_peer_ = needs_establish_peer; 894} 895 896void 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) 907bool 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. 939static 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 949static 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. 959enum MediaKeyException { 960 kUnknownResultId, 961 kSuccess, 962 kKeySystemNotSupported, 963 kInvalidPlayerState, 964 kMaxMediaKeyException 965}; 966 967static 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. 983static 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 993WebMediaPlayer::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 1003WebMediaPlayer::MediaKeyException 1004WebMediaPlayerAndroid::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 1036WebMediaPlayer::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 1049WebMediaPlayer::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 1076WebMediaPlayer::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 1085WebMediaPlayer::MediaKeyException 1086WebMediaPlayerAndroid::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 1099void 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 1108void 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 1121void 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 1135void 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) 1158bool 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 1170void WebMediaPlayerAndroid::OnReadFromDemuxer(media::DemuxerStream::Type type) { 1171 if (media_source_delegate_) 1172 media_source_delegate_->OnReadFromDemuxer(type); 1173 else 1174 NOTIMPLEMENTED(); 1175} 1176 1177void WebMediaPlayerAndroid::enterFullscreen() { 1178 if (manager_->CanEnterFullscreen(frame_)) { 1179 proxy_->EnterFullscreen(player_id_); 1180 SetNeedsEstablishPeer(false); 1181 } 1182} 1183 1184void WebMediaPlayerAndroid::exitFullscreen() { 1185 proxy_->ExitFullscreen(player_id_); 1186} 1187 1188bool WebMediaPlayerAndroid::canEnterFullscreen() const { 1189 return manager_->CanEnterFullscreen(frame_); 1190} 1191 1192} // namespace content 1193