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.
5 #include "content/renderer/media/android/webmediaplayer_android.h"
10 #include "base/android/build_info.h"
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.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/compositor_bindings/web_layer_impl.h"
25 #include "content/renderer/media/android/renderer_demuxer_android.h"
26 #include "content/renderer/media/android/renderer_media_player_manager.h"
27 #include "content/renderer/media/crypto/key_systems.h"
28 #include "content/renderer/media/crypto/renderer_cdm_manager.h"
29 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
30 #include "content/renderer/media/webmediaplayer_delegate.h"
31 #include "content/renderer/media/webmediaplayer_util.h"
32 #include "content/renderer/render_frame_impl.h"
33 #include "content/renderer/render_thread_impl.h"
34 #include "gpu/GLES2/gl2extchromium.h"
35 #include "gpu/command_buffer/client/gles2_interface.h"
36 #include "gpu/command_buffer/common/mailbox_holder.h"
37 #include "media/base/android/media_player_android.h"
38 #include "media/base/bind_to_current_loop.h"
39 // TODO(xhwang): Remove when we remove prefixed EME implementation.
40 #include "media/base/media_keys.h"
41 #include "media/base/media_switches.h"
42 #include "media/base/video_frame.h"
43 #include "net/base/mime_util.h"
44 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
45 #include "third_party/WebKit/public/platform/WebString.h"
46 #include "third_party/WebKit/public/platform/WebURL.h"
47 #include "third_party/WebKit/public/web/WebDocument.h"
48 #include "third_party/WebKit/public/web/WebFrame.h"
49 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
50 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
51 #include "third_party/WebKit/public/web/WebView.h"
52 #include "third_party/skia/include/core/SkBitmap.h"
53 #include "third_party/skia/include/core/SkCanvas.h"
54 #include "third_party/skia/include/core/SkPaint.h"
55 #include "third_party/skia/include/core/SkTypeface.h"
56 #include "ui/gfx/image/image.h"
58 static const uint32 kGLTextureExternalOES = 0x8D65;
59 static const int kSDKVersionToSupportSecurityOriginCheck = 20;
61 using blink::WebMediaPlayer;
63 using blink::WebString;
64 using blink::WebTimeRanges;
66 using gpu::gles2::GLES2Interface;
67 using media::MediaPlayerAndroid;
68 using media::VideoFrame;
71 // Prefix for histograms related to Encrypted Media Extensions.
72 const char* kMediaEme = "Media.EME.";
74 // File-static function is to allow it to run even after WMPA is deleted.
75 void OnReleaseTexture(
76 const scoped_refptr<content::StreamTextureFactory>& factories,
78 const std::vector<uint32>& release_sync_points) {
79 GLES2Interface* gl = factories->ContextGL();
80 for (size_t i = 0; i < release_sync_points.size(); i++)
81 gl->WaitSyncPointCHROMIUM(release_sync_points[i]);
82 gl->DeleteTextures(1, &texture_id);
88 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
89 blink::WebFrame* frame,
90 blink::WebMediaPlayerClient* client,
91 base::WeakPtr<WebMediaPlayerDelegate> delegate,
92 RendererMediaPlayerManager* player_manager,
93 RendererCdmManager* cdm_manager,
94 scoped_refptr<StreamTextureFactory> factory,
95 const scoped_refptr<base::MessageLoopProxy>& media_loop,
96 media::MediaLog* media_log)
97 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
101 buffered_(static_cast<size_t>(1)),
102 media_loop_(media_loop),
103 ignore_metadata_duration_change_(false),
104 pending_seek_(false),
106 did_loading_progress_(false),
107 player_manager_(player_manager),
108 cdm_manager_(cdm_manager),
109 network_state_(WebMediaPlayer::NetworkStateEmpty),
110 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
114 needs_establish_peer_(true),
115 stream_texture_proxy_initialized_(false),
116 has_size_info_(false),
117 stream_texture_factory_(factory),
118 needs_external_surface_(false),
119 video_frame_provider_client_(NULL),
120 pending_playback_(false),
121 player_type_(MEDIA_PLAYER_TYPE_URL),
124 media_log_(media_log),
126 allow_stored_credentials_(false),
127 weak_factory_(this) {
128 DCHECK(player_manager_);
129 DCHECK(cdm_manager_);
131 DCHECK(main_thread_checker_.CalledOnValidThread());
133 player_id_ = player_manager_->RegisterMediaPlayer(this);
135 #if defined(VIDEO_HOLE)
136 force_use_overlay_embedded_video_ = CommandLine::ForCurrentProcess()->
137 HasSwitch(switches::kForceUseOverlayEmbeddedVideo);
138 if (force_use_overlay_embedded_video_ ||
139 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo()) {
140 // Defer stream texture creation until we are sure it's necessary.
141 needs_establish_peer_ = false;
142 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
144 #endif // defined(VIDEO_HOLE)
145 TryCreateStreamTextureProxyIfNeeded();
148 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
149 SetVideoFrameProviderClient(NULL);
150 client_->setWebLayer(NULL);
152 if (player_manager_) {
153 player_manager_->DestroyPlayer(player_id_);
154 player_manager_->UnregisterMediaPlayer(player_id_);
158 GLES2Interface* gl = stream_texture_factory_->ContextGL();
159 gl->DeleteTextures(1, &texture_id_);
161 texture_mailbox_ = gpu::Mailbox();
166 base::AutoLock auto_lock(current_frame_lock_);
167 current_frame_ = NULL;
170 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
171 delegate_->PlayerGone(this);
174 void WebMediaPlayerAndroid::load(LoadType load_type,
175 const blink::WebURL& url,
176 CORSMode cors_mode) {
177 ReportMediaSchemeUma(GURL(url));
181 player_type_ = MEDIA_PLAYER_TYPE_URL;
184 case LoadTypeMediaSource:
185 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
188 case LoadTypeMediaStream:
189 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
195 int demuxer_client_id = 0;
196 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
197 RendererDemuxerAndroid* demuxer =
198 RenderThreadImpl::current()->renderer_demuxer();
199 demuxer_client_id = demuxer->GetNextDemuxerClientID();
201 media_source_delegate_.reset(new MediaSourceDelegate(
202 demuxer, demuxer_client_id, media_loop_, media_log_));
204 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
205 media::SetDecryptorReadyCB set_decryptor_ready_cb =
206 media::BindToCurrentLoop(
207 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
208 weak_factory_.GetWeakPtr()));
210 media_source_delegate_->InitializeMediaSource(
211 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
212 weak_factory_.GetWeakPtr()),
213 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
214 weak_factory_.GetWeakPtr()),
215 set_decryptor_ready_cb,
216 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
217 weak_factory_.GetWeakPtr()),
218 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
219 weak_factory_.GetWeakPtr()));
220 InitializePlayer(url_, frame_->document().firstPartyForCookies(),
221 true, demuxer_client_id);
228 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
229 weak_factory_.GetWeakPtr())));
230 info_loader_->Start(frame_);
233 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
234 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
237 void WebMediaPlayerAndroid::DidLoadMediaInfo(
238 MediaInfoLoader::Status status,
239 const GURL& redirected_url,
240 const GURL& first_party_for_cookies,
241 bool allow_stored_credentials) {
242 DCHECK(!media_source_delegate_);
243 if (status == MediaInfoLoader::kFailed) {
244 info_loader_.reset();
245 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
250 redirected_url, first_party_for_cookies, allow_stored_credentials, 0);
252 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
255 void WebMediaPlayerAndroid::play() {
256 #if defined(VIDEO_HOLE)
257 if (hasVideo() && needs_external_surface_ &&
258 !player_manager_->IsInFullscreen(frame_)) {
259 DCHECK(!needs_establish_peer_);
260 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
262 #endif // defined(VIDEO_HOLE)
264 TryCreateStreamTextureProxyIfNeeded();
265 // There is no need to establish the surface texture peer for fullscreen
267 if (hasVideo() && needs_establish_peer_ &&
268 !player_manager_->IsInFullscreen(frame_)) {
269 EstablishSurfaceTexturePeer();
273 player_manager_->Start(player_id_);
274 UpdatePlayingState(true);
275 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
278 void WebMediaPlayerAndroid::pause() {
282 void WebMediaPlayerAndroid::seek(double seconds) {
283 DCHECK(main_thread_checker_.CalledOnValidThread());
284 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
286 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
289 if (new_seek_time == seek_time_) {
290 if (media_source_delegate_) {
291 if (!pending_seek_) {
292 // If using media source demuxer, only suppress redundant seeks if
293 // there is no pending seek. This enforces that any pending seek that
294 // results in a demuxer seek is preceded by matching
295 // CancelPendingSeek() and StartWaitingForSeek() calls.
299 // Suppress all redundant seeks if unrestricted by media source
301 pending_seek_ = false;
306 pending_seek_ = true;
307 pending_seek_time_ = new_seek_time;
309 if (media_source_delegate_)
310 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
312 // Later, OnSeekComplete will trigger the pending seek.
317 seek_time_ = new_seek_time;
319 if (media_source_delegate_)
320 media_source_delegate_->StartWaitingForSeek(seek_time_);
322 // Kick off the asynchronous seek!
323 player_manager_->Seek(player_id_, seek_time_);
326 bool WebMediaPlayerAndroid::supportsSave() const {
330 void WebMediaPlayerAndroid::setRate(double rate) {
334 void WebMediaPlayerAndroid::setVolume(double volume) {
335 player_manager_->SetVolume(player_id_, volume);
338 bool WebMediaPlayerAndroid::hasVideo() const {
339 // If we have obtained video size information before, use it.
341 return !natural_size_.isEmpty();
343 // TODO(qinmin): need a better method to determine whether the current media
344 // content contains video. Android does not provide any function to do
346 // We don't know whether the current media content has video unless
347 // the player is prepared. If the player is not prepared, we fall back
348 // to the mime-type. There may be no mime-type on a redirect URL.
349 // In that case, we conservatively assume it contains video so that
350 // enterfullscreen call will not fail.
351 if (!url_.has_path())
354 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
356 return mime.find("audio/") == std::string::npos;
359 bool WebMediaPlayerAndroid::hasAudio() const {
360 if (!url_.has_path())
363 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
366 if (mime.find("audio/") != std::string::npos ||
367 mime.find("video/") != std::string::npos ||
368 mime.find("application/ogg") != std::string::npos) {
374 bool WebMediaPlayerAndroid::paused() const {
378 bool WebMediaPlayerAndroid::seeking() const {
382 double WebMediaPlayerAndroid::duration() const {
383 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
384 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
385 return std::numeric_limits<double>::quiet_NaN();
387 if (duration_ == media::kInfiniteDuration())
388 return std::numeric_limits<double>::infinity();
390 return duration_.InSecondsF();
393 double WebMediaPlayerAndroid::timelineOffset() const {
394 base::Time timeline_offset;
395 if (media_source_delegate_)
396 timeline_offset = media_source_delegate_->GetTimelineOffset();
398 if (timeline_offset.is_null())
399 return std::numeric_limits<double>::quiet_NaN();
401 return timeline_offset.ToJsTime();
404 double WebMediaPlayerAndroid::currentTime() const {
405 // If the player is processing a seek, return the seek time.
406 // Blink may still query us if updatePlaybackState() occurs while seeking.
408 return pending_seek_ ?
409 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
412 return current_time_;
415 WebSize WebMediaPlayerAndroid::naturalSize() const {
416 return natural_size_;
419 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
420 return network_state_;
423 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
427 WebTimeRanges WebMediaPlayerAndroid::buffered() const {
428 if (media_source_delegate_)
429 return media_source_delegate_->Buffered();
433 double WebMediaPlayerAndroid::maxTimeSeekable() const {
434 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
435 // return 0 so that the seekable range is empty.
436 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
442 bool WebMediaPlayerAndroid::didLoadingProgress() {
443 bool ret = did_loading_progress_;
444 did_loading_progress_ = false;
448 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
449 const blink::WebRect& rect,
450 unsigned char alpha) {
454 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
455 blink::WebGraphicsContext3D* web_graphics_context,
456 unsigned int texture,
458 unsigned int internal_format,
460 bool premultiply_alpha,
462 // Don't allow clients to copy an encrypted video frame.
463 if (needs_external_surface_)
466 scoped_refptr<VideoFrame> video_frame;
468 base::AutoLock auto_lock(current_frame_lock_);
469 video_frame = current_frame_;
473 video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
475 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
476 DCHECK((!is_remote_ &&
477 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) ||
478 (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D));
480 // For hidden video element (with style "display:none"), ensure the texture
483 (cached_stream_texture_size_.width != natural_size_.width ||
484 cached_stream_texture_size_.height != natural_size_.height)) {
485 stream_texture_factory_->SetStreamTextureSize(
486 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
487 cached_stream_texture_size_ = natural_size_;
490 uint32 source_texture = web_graphics_context->createTexture();
491 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
493 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
494 // an invalid texture target may be used for copy texture.
495 web_graphics_context->bindTexture(mailbox_holder->texture_target,
497 web_graphics_context->consumeTextureCHROMIUM(mailbox_holder->texture_target,
498 mailbox_holder->mailbox.name);
500 // The video is stored in an unmultiplied format, so premultiply if
502 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
505 // Application itself needs to take care of setting the right flip_y
506 // value down to get the expected result.
507 // flip_y==true means to reverse the video orientation while
508 // flip_y==false means to keep the intrinsic orientation.
509 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
510 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture,
511 texture, level, internal_format,
513 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
514 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
517 if (mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES)
518 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
520 web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
521 web_graphics_context->deleteTexture(source_texture);
522 web_graphics_context->flush();
523 video_frame->AppendReleaseSyncPoint(web_graphics_context->insertSyncPoint());
527 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
528 if (player_type_ != MEDIA_PLAYER_TYPE_URL)
531 if (!info_loader_ || !info_loader_->HasSingleOrigin())
534 // TODO(qinmin): The url might be redirected when android media player
535 // requests the stream. As a result, we cannot guarantee there is only
536 // a single origin. Only if the HTTP request was made without credentials,
537 // we will honor the return value from HasSingleSecurityOriginInternal()
538 // in pre-L android versions.
539 // Check http://crbug.com/334204.
540 if (!allow_stored_credentials_)
543 return base::android::BuildInfo::GetInstance()->sdk_int() >=
544 kSDKVersionToSupportSecurityOriginCheck;
547 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
549 return info_loader_->DidPassCORSAccessCheck();
553 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
554 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
557 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
558 if (media_source_delegate_)
559 return media_source_delegate_->DecodedFrameCount();
564 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
565 if (media_source_delegate_)
566 return media_source_delegate_->DroppedFrameCount();
571 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
572 if (media_source_delegate_)
573 return media_source_delegate_->AudioDecodedByteCount();
578 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
579 if (media_source_delegate_)
580 return media_source_delegate_->VideoDecodedByteCount();
585 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
586 const base::TimeDelta& duration, int width, int height, bool success) {
587 bool need_to_signal_duration_changed = false;
589 if (url_.SchemeIs("file") || url_.SchemeIs("app"))
590 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
592 // Update duration, if necessary, prior to ready state updates that may
593 // cause duration() query.
594 if (!ignore_metadata_duration_change_ && duration_ != duration) {
595 duration_ = duration;
597 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
598 // already triggers a durationchanged event. If this is a different
599 // transition, remember to signal durationchanged.
600 // Do not ever signal durationchanged on metadata change in MSE case
601 // because OnDurationChanged() handles this.
602 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
603 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
604 need_to_signal_duration_changed = true;
608 if (ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
609 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
610 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
613 // TODO(wolenetz): Should we just abort early and set network state to an
614 // error if success == false? See http://crbug.com/248399
616 OnVideoSizeChanged(width, height);
618 if (need_to_signal_duration_changed)
619 client_->durationChanged();
622 void WebMediaPlayerAndroid::OnPlaybackComplete() {
623 // When playback is about to finish, android media player often stops
624 // at a time which is smaller than the duration. This makes webkit never
625 // know that the playback has finished. To solve this, we set the
626 // current time to media duration when OnPlaybackComplete() get called.
627 OnTimeUpdate(duration_);
628 client_->timeChanged();
630 // if the loop attribute is set, timeChanged() will update the current time
631 // to 0. It will perform a seek to 0. As the requests to the renderer
632 // process are sequential, the OnSeekComplete() will only occur
633 // once OnPlaybackComplete() is done. As the playback can only be executed
634 // upon completion of OnSeekComplete(), the request needs to be saved.
636 if (seeking_ && seek_time_ == base::TimeDelta())
637 pending_playback_ = true;
640 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
641 buffered_[0].end = duration() * percentage / 100;
642 did_loading_progress_ = true;
645 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
646 DCHECK(main_thread_checker_.CalledOnValidThread());
647 client_->requestSeek(time_to_seek.InSecondsF());
650 void WebMediaPlayerAndroid::OnSeekComplete(
651 const base::TimeDelta& current_time) {
652 DCHECK(main_thread_checker_.CalledOnValidThread());
655 pending_seek_ = false;
656 seek(pending_seek_time_.InSecondsF());
660 OnTimeUpdate(current_time);
662 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
664 client_->timeChanged();
666 if (pending_playback_) {
668 pending_playback_ = false;
672 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
673 switch (error_type) {
674 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
675 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
677 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
678 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
680 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
681 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
683 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
689 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
690 has_size_info_ = true;
691 if (natural_size_.width == width && natural_size_.height == height)
694 #if defined(VIDEO_HOLE)
695 // Use H/W surface for encrypted video.
696 // TODO(qinmin): Change this so that only EME needs the H/W surface
697 if (force_use_overlay_embedded_video_ ||
698 (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted() &&
699 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo())) {
700 needs_external_surface_ = true;
701 if (!paused() && !player_manager_->IsInFullscreen(frame_))
702 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
703 } else if (stream_texture_proxy_ && !stream_id_) {
704 // Do deferred stream texture creation finally.
705 DoCreateStreamTexture();
706 SetNeedsEstablishPeer(true);
708 #endif // defined(VIDEO_HOLE)
709 // When play() gets called, |natural_size_| may still be empty and
710 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
711 // may play without a surface texture. When we finally get the valid video
712 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
713 // previously called.
714 if (!paused() && needs_establish_peer_)
715 EstablishSurfaceTexturePeer();
717 natural_size_.width = width;
718 natural_size_.height = height;
719 ReallocateVideoFrame();
721 // Lazily allocate compositing layer.
722 if (!video_weblayer_) {
723 video_weblayer_.reset(new WebLayerImpl(cc::VideoLayer::Create(this)));
724 client_->setWebLayer(video_weblayer_.get());
727 // TODO(qinmin): This is a hack. We need the media element to stop showing the
728 // poster image by forcing it to call setDisplayMode(video). Should move the
729 // logic into HTMLMediaElement.cpp.
730 client_->timeChanged();
733 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
734 DCHECK(main_thread_checker_.CalledOnValidThread());
735 current_time_ = current_time.InSecondsF();
738 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
739 const std::string& remote_playback_message) {
740 DCHECK(main_thread_checker_.CalledOnValidThread());
741 DCHECK(!media_source_delegate_);
742 DrawRemotePlaybackText(remote_playback_message);
744 SetNeedsEstablishPeer(false);
747 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
748 DCHECK(main_thread_checker_.CalledOnValidThread());
749 DCHECK(!media_source_delegate_);
750 SetNeedsEstablishPeer(true);
752 EstablishSurfaceTexturePeer();
754 ReallocateVideoFrame();
757 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
758 if (!player_manager_->IsInFullscreen(frame_)) {
759 frame_->view()->willEnterFullScreen();
760 frame_->view()->didEnterFullScreen();
761 player_manager_->DidEnterFullscreen(frame_);
765 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
766 // |needs_external_surface_| is always false on non-TV devices.
767 if (!needs_external_surface_)
768 SetNeedsEstablishPeer(true);
769 // We had the fullscreen surface connected to Android MediaPlayer,
770 // so reconnect our surface texture for embedded playback.
771 if (!paused() && needs_establish_peer_)
772 EstablishSurfaceTexturePeer();
774 #if defined(VIDEO_HOLE)
775 if (!paused() && needs_external_surface_)
776 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
777 #endif // defined(VIDEO_HOLE)
779 frame_->view()->willExitFullScreen();
780 frame_->view()->didExitFullScreen();
781 player_manager_->DidExitFullscreen();
785 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
786 UpdatePlayingState(true);
787 client_->playbackStateChanged();
790 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
791 UpdatePlayingState(false);
792 client_->playbackStateChanged();
795 void WebMediaPlayerAndroid::OnRequestFullscreen() {
796 client_->requestFullscreen();
799 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
800 DCHECK(main_thread_checker_.CalledOnValidThread());
801 // Only MSE |player_type_| registers this callback.
802 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
804 // Cache the new duration value and trust it over any subsequent duration
805 // values received in OnMediaMetadataChanged().
806 duration_ = duration;
807 ignore_metadata_duration_change_ = true;
809 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
810 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
811 client_->durationChanged();
814 void WebMediaPlayerAndroid::UpdateNetworkState(
815 WebMediaPlayer::NetworkState state) {
816 DCHECK(main_thread_checker_.CalledOnValidThread());
817 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
818 (state == WebMediaPlayer::NetworkStateNetworkError ||
819 state == WebMediaPlayer::NetworkStateDecodeError)) {
820 // Any error that occurs before reaching ReadyStateHaveMetadata should
821 // be considered a format error.
822 network_state_ = WebMediaPlayer::NetworkStateFormatError;
824 network_state_ = state;
826 client_->networkStateChanged();
829 void WebMediaPlayerAndroid::UpdateReadyState(
830 WebMediaPlayer::ReadyState state) {
831 ready_state_ = state;
832 client_->readyStateChanged();
835 void WebMediaPlayerAndroid::OnPlayerReleased() {
836 // |needs_external_surface_| is always false on non-TV devices.
837 if (!needs_external_surface_)
838 needs_establish_peer_ = true;
841 OnMediaPlayerPause();
843 #if defined(VIDEO_HOLE)
844 last_computed_rect_ = gfx::RectF();
845 #endif // defined(VIDEO_HOLE)
848 void WebMediaPlayerAndroid::ReleaseMediaResources() {
849 switch (network_state_) {
850 // Pause the media player and inform WebKit if the player is in a good
852 case WebMediaPlayer::NetworkStateIdle:
853 case WebMediaPlayer::NetworkStateLoading:
854 case WebMediaPlayer::NetworkStateLoaded:
856 client_->playbackStateChanged();
858 // If a WebMediaPlayer instance has entered into one of these states,
859 // the internal network state in HTMLMediaElement could be set to empty.
860 // And calling playbackStateChanged() could get this object deleted.
861 case WebMediaPlayer::NetworkStateEmpty:
862 case WebMediaPlayer::NetworkStateFormatError:
863 case WebMediaPlayer::NetworkStateNetworkError:
864 case WebMediaPlayer::NetworkStateDecodeError:
867 player_manager_->ReleaseResources(player_id_);
871 void WebMediaPlayerAndroid::OnDestruct() {
872 NOTREACHED() << "WebMediaPlayer should be destroyed before any "
873 "RenderFrameObserver::OnDestruct() gets called when "
874 "the RenderFrame goes away.";
877 void WebMediaPlayerAndroid::InitializePlayer(
879 const GURL& first_party_for_cookies,
880 bool allow_stored_credentials,
881 int demuxer_client_id) {
882 allow_stored_credentials_ = allow_stored_credentials;
883 player_manager_->Initialize(
884 player_type_, player_id_, url, first_party_for_cookies, demuxer_client_id,
885 frame_->document().url(), allow_stored_credentials);
886 if (player_manager_->ShouldEnterFullscreen(frame_))
887 player_manager_->EnterFullscreen(player_id_, frame_);
890 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
891 player_manager_->Pause(player_id_, is_media_related_action);
892 UpdatePlayingState(false);
895 void WebMediaPlayerAndroid::DrawRemotePlaybackText(
896 const std::string& remote_playback_message) {
898 DCHECK(main_thread_checker_.CalledOnValidThread());
899 if (!video_weblayer_)
902 // TODO(johnme): Should redraw this frame if the layer bounds change; but
903 // there seems no easy way to listen for the layer resizing (as opposed to
904 // OnVideoSizeChanged, which is when the frame sizes of the video file
905 // change). Perhaps have to poll (on main thread of course)?
906 gfx::Size video_size_css_px = video_weblayer_->bounds();
907 float device_scale_factor = frame_->view()->deviceScaleFactor();
908 // canvas_size will be the size in device pixels when pageScaleFactor == 1
909 gfx::Size canvas_size(
910 static_cast<int>(video_size_css_px.width() * device_scale_factor),
911 static_cast<int>(video_size_css_px.height() * device_scale_factor));
915 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
916 bitmap.allocPixels();
918 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
919 SkCanvas canvas(bitmap);
920 canvas.drawColor(SK_ColorBLACK);
922 const SkScalar kTextSize(40);
923 const SkScalar kMinPadding(40);
926 paint.setAntiAlias(true);
927 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
928 paint.setColor(SK_ColorWHITE);
929 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
930 paint.setTextSize(kTextSize);
932 // Calculate the vertical margin from the top
933 SkPaint::FontMetrics font_metrics;
934 paint.getFontMetrics(&font_metrics);
935 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
937 // Measure the width of the entire text to display
938 size_t display_text_width = paint.measureText(
939 remote_playback_message.c_str(), remote_playback_message.size());
940 std::string display_text(remote_playback_message);
942 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
943 // The text is too long to fit in one line, truncate it and append ellipsis
946 // First, figure out how much of the canvas the '...' will take up.
947 const std::string kTruncationEllipsis("\xE2\x80\xA6");
948 SkScalar sk_ellipse_width = paint.measureText(
949 kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
951 // Then calculate how much of the text can be drawn with the '...' appended
952 // to the end of the string.
953 SkScalar sk_max_original_text_width(
954 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
955 size_t sk_max_original_text_length = paint.breakText(
956 remote_playback_message.c_str(),
957 remote_playback_message.size(),
958 sk_max_original_text_width);
960 // Remove the part of the string that doesn't fit and append '...'.
961 display_text.erase(sk_max_original_text_length,
962 remote_playback_message.size() - sk_max_original_text_length);
963 display_text.append(kTruncationEllipsis);
964 display_text_width = paint.measureText(
965 display_text.c_str(), display_text.size());
968 // Center the text horizontally.
969 SkScalar sk_horizontal_margin =
970 (canvas_size.width() - display_text_width) / 2.0;
971 canvas.drawText(display_text.c_str(),
973 sk_horizontal_margin,
977 GLES2Interface* gl = stream_texture_factory_->ContextGL();
978 GLuint remote_playback_texture_id = 0;
979 gl->GenTextures(1, &remote_playback_texture_id);
980 GLuint texture_target = GL_TEXTURE_2D;
981 gl->BindTexture(texture_target, remote_playback_texture_id);
982 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
983 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
984 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
985 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
988 SkAutoLockPixels lock(bitmap);
989 gl->TexImage2D(texture_target,
991 GL_RGBA /* internalformat */,
995 GL_RGBA /* format */,
996 GL_UNSIGNED_BYTE /* type */,
1000 gpu::Mailbox texture_mailbox;
1001 gl->GenMailboxCHROMIUM(texture_mailbox.name);
1002 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
1004 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1006 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1007 make_scoped_ptr(new gpu::MailboxHolder(
1008 texture_mailbox, texture_target, texture_mailbox_sync_point)),
1009 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture,
1010 stream_texture_factory_,
1011 remote_playback_texture_id)),
1012 canvas_size /* coded_size */,
1013 gfx::Rect(canvas_size) /* visible_rect */,
1014 canvas_size /* natural_size */,
1015 base::TimeDelta() /* timestamp */,
1016 VideoFrame::ReadPixelsCB());
1017 SetCurrentFrameInternal(new_frame);
1020 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
1021 if (needs_external_surface_) {
1022 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
1023 #if defined(VIDEO_HOLE)
1024 if (!natural_size_.isEmpty()) {
1025 scoped_refptr<VideoFrame> new_frame =
1026 VideoFrame::CreateHoleFrame(natural_size_);
1027 SetCurrentFrameInternal(new_frame);
1028 // Force the client to grab the hole frame.
1032 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
1033 #endif // defined(VIDEO_HOLE)
1034 } else if (!is_remote_ && texture_id_) {
1035 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1036 GLuint texture_id_ref = 0;
1037 gl->GenTextures(1, &texture_id_ref);
1038 GLuint texture_target = kGLTextureExternalOES;
1039 gl->BindTexture(texture_target, texture_id_ref);
1040 gl->ConsumeTextureCHROMIUM(texture_target, texture_mailbox_.name);
1042 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1044 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1045 make_scoped_ptr(new gpu::MailboxHolder(
1046 texture_mailbox_, texture_target, texture_mailbox_sync_point)),
1047 media::BindToCurrentLoop(base::Bind(
1048 &OnReleaseTexture, stream_texture_factory_, texture_id_ref)),
1050 gfx::Rect(natural_size_),
1053 VideoFrame::ReadPixelsCB());
1054 SetCurrentFrameInternal(new_frame);
1058 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1059 cc::VideoFrameProvider::Client* client) {
1060 // This is called from both the main renderer thread and the compositor
1061 // thread (when the main thread is blocked).
1062 if (video_frame_provider_client_)
1063 video_frame_provider_client_->StopUsingProvider();
1064 video_frame_provider_client_ = client;
1066 // Set the callback target when a frame is produced.
1067 if (stream_texture_proxy_)
1068 stream_texture_proxy_->SetClient(client);
1071 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1072 scoped_refptr<media::VideoFrame>& video_frame) {
1073 base::AutoLock auto_lock(current_frame_lock_);
1074 current_frame_ = video_frame;
1077 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1078 scoped_refptr<VideoFrame> video_frame;
1080 base::AutoLock auto_lock(current_frame_lock_);
1081 video_frame = current_frame_;
1084 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
1085 stream_id_ && !needs_external_surface_ && !is_remote_) {
1086 gfx::Size natural_size = video_frame->natural_size();
1087 // TODO(sievers): These variables are accessed on the wrong thread here.
1088 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1089 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1090 stream_texture_proxy_initialized_ = true;
1091 cached_stream_texture_size_ = natural_size;
1097 void WebMediaPlayerAndroid::PutCurrentFrame(
1098 const scoped_refptr<media::VideoFrame>& frame) {
1101 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1103 if (stream_texture_proxy_)
1106 // No factory to create proxy.
1107 if (!stream_texture_factory_)
1110 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1111 if (needs_establish_peer_ && stream_texture_proxy_) {
1112 DoCreateStreamTexture();
1113 ReallocateVideoFrame();
1116 if (stream_texture_proxy_ && video_frame_provider_client_)
1117 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1120 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1121 if (!stream_texture_proxy_)
1124 if (stream_texture_factory_.get() && stream_id_)
1125 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1126 needs_establish_peer_ = false;
1129 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1130 DCHECK(!stream_id_);
1131 DCHECK(!texture_id_);
1132 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1133 kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
1136 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1137 needs_establish_peer_ = needs_establish_peer;
1140 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1141 player_manager_->SetPoster(player_id_, poster);
1144 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1145 is_playing_ = is_playing;
1149 delegate_->DidPlay(this);
1151 delegate_->DidPause(this);
1154 #if defined(VIDEO_HOLE)
1155 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1156 if (!video_weblayer_)
1159 // Compute the geometry of video frame layer.
1160 cc::Layer* layer = video_weblayer_->layer();
1161 gfx::RectF rect(layer->bounds());
1163 rect.Offset(layer->position().OffsetFromOrigin());
1164 layer = layer->parent();
1167 // Return false when the geometry hasn't been changed from the last time.
1168 if (last_computed_rect_ == rect)
1171 // Store the changed geometry information when it is actually changed.
1172 last_computed_rect_ = rect;
1176 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1177 return last_computed_rect_;
1181 // The following EME related code is copied from WebMediaPlayerImpl.
1182 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1183 // WebMediaPlayerImpl.
1185 // Convert a WebString to ASCII, falling back on an empty string in the case
1186 // of a non-ASCII string.
1187 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
1188 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
1192 // Helper functions to report media EME related stats to UMA. They follow the
1193 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1194 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1195 // that UMA_* macros require the names to be constant throughout the process'
1198 static void EmeUMAHistogramEnumeration(const std::string& key_system,
1199 const std::string& method,
1201 int boundary_value) {
1202 base::LinearHistogram::FactoryGet(
1203 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1204 1, boundary_value, boundary_value + 1,
1205 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1208 static void EmeUMAHistogramCounts(const std::string& key_system,
1209 const std::string& method,
1211 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1212 base::Histogram::FactoryGet(
1213 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1214 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1217 // Helper enum for reporting generateKeyRequest/addKey histograms.
1218 enum MediaKeyException {
1221 kKeySystemNotSupported,
1222 kInvalidPlayerState,
1223 kMaxMediaKeyException
1226 static MediaKeyException MediaKeyExceptionForUMA(
1227 WebMediaPlayer::MediaKeyException e) {
1229 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1230 return kKeySystemNotSupported;
1231 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1232 return kInvalidPlayerState;
1233 case WebMediaPlayer::MediaKeyExceptionNoError:
1236 return kUnknownResultId;
1240 // Helper for converting |key_system| name and exception |e| to a pair of enum
1241 // values from above, for reporting to UMA.
1242 static void ReportMediaKeyExceptionToUMA(const std::string& method,
1243 const std::string& key_system,
1244 WebMediaPlayer::MediaKeyException e) {
1245 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1246 DCHECK_NE(result_id, kUnknownResultId) << e;
1247 EmeUMAHistogramEnumeration(
1248 key_system, method, result_id, kMaxMediaKeyException);
1251 bool WebMediaPlayerAndroid::IsKeySystemSupported(
1252 const std::string& key_system) {
1253 // On Android, EME only works with MSE.
1254 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1255 IsConcreteSupportedKeySystem(key_system);
1258 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1259 const WebString& key_system,
1260 const unsigned char* init_data,
1261 unsigned init_data_length) {
1262 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
1263 << std::string(reinterpret_cast<const char*>(init_data),
1264 static_cast<size_t>(init_data_length));
1266 std::string ascii_key_system =
1267 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1269 WebMediaPlayer::MediaKeyException e =
1270 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
1271 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
1275 // Guess the type of |init_data|. This is only used to handle some corner cases
1276 // so we keep it as simple as possible without breaking major use cases.
1277 static std::string GuessInitDataType(const unsigned char* init_data,
1278 unsigned init_data_length) {
1279 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
1280 if (init_data_length == 16)
1281 return "video/webm";
1286 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1287 // not enabled. Currently the player just doesn't start and waits for
1289 WebMediaPlayer::MediaKeyException
1290 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1291 const std::string& key_system,
1292 const unsigned char* init_data,
1293 unsigned init_data_length) {
1294 if (!IsKeySystemSupported(key_system))
1295 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1297 // We do not support run-time switching between key systems for now.
1298 if (current_key_system_.empty()) {
1299 if (!proxy_decryptor_) {
1300 proxy_decryptor_.reset(new ProxyDecryptor(
1302 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1303 weak_factory_.GetWeakPtr()),
1304 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1305 weak_factory_.GetWeakPtr()),
1306 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1307 weak_factory_.GetWeakPtr())));
1310 GURL security_origin(frame_->document().securityOrigin().toString());
1311 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
1312 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1314 if (!decryptor_ready_cb_.is_null()) {
1315 base::ResetAndReturn(&decryptor_ready_cb_)
1316 .Run(proxy_decryptor_->GetDecryptor());
1319 // Only browser CDMs have CDM ID. Render side CDMs (e.g. ClearKey CDM) do
1320 // not have a CDM ID and there is no need to call player_manager_->SetCdm().
1321 if (proxy_decryptor_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1322 player_manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId());
1324 current_key_system_ = key_system;
1325 } else if (key_system != current_key_system_) {
1326 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1329 std::string init_data_type = init_data_type_;
1330 if (init_data_type.empty())
1331 init_data_type = GuessInitDataType(init_data, init_data_length);
1333 // TODO(xhwang): We assume all streams are from the same container (thus have
1334 // the same "type") for now. In the future, the "type" should be passed down
1335 // from the application.
1336 if (!proxy_decryptor_->GenerateKeyRequest(
1337 init_data_type, init_data, init_data_length)) {
1338 current_key_system_.clear();
1339 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1342 return WebMediaPlayer::MediaKeyExceptionNoError;
1345 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1346 const WebString& key_system,
1347 const unsigned char* key,
1348 unsigned key_length,
1349 const unsigned char* init_data,
1350 unsigned init_data_length,
1351 const WebString& session_id) {
1352 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
1353 << std::string(reinterpret_cast<const char*>(key),
1354 static_cast<size_t>(key_length)) << ", "
1355 << std::string(reinterpret_cast<const char*>(init_data),
1356 static_cast<size_t>(init_data_length)) << " ["
1357 << base::string16(session_id) << "]";
1359 std::string ascii_key_system =
1360 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1361 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1363 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
1369 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
1373 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1374 const std::string& key_system,
1375 const unsigned char* key,
1376 unsigned key_length,
1377 const unsigned char* init_data,
1378 unsigned init_data_length,
1379 const std::string& session_id) {
1381 DCHECK_GT(key_length, 0u);
1383 if (!IsKeySystemSupported(key_system))
1384 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1386 if (current_key_system_.empty() || key_system != current_key_system_)
1387 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1389 proxy_decryptor_->AddKey(
1390 key, key_length, init_data, init_data_length, session_id);
1391 return WebMediaPlayer::MediaKeyExceptionNoError;
1394 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1395 const WebString& key_system,
1396 const WebString& session_id) {
1397 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
1398 << " [" << base::string16(session_id) << "]";
1400 std::string ascii_key_system =
1401 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1402 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1404 WebMediaPlayer::MediaKeyException e =
1405 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
1406 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
1410 WebMediaPlayer::MediaKeyException
1411 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
1412 const std::string& session_id) {
1413 if (!IsKeySystemSupported(key_system))
1414 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1416 if (current_key_system_.empty() || key_system != current_key_system_)
1417 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1419 proxy_decryptor_->CancelKeyRequest(session_id);
1420 return WebMediaPlayer::MediaKeyExceptionNoError;
1423 void WebMediaPlayerAndroid::setContentDecryptionModule(
1424 blink::WebContentDecryptionModule* cdm) {
1425 DCHECK(main_thread_checker_.CalledOnValidThread());
1427 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1431 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1435 if (!decryptor_ready_cb_.is_null())
1436 base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
1438 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1439 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1442 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1443 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1446 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1447 WebString::fromUTF8(session_id));
1450 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1451 media::MediaKeys::KeyError error_code,
1452 uint32 system_code) {
1453 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1454 error_code, media::MediaKeys::kMaxKeyError);
1456 unsigned short short_system_code = 0;
1457 if (system_code > std::numeric_limits<unsigned short>::max()) {
1458 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1459 short_system_code = std::numeric_limits<unsigned short>::max();
1461 short_system_code = static_cast<unsigned short>(system_code);
1465 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1466 WebString::fromUTF8(session_id),
1467 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1471 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1472 const std::vector<uint8>& message,
1473 const GURL& destination_url) {
1474 DCHECK(destination_url.is_empty() || destination_url.is_valid());
1476 client_->keyMessage(
1477 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1478 WebString::fromUTF8(session_id),
1479 message.empty() ? NULL : &message[0],
1484 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1485 blink::WebMediaSource* web_media_source) {
1486 client_->mediaSourceOpened(web_media_source);
1489 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1490 const std::vector<uint8>& init_data) {
1491 DCHECK(main_thread_checker_.CalledOnValidThread());
1493 // Do not fire NeedKey event if encrypted media is not enabled.
1494 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1495 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1499 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1501 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1502 if (init_data_type_.empty())
1503 init_data_type_ = type;
1505 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1507 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1510 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1511 const media::DecryptorReadyCB& decryptor_ready_cb) {
1512 DCHECK(main_thread_checker_.CalledOnValidThread());
1514 // Cancels the previous decryptor request.
1515 if (decryptor_ready_cb.is_null()) {
1516 if (!decryptor_ready_cb_.is_null())
1517 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1521 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1522 // video and audio). The current implementation is okay for the current
1523 // media pipeline since we initialize audio and video decoders in sequence.
1524 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1526 DCHECK(decryptor_ready_cb_.is_null());
1528 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1529 DCHECK(!proxy_decryptor_ || !web_cdm_);
1531 if (proxy_decryptor_) {
1532 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1537 decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
1541 decryptor_ready_cb_ = decryptor_ready_cb;
1544 void WebMediaPlayerAndroid::enterFullscreen() {
1545 if (player_manager_->CanEnterFullscreen(frame_)) {
1546 player_manager_->EnterFullscreen(player_id_, frame_);
1547 SetNeedsEstablishPeer(false);
1551 void WebMediaPlayerAndroid::exitFullscreen() {
1552 player_manager_->ExitFullscreen(player_id_);
1555 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1556 return player_manager_->CanEnterFullscreen(frame_);
1559 } // namespace content