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