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