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/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/message_loop/message_loop.h"
16 #include "base/metrics/histogram.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "cc/layers/video_layer.h"
20 #include "content/public/common/content_client.h"
21 #include "content/public/renderer/render_frame.h"
22 #include "content/renderer/media/android/proxy_media_keys.h"
23 #include "content/renderer/media/android/renderer_demuxer_android.h"
24 #include "content/renderer/media/android/renderer_media_player_manager.h"
25 #include "content/renderer/media/crypto/key_systems.h"
26 #include "content/renderer/media/webmediaplayer_delegate.h"
27 #include "content/renderer/media/webmediaplayer_util.h"
28 #include "content/renderer/render_frame_impl.h"
29 #include "content/renderer/render_thread_impl.h"
30 #include "gpu/GLES2/gl2extchromium.h"
31 #include "gpu/command_buffer/client/gles2_interface.h"
32 #include "gpu/command_buffer/common/mailbox_holder.h"
33 #include "media/base/android/media_player_android.h"
34 #include "media/base/bind_to_current_loop.h"
35 #include "media/base/media_switches.h"
36 #include "media/base/video_frame.h"
37 #include "net/base/mime_util.h"
38 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
39 #include "third_party/WebKit/public/platform/WebString.h"
40 #include "third_party/WebKit/public/platform/WebURL.h"
41 #include "third_party/WebKit/public/web/WebDocument.h"
42 #include "third_party/WebKit/public/web/WebFrame.h"
43 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
44 #include "third_party/WebKit/public/web/WebView.h"
45 #include "third_party/skia/include/core/SkBitmap.h"
46 #include "third_party/skia/include/core/SkCanvas.h"
47 #include "third_party/skia/include/core/SkPaint.h"
48 #include "third_party/skia/include/core/SkTypeface.h"
49 #include "ui/gfx/image/image.h"
50 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
52 static const uint32 kGLTextureExternalOES = 0x8D65;
54 using blink::WebMediaPlayer;
56 using blink::WebString;
57 using blink::WebTimeRanges;
59 using gpu::gles2::GLES2Interface;
60 using media::MediaPlayerAndroid;
61 using media::VideoFrame;
64 // Prefix for histograms related to Encrypted Media Extensions.
65 const char* kMediaEme = "Media.EME.";
70 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
71 blink::WebFrame* frame,
72 blink::WebMediaPlayerClient* client,
73 base::WeakPtr<WebMediaPlayerDelegate> delegate,
74 RendererMediaPlayerManager* manager,
75 StreamTextureFactory* factory,
76 const scoped_refptr<base::MessageLoopProxy>& media_loop,
77 media::MediaLog* media_log)
78 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
82 buffered_(static_cast<size_t>(1)),
83 media_loop_(media_loop),
84 ignore_metadata_duration_change_(false),
87 did_loading_progress_(false),
89 network_state_(WebMediaPlayer::NetworkStateEmpty),
90 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
91 remote_playback_texture_id_(0),
93 texture_mailbox_sync_point_(0),
96 playing_started_(false),
97 needs_establish_peer_(true),
98 stream_texture_proxy_initialized_(false),
99 has_size_info_(false),
100 has_media_metadata_(false),
101 has_media_info_(false),
102 stream_texture_factory_(factory),
103 needs_external_surface_(false),
104 video_frame_provider_client_(NULL),
105 pending_playback_(false),
106 player_type_(MEDIA_PLAYER_TYPE_URL),
109 media_log_(media_log),
110 weak_factory_(this) {
113 DCHECK(main_thread_checker_.CalledOnValidThread());
115 player_id_ = manager_->RegisterMediaPlayer(this);
117 #if defined(VIDEO_HOLE)
118 // Defer stream texture creation until we are sure it's necessary.
119 needs_establish_peer_ = false;
120 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
121 #endif // defined(VIDEO_HOLE)
122 TryCreateStreamTextureProxyIfNeeded();
125 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
126 SetVideoFrameProviderClient(NULL);
127 client_->setWebLayer(NULL);
130 manager_->DestroyPlayer(player_id_);
131 manager_->UnregisterMediaPlayer(player_id_);
135 stream_texture_factory_->DestroyStreamTexture(texture_id_);
137 if (remote_playback_texture_id_) {
138 stream_texture_factory_->ContextGL()->
139 DeleteTextures(1, &remote_playback_texture_id_);
142 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
143 delegate_->PlayerGone(this);
146 void WebMediaPlayerAndroid::load(LoadType load_type,
147 const blink::WebURL& url,
148 CORSMode cors_mode) {
151 player_type_ = MEDIA_PLAYER_TYPE_URL;
154 case LoadTypeMediaSource:
155 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
158 case LoadTypeMediaStream:
159 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
164 has_media_metadata_ = false;
165 has_media_info_ = false;
167 int demuxer_client_id = 0;
168 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
169 has_media_info_ = true;
171 RendererDemuxerAndroid* demuxer =
172 RenderThreadImpl::current()->renderer_demuxer();
173 demuxer_client_id = demuxer->GetNextDemuxerClientID();
175 media_source_delegate_.reset(new MediaSourceDelegate(
176 demuxer, demuxer_client_id, media_loop_, media_log_));
178 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
179 media::SetDecryptorReadyCB set_decryptor_ready_cb =
180 media::BindToCurrentLoop(
181 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
182 weak_factory_.GetWeakPtr()));
184 media_source_delegate_->InitializeMediaSource(
185 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
186 weak_factory_.GetWeakPtr()),
187 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
188 weak_factory_.GetWeakPtr()),
189 set_decryptor_ready_cb,
190 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
191 weak_factory_.GetWeakPtr()),
192 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
193 weak_factory_.GetWeakPtr()));
200 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
201 weak_factory_.GetWeakPtr())));
202 info_loader_->Start(frame_);
206 GURL first_party_url = frame_->document().firstPartyForCookies();
207 manager_->Initialize(
208 player_type_, player_id_, url, first_party_url, demuxer_client_id);
210 if (manager_->ShouldEnterFullscreen(frame_))
211 manager_->EnterFullscreen(player_id_, frame_);
213 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
214 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
217 void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
218 DCHECK(!media_source_delegate_);
219 if (status == MediaInfoLoader::kFailed) {
220 info_loader_.reset();
221 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
225 has_media_info_ = true;
226 if (has_media_metadata_ &&
227 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
228 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
229 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
231 // Android doesn't start fetching resources until an implementation-defined
232 // event (e.g. playback request) occurs. Sets the network state to IDLE
233 // if play is not requested yet.
234 if (!playing_started_)
235 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
238 void WebMediaPlayerAndroid::play() {
239 #if defined(VIDEO_HOLE)
240 if (hasVideo() && needs_external_surface_ &&
241 !manager_->IsInFullscreen(frame_)) {
242 DCHECK(!needs_establish_peer_);
243 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
245 #endif // defined(VIDEO_HOLE)
247 TryCreateStreamTextureProxyIfNeeded();
248 if (hasVideo() && needs_establish_peer_)
249 EstablishSurfaceTexturePeer();
252 manager_->Start(player_id_);
253 UpdatePlayingState(true);
254 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
255 playing_started_ = true;
258 void WebMediaPlayerAndroid::pause() {
262 void WebMediaPlayerAndroid::seek(double seconds) {
263 DCHECK(main_thread_checker_.CalledOnValidThread());
264 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
266 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
269 if (new_seek_time == seek_time_) {
270 if (media_source_delegate_) {
271 if (!pending_seek_) {
272 // If using media source demuxer, only suppress redundant seeks if
273 // there is no pending seek. This enforces that any pending seek that
274 // results in a demuxer seek is preceded by matching
275 // CancelPendingSeek() and StartWaitingForSeek() calls.
279 // Suppress all redundant seeks if unrestricted by media source
281 pending_seek_ = false;
286 pending_seek_ = true;
287 pending_seek_time_ = new_seek_time;
289 if (media_source_delegate_)
290 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
292 // Later, OnSeekComplete will trigger the pending seek.
297 seek_time_ = new_seek_time;
299 if (media_source_delegate_)
300 media_source_delegate_->StartWaitingForSeek(seek_time_);
302 // Kick off the asynchronous seek!
303 manager_->Seek(player_id_, seek_time_);
306 bool WebMediaPlayerAndroid::supportsSave() const {
310 void WebMediaPlayerAndroid::setRate(double rate) {
314 void WebMediaPlayerAndroid::setVolume(double volume) {
315 manager_->SetVolume(player_id_, volume);
318 bool WebMediaPlayerAndroid::hasVideo() const {
319 // If we have obtained video size information before, use it.
321 return !natural_size_.isEmpty();
323 // TODO(qinmin): need a better method to determine whether the current media
324 // content contains video. Android does not provide any function to do
326 // We don't know whether the current media content has video unless
327 // the player is prepared. If the player is not prepared, we fall back
328 // to the mime-type. There may be no mime-type on a redirect URL.
329 // In that case, we conservatively assume it contains video so that
330 // enterfullscreen call will not fail.
331 if (!url_.has_path())
334 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
336 return mime.find("audio/") == std::string::npos;
339 bool WebMediaPlayerAndroid::hasAudio() const {
340 if (!url_.has_path())
343 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
346 if (mime.find("audio/") != std::string::npos ||
347 mime.find("video/") != std::string::npos ||
348 mime.find("application/ogg") != std::string::npos) {
354 bool WebMediaPlayerAndroid::paused() const {
358 bool WebMediaPlayerAndroid::seeking() const {
362 double WebMediaPlayerAndroid::duration() const {
363 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
364 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
365 return std::numeric_limits<double>::quiet_NaN();
367 if (duration_ == media::kInfiniteDuration())
368 return std::numeric_limits<double>::infinity();
370 return duration_.InSecondsF();
373 double WebMediaPlayerAndroid::currentTime() const {
374 // If the player is processing a seek, return the seek time.
375 // Blink may still query us if updatePlaybackState() occurs while seeking.
377 return pending_seek_ ?
378 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
381 return current_time_;
384 WebSize WebMediaPlayerAndroid::naturalSize() const {
385 return natural_size_;
388 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
389 return network_state_;
392 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
396 const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
397 if (media_source_delegate_)
398 return media_source_delegate_->Buffered();
402 double WebMediaPlayerAndroid::maxTimeSeekable() const {
403 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
404 // return 0 so that the seekable range is empty.
405 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
408 if (duration() == std::numeric_limits<double>::infinity())
411 return std::min(std::numeric_limits<int32>::max() / 1000.0, duration());
414 bool WebMediaPlayerAndroid::didLoadingProgress() const {
415 bool ret = did_loading_progress_;
416 did_loading_progress_ = false;
420 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
421 const blink::WebRect& rect,
422 unsigned char alpha) {
426 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
427 blink::WebGraphicsContext3D* web_graphics_context,
428 unsigned int texture,
430 unsigned int internal_format,
432 bool premultiply_alpha,
434 if (is_remote_ || !texture_id_)
437 // For hidden video element (with style "display:none"), ensure the texture
439 if (cached_stream_texture_size_.width != natural_size_.width ||
440 cached_stream_texture_size_.height != natural_size_.height) {
441 stream_texture_factory_->SetStreamTextureSize(
442 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
443 cached_stream_texture_size_ = natural_size_;
446 uint32 source_texture = web_graphics_context->createTexture();
447 // This is strictly not necessary, because we flush when we create the
448 // one and only stream texture.
449 web_graphics_context->waitSyncPoint(texture_mailbox_sync_point_);
451 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
452 // an invalid texture target may be used for copy texture.
453 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, source_texture);
454 web_graphics_context->consumeTextureCHROMIUM(GL_TEXTURE_EXTERNAL_OES,
455 texture_mailbox_.name);
457 // The video is stored in an unmultiplied format, so premultiply if
459 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
462 // Application itself needs to take care of setting the right flip_y
463 // value down to get the expected result.
464 // flip_y==true means to reverse the video orientation while
465 // flip_y==false means to keep the intrinsic orientation.
466 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
467 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture,
468 texture, level, internal_format,
470 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
471 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
474 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
475 web_graphics_context->deleteTexture(source_texture);
476 web_graphics_context->flush();
480 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
482 return info_loader_->HasSingleOrigin();
483 // The info loader may have failed.
484 if (player_type_ == MEDIA_PLAYER_TYPE_URL)
489 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
491 return info_loader_->DidPassCORSAccessCheck();
495 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
496 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
499 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
500 if (media_source_delegate_)
501 return media_source_delegate_->DecodedFrameCount();
506 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
507 if (media_source_delegate_)
508 return media_source_delegate_->DroppedFrameCount();
513 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
514 if (media_source_delegate_)
515 return media_source_delegate_->AudioDecodedByteCount();
520 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
521 if (media_source_delegate_)
522 return media_source_delegate_->VideoDecodedByteCount();
527 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
528 const base::TimeDelta& duration, int width, int height, bool success) {
529 bool need_to_signal_duration_changed = false;
531 if (url_.SchemeIs("file") || url_.SchemeIs("app"))
532 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
534 // Update duration, if necessary, prior to ready state updates that may
535 // cause duration() query.
536 if (!ignore_metadata_duration_change_ && duration_ != duration) {
537 duration_ = duration;
539 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
540 // already triggers a durationchanged event. If this is a different
541 // transition, remember to signal durationchanged.
542 // Do not ever signal durationchanged on metadata change in MSE case
543 // because OnDurationChanged() handles this.
544 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
545 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
546 need_to_signal_duration_changed = true;
550 has_media_metadata_ = true;
551 if (has_media_info_ &&
552 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
553 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
554 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
557 // TODO(wolenetz): Should we just abort early and set network state to an
558 // error if success == false? See http://crbug.com/248399
560 OnVideoSizeChanged(width, height);
562 if (need_to_signal_duration_changed)
563 client_->durationChanged();
566 void WebMediaPlayerAndroid::OnPlaybackComplete() {
567 // When playback is about to finish, android media player often stops
568 // at a time which is smaller than the duration. This makes webkit never
569 // know that the playback has finished. To solve this, we set the
570 // current time to media duration when OnPlaybackComplete() get called.
571 OnTimeUpdate(duration_);
572 client_->timeChanged();
574 // if the loop attribute is set, timeChanged() will update the current time
575 // to 0. It will perform a seek to 0. As the requests to the renderer
576 // process are sequential, the OnSeekComplete() will only occur
577 // once OnPlaybackComplete() is done. As the playback can only be executed
578 // upon completion of OnSeekComplete(), the request needs to be saved.
580 if (seeking_ && seek_time_ == base::TimeDelta())
581 pending_playback_ = true;
584 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
585 buffered_[0].end = duration() * percentage / 100;
586 did_loading_progress_ = true;
589 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
590 DCHECK(main_thread_checker_.CalledOnValidThread());
591 client_->requestSeek(time_to_seek.InSecondsF());
594 void WebMediaPlayerAndroid::OnSeekComplete(
595 const base::TimeDelta& current_time) {
596 DCHECK(main_thread_checker_.CalledOnValidThread());
599 pending_seek_ = false;
600 seek(pending_seek_time_.InSecondsF());
604 OnTimeUpdate(current_time);
606 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
608 client_->timeChanged();
610 if (pending_playback_) {
612 pending_playback_ = false;
616 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
617 switch (error_type) {
618 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
619 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
621 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
622 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
624 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
625 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
627 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
633 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
634 has_size_info_ = true;
635 if (natural_size_.width == width && natural_size_.height == height)
638 #if defined(VIDEO_HOLE)
639 // Use H/W surface for encrypted video.
640 // TODO(qinmin): Change this so that only EME needs the H/W surface
641 if (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted()) {
642 needs_external_surface_ = true;
643 if (!paused() && !manager_->IsInFullscreen(frame_))
644 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
645 } else if (stream_texture_factory_ && !stream_id_) {
646 // Do deferred stream texture creation finally.
647 DoCreateStreamTexture();
649 SetNeedsEstablishPeer(true);
651 EstablishSurfaceTexturePeer();
655 // When play() gets called, |natural_size_| may still be empty and
656 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
657 // may play without a surface texture. When we finally get the valid video
658 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
659 // previously called.
660 if (!paused() && needs_establish_peer_)
661 EstablishSurfaceTexturePeer();
662 #endif // defined(VIDEO_HOLE)
664 natural_size_.width = width;
665 natural_size_.height = height;
666 ReallocateVideoFrame();
668 // Lazily allocate compositing layer.
669 if (!video_weblayer_) {
670 video_weblayer_.reset(
671 new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
672 client_->setWebLayer(video_weblayer_.get());
675 // TODO(qinmin): This is a hack. We need the media element to stop showing the
676 // poster image by forcing it to call setDisplayMode(video). Should move the
677 // logic into HTMLMediaElement.cpp.
678 client_->timeChanged();
681 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
682 DCHECK(main_thread_checker_.CalledOnValidThread());
683 current_time_ = current_time.InSecondsF();
686 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
687 const std::string& remote_playback_message) {
688 DCHECK(main_thread_checker_.CalledOnValidThread());
689 DCHECK(!media_source_delegate_);
690 DrawRemotePlaybackText(remote_playback_message);
692 SetNeedsEstablishPeer(false);
695 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
696 DCHECK(main_thread_checker_.CalledOnValidThread());
697 DCHECK(!media_source_delegate_);
698 SetNeedsEstablishPeer(true);
700 EstablishSurfaceTexturePeer();
702 ReallocateVideoFrame();
705 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
706 if (!manager_->IsInFullscreen(frame_)) {
707 frame_->view()->willEnterFullScreen();
708 frame_->view()->didEnterFullScreen();
709 manager_->DidEnterFullscreen(frame_);
713 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
714 // |needs_external_surface_| is always false on non-TV devices.
715 if (!needs_external_surface_)
716 SetNeedsEstablishPeer(true);
717 // We had the fullscreen surface connected to Android MediaPlayer,
718 // so reconnect our surface texture for embedded playback.
719 if (!paused() && needs_establish_peer_)
720 EstablishSurfaceTexturePeer();
722 #if defined(VIDEO_HOLE)
723 if (!paused() && needs_external_surface_)
724 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
725 #endif // defined(VIDEO_HOLE)
727 frame_->view()->willExitFullScreen();
728 frame_->view()->didExitFullScreen();
729 manager_->DidExitFullscreen();
733 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
734 UpdatePlayingState(true);
735 client_->playbackStateChanged();
738 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
739 UpdatePlayingState(false);
740 client_->playbackStateChanged();
743 void WebMediaPlayerAndroid::OnRequestFullscreen() {
744 client_->requestFullscreen();
747 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
748 DCHECK(main_thread_checker_.CalledOnValidThread());
749 // Only MSE |player_type_| registers this callback.
750 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
752 // Cache the new duration value and trust it over any subsequent duration
753 // values received in OnMediaMetadataChanged().
754 duration_ = duration;
755 ignore_metadata_duration_change_ = true;
757 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
758 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
759 client_->durationChanged();
762 void WebMediaPlayerAndroid::UpdateNetworkState(
763 WebMediaPlayer::NetworkState state) {
764 DCHECK(main_thread_checker_.CalledOnValidThread());
765 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
766 (state == WebMediaPlayer::NetworkStateNetworkError ||
767 state == WebMediaPlayer::NetworkStateDecodeError)) {
768 // Any error that occurs before reaching ReadyStateHaveMetadata should
769 // be considered a format error.
770 network_state_ = WebMediaPlayer::NetworkStateFormatError;
772 network_state_ = state;
774 client_->networkStateChanged();
777 void WebMediaPlayerAndroid::UpdateReadyState(
778 WebMediaPlayer::ReadyState state) {
779 ready_state_ = state;
780 client_->readyStateChanged();
783 void WebMediaPlayerAndroid::OnPlayerReleased() {
784 // |needs_external_surface_| is always false on non-TV devices.
785 if (!needs_external_surface_)
786 needs_establish_peer_ = true;
789 OnMediaPlayerPause();
791 #if defined(VIDEO_HOLE)
792 last_computed_rect_ = gfx::RectF();
793 #endif // defined(VIDEO_HOLE)
796 void WebMediaPlayerAndroid::ReleaseMediaResources() {
797 switch (network_state_) {
798 // Pause the media player and inform WebKit if the player is in a good
800 case WebMediaPlayer::NetworkStateIdle:
801 case WebMediaPlayer::NetworkStateLoading:
802 case WebMediaPlayer::NetworkStateLoaded:
804 client_->playbackStateChanged();
806 // If a WebMediaPlayer instance has entered into one of these states,
807 // the internal network state in HTMLMediaElement could be set to empty.
808 // And calling playbackStateChanged() could get this object deleted.
809 case WebMediaPlayer::NetworkStateEmpty:
810 case WebMediaPlayer::NetworkStateFormatError:
811 case WebMediaPlayer::NetworkStateNetworkError:
812 case WebMediaPlayer::NetworkStateDecodeError:
815 manager_->ReleaseResources(player_id_);
819 void WebMediaPlayerAndroid::OnDestruct() {
821 manager_->UnregisterMediaPlayer(player_id_);
825 void WebMediaPlayerAndroid::Detach() {
827 stream_texture_factory_->DestroyStreamTexture(texture_id_);
831 media_source_delegate_.reset();
833 base::AutoLock auto_lock(current_frame_lock_);
834 current_frame_ = NULL;
840 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
841 manager_->Pause(player_id_, is_media_related_action);
842 UpdatePlayingState(false);
845 void WebMediaPlayerAndroid::DrawRemotePlaybackText(
846 const std::string& remote_playback_message) {
848 DCHECK(main_thread_checker_.CalledOnValidThread());
849 if (!video_weblayer_)
852 // TODO(johnme): Should redraw this frame if the layer bounds change; but
853 // there seems no easy way to listen for the layer resizing (as opposed to
854 // OnVideoSizeChanged, which is when the frame sizes of the video file
855 // change). Perhaps have to poll (on main thread of course)?
856 gfx::Size video_size_css_px = video_weblayer_->bounds();
857 float device_scale_factor = frame_->view()->deviceScaleFactor();
858 // canvas_size will be the size in device pixels when pageScaleFactor == 1
859 gfx::Size canvas_size(
860 static_cast<int>(video_size_css_px.width() * device_scale_factor),
861 static_cast<int>(video_size_css_px.height() * device_scale_factor));
865 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
866 bitmap.allocPixels();
868 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
869 SkCanvas canvas(bitmap);
870 canvas.drawColor(SK_ColorBLACK);
872 const SkScalar kTextSize(40);
873 const SkScalar kMinPadding(40);
876 paint.setAntiAlias(true);
877 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
878 paint.setColor(SK_ColorWHITE);
879 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
880 paint.setTextSize(kTextSize);
882 // Calculate the vertical margin from the top
883 SkPaint::FontMetrics font_metrics;
884 paint.getFontMetrics(&font_metrics);
885 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
887 // Measure the width of the entire text to display
888 size_t display_text_width = paint.measureText(
889 remote_playback_message.c_str(), remote_playback_message.size());
890 std::string display_text(remote_playback_message);
892 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
893 // The text is too long to fit in one line, truncate it and append ellipsis
896 // First, figure out how much of the canvas the '...' will take up.
897 const std::string kTruncationEllipsis("\xE2\x80\xA6");
898 SkScalar sk_ellipse_width = paint.measureText(
899 kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
901 // Then calculate how much of the text can be drawn with the '...' appended
902 // to the end of the string.
903 SkScalar sk_max_original_text_width(
904 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
905 size_t sk_max_original_text_length = paint.breakText(
906 remote_playback_message.c_str(),
907 remote_playback_message.size(),
908 sk_max_original_text_width);
910 // Remove the part of the string that doesn't fit and append '...'.
911 display_text.erase(sk_max_original_text_length,
912 remote_playback_message.size() - sk_max_original_text_length);
913 display_text.append(kTruncationEllipsis);
914 display_text_width = paint.measureText(
915 display_text.c_str(), display_text.size());
918 // Center the text horizontally.
919 SkScalar sk_horizontal_margin =
920 (canvas_size.width() - display_text_width) / 2.0;
921 canvas.drawText(display_text.c_str(),
923 sk_horizontal_margin,
927 GLES2Interface* gl = stream_texture_factory_->ContextGL();
929 if (!remote_playback_texture_id_)
930 gl->GenTextures(1, &remote_playback_texture_id_);
931 GLuint texture_target = GL_TEXTURE_2D;
932 gl->BindTexture(texture_target, remote_playback_texture_id_);
933 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
934 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
935 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
936 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
939 SkAutoLockPixels lock(bitmap);
940 gl->TexImage2D(texture_target,
942 GL_RGBA /* internalformat */,
946 GL_RGBA /* format */,
947 GL_UNSIGNED_BYTE /* type */,
951 gpu::Mailbox texture_mailbox;
952 gl->GenMailboxCHROMIUM(texture_mailbox.name);
953 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
955 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
957 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
958 make_scoped_ptr(new gpu::MailboxHolder(
959 texture_mailbox, texture_target, texture_mailbox_sync_point)),
960 media::BindToCurrentLoop(
961 base::Bind(&WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture,
962 weak_factory_.GetWeakPtr())),
963 canvas_size /* coded_size */,
964 gfx::Rect(canvas_size) /* visible_rect */,
965 canvas_size /* natural_size */,
966 base::TimeDelta() /* timestamp */,
967 VideoFrame::ReadPixelsCB());
968 SetCurrentFrameInternal(new_frame);
971 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
972 if (needs_external_surface_) {
973 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
974 #if defined(VIDEO_HOLE)
975 if (!natural_size_.isEmpty()) {
976 scoped_refptr<VideoFrame> new_frame =
977 VideoFrame::CreateHoleFrame(natural_size_);
978 SetCurrentFrameInternal(new_frame);
979 // Force the client to grab the hole frame.
983 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
984 #endif // defined(VIDEO_HOLE)
985 } else if (!is_remote_ && texture_id_) {
986 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
987 make_scoped_ptr(new gpu::MailboxHolder(texture_mailbox_,
988 kGLTextureExternalOES,
989 texture_mailbox_sync_point_)),
990 media::VideoFrame::ReleaseMailboxCB(),
992 gfx::Rect(natural_size_),
995 VideoFrame::ReadPixelsCB());
996 SetCurrentFrameInternal(new_frame);
1000 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1001 cc::VideoFrameProvider::Client* client) {
1002 // This is called from both the main renderer thread and the compositor
1003 // thread (when the main thread is blocked).
1004 if (video_frame_provider_client_)
1005 video_frame_provider_client_->StopUsingProvider();
1006 video_frame_provider_client_ = client;
1008 // Set the callback target when a frame is produced.
1009 if (stream_texture_proxy_)
1010 stream_texture_proxy_->SetClient(client);
1013 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1014 scoped_refptr<media::VideoFrame>& video_frame) {
1015 base::AutoLock auto_lock(current_frame_lock_);
1016 current_frame_ = video_frame;
1019 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1020 scoped_refptr<VideoFrame> video_frame;
1022 base::AutoLock auto_lock(current_frame_lock_);
1023 video_frame = current_frame_;
1026 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
1027 stream_id_ && !needs_external_surface_ && !is_remote_) {
1028 gfx::Size natural_size = video_frame->natural_size();
1029 // TODO(sievers): These variables are accessed on the wrong thread here.
1030 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1031 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1032 stream_texture_proxy_initialized_ = true;
1033 cached_stream_texture_size_ = natural_size;
1039 void WebMediaPlayerAndroid::PutCurrentFrame(
1040 const scoped_refptr<media::VideoFrame>& frame) {
1043 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1045 if (stream_texture_proxy_)
1048 // No factory to create proxy.
1049 if (!stream_texture_factory_)
1052 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1053 if (needs_establish_peer_ && stream_texture_proxy_) {
1054 DoCreateStreamTexture();
1055 ReallocateVideoFrame();
1058 if (stream_texture_proxy_ && video_frame_provider_client_)
1059 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1062 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1063 if (!stream_texture_proxy_)
1066 if (stream_texture_factory_.get() && stream_id_)
1067 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1068 needs_establish_peer_ = false;
1071 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1072 DCHECK(!stream_id_);
1073 DCHECK(!texture_id_);
1074 DCHECK(!texture_mailbox_sync_point_);
1075 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1076 kGLTextureExternalOES,
1079 &texture_mailbox_sync_point_);
1082 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1083 needs_establish_peer_ = needs_establish_peer;
1086 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1087 manager_->SetPoster(player_id_, poster);
1090 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1091 is_playing_ = is_playing;
1095 delegate_->DidPlay(this);
1097 delegate_->DidPause(this);
1100 #if defined(VIDEO_HOLE)
1101 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1102 if (!video_weblayer_)
1105 // Compute the geometry of video frame layer.
1106 cc::Layer* layer = video_weblayer_->layer();
1107 gfx::RectF rect(layer->bounds());
1109 rect.Offset(layer->position().OffsetFromOrigin());
1110 layer = layer->parent();
1113 // Return false when the geometry hasn't been changed from the last time.
1114 if (last_computed_rect_ == rect)
1117 // Store the changed geometry information when it is actually changed.
1118 last_computed_rect_ = rect;
1122 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1123 return last_computed_rect_;
1127 // The following EME related code is copied from WebMediaPlayerImpl.
1128 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1129 // WebMediaPlayerImpl.
1131 // Convert a WebString to ASCII, falling back on an empty string in the case
1132 // of a non-ASCII string.
1133 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
1134 return IsStringASCII(string) ? base::UTF16ToASCII(string) : std::string();
1137 // Helper functions to report media EME related stats to UMA. They follow the
1138 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1139 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1140 // that UMA_* macros require the names to be constant throughout the process'
1143 static void EmeUMAHistogramEnumeration(const std::string& key_system,
1144 const std::string& method,
1146 int boundary_value) {
1147 base::LinearHistogram::FactoryGet(
1148 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1149 1, boundary_value, boundary_value + 1,
1150 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1153 static void EmeUMAHistogramCounts(const std::string& key_system,
1154 const std::string& method,
1156 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1157 base::Histogram::FactoryGet(
1158 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1159 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1162 // Helper enum for reporting generateKeyRequest/addKey histograms.
1163 enum MediaKeyException {
1166 kKeySystemNotSupported,
1167 kInvalidPlayerState,
1168 kMaxMediaKeyException
1171 static MediaKeyException MediaKeyExceptionForUMA(
1172 WebMediaPlayer::MediaKeyException e) {
1174 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1175 return kKeySystemNotSupported;
1176 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1177 return kInvalidPlayerState;
1178 case WebMediaPlayer::MediaKeyExceptionNoError:
1181 return kUnknownResultId;
1185 // Helper for converting |key_system| name and exception |e| to a pair of enum
1186 // values from above, for reporting to UMA.
1187 static void ReportMediaKeyExceptionToUMA(const std::string& method,
1188 const std::string& key_system,
1189 WebMediaPlayer::MediaKeyException e) {
1190 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1191 DCHECK_NE(result_id, kUnknownResultId) << e;
1192 EmeUMAHistogramEnumeration(
1193 key_system, method, result_id, kMaxMediaKeyException);
1196 bool WebMediaPlayerAndroid::IsKeySystemSupported(
1197 const std::string& key_system) {
1198 // On Android, EME only works with MSE.
1199 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1200 IsConcreteSupportedKeySystem(key_system);
1203 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1204 const WebString& key_system,
1205 const unsigned char* init_data,
1206 unsigned init_data_length) {
1207 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
1208 << std::string(reinterpret_cast<const char*>(init_data),
1209 static_cast<size_t>(init_data_length));
1211 std::string ascii_key_system =
1212 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1214 WebMediaPlayer::MediaKeyException e =
1215 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
1216 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
1220 // Guess the type of |init_data|. This is only used to handle some corner cases
1221 // so we keep it as simple as possible without breaking major use cases.
1222 static std::string GuessInitDataType(const unsigned char* init_data,
1223 unsigned init_data_length) {
1224 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
1225 if (init_data_length == 16)
1226 return "video/webm";
1231 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1232 // not enabled. Currently the player just doesn't start and waits for
1234 WebMediaPlayer::MediaKeyException
1235 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1236 const std::string& key_system,
1237 const unsigned char* init_data,
1238 unsigned init_data_length) {
1239 if (!IsKeySystemSupported(key_system))
1240 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1242 // We do not support run-time switching between key systems for now.
1243 if (current_key_system_.empty()) {
1244 if (!proxy_decryptor_) {
1245 proxy_decryptor_.reset(new ProxyDecryptor(
1246 #if defined(ENABLE_PEPPER_CDMS)
1251 player_id_, // TODO(xhwang): Use cdm_id when MediaKeys are
1252 // separated from WebMediaPlayer.
1253 #endif // defined(ENABLE_PEPPER_CDMS)
1254 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1255 weak_factory_.GetWeakPtr()),
1256 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1257 weak_factory_.GetWeakPtr()),
1258 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1259 weak_factory_.GetWeakPtr())));
1262 if (!proxy_decryptor_->InitializeCDM(key_system,
1263 frame_->document().url())) {
1264 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1267 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
1268 base::ResetAndReturn(&decryptor_ready_cb_)
1269 .Run(proxy_decryptor_->GetDecryptor());
1272 current_key_system_ = key_system;
1273 } else if (key_system != current_key_system_) {
1274 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1277 std::string init_data_type = init_data_type_;
1278 if (init_data_type.empty())
1279 init_data_type = GuessInitDataType(init_data, init_data_length);
1281 // TODO(xhwang): We assume all streams are from the same container (thus have
1282 // the same "type") for now. In the future, the "type" should be passed down
1283 // from the application.
1284 if (!proxy_decryptor_->GenerateKeyRequest(
1285 init_data_type, init_data, init_data_length)) {
1286 current_key_system_.clear();
1287 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1290 return WebMediaPlayer::MediaKeyExceptionNoError;
1293 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1294 const WebString& key_system,
1295 const unsigned char* key,
1296 unsigned key_length,
1297 const unsigned char* init_data,
1298 unsigned init_data_length,
1299 const WebString& session_id) {
1300 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
1301 << std::string(reinterpret_cast<const char*>(key),
1302 static_cast<size_t>(key_length)) << ", "
1303 << std::string(reinterpret_cast<const char*>(init_data),
1304 static_cast<size_t>(init_data_length)) << " ["
1305 << base::string16(session_id) << "]";
1307 std::string ascii_key_system =
1308 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1309 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1311 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
1317 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
1321 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1322 const std::string& key_system,
1323 const unsigned char* key,
1324 unsigned key_length,
1325 const unsigned char* init_data,
1326 unsigned init_data_length,
1327 const std::string& session_id) {
1329 DCHECK_GT(key_length, 0u);
1331 if (!IsKeySystemSupported(key_system))
1332 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1334 if (current_key_system_.empty() || key_system != current_key_system_)
1335 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1337 proxy_decryptor_->AddKey(
1338 key, key_length, init_data, init_data_length, session_id);
1339 return WebMediaPlayer::MediaKeyExceptionNoError;
1342 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1343 const WebString& key_system,
1344 const WebString& session_id) {
1345 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
1346 << " [" << base::string16(session_id) << "]";
1348 std::string ascii_key_system =
1349 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1350 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1352 WebMediaPlayer::MediaKeyException e =
1353 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
1354 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
1358 WebMediaPlayer::MediaKeyException
1359 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
1360 const std::string& session_id) {
1361 if (!IsKeySystemSupported(key_system))
1362 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1364 if (current_key_system_.empty() || key_system != current_key_system_)
1365 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1367 proxy_decryptor_->CancelKeyRequest(session_id);
1368 return WebMediaPlayer::MediaKeyExceptionNoError;
1371 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1372 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1375 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1376 WebString::fromUTF8(session_id));
1379 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1380 media::MediaKeys::KeyError error_code,
1381 uint32 system_code) {
1382 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1383 error_code, media::MediaKeys::kMaxKeyError);
1385 unsigned short short_system_code = 0;
1386 if (system_code > std::numeric_limits<unsigned short>::max()) {
1387 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1388 short_system_code = std::numeric_limits<unsigned short>::max();
1390 short_system_code = static_cast<unsigned short>(system_code);
1394 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1395 WebString::fromUTF8(session_id),
1396 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1400 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1401 const std::vector<uint8>& message,
1402 const std::string& destination_url) {
1403 const GURL destination_url_gurl(destination_url);
1404 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid())
1405 << "Invalid URL in destination_url: " << destination_url;
1407 client_->keyMessage(
1408 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1409 WebString::fromUTF8(session_id),
1410 message.empty() ? NULL : &message[0],
1412 destination_url_gurl);
1415 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1416 blink::WebMediaSource* web_media_source) {
1417 client_->mediaSourceOpened(web_media_source);
1420 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1421 const std::vector<uint8>& init_data) {
1422 DCHECK(main_thread_checker_.CalledOnValidThread());
1424 // Do not fire NeedKey event if encrypted media is not enabled.
1425 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1426 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1430 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1432 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1433 if (init_data_type_.empty())
1434 init_data_type_ = type;
1436 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1438 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1441 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1442 const media::DecryptorReadyCB& decryptor_ready_cb) {
1443 DCHECK(main_thread_checker_.CalledOnValidThread());
1445 // Cancels the previous decryptor request.
1446 if (decryptor_ready_cb.is_null()) {
1447 if (!decryptor_ready_cb_.is_null())
1448 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1452 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1453 // video and audio). The current implementation is okay for the current
1454 // media pipeline since we initialize audio and video decoders in sequence.
1455 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1457 DCHECK(decryptor_ready_cb_.is_null());
1459 if (proxy_decryptor_) {
1460 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1464 // TODO(xhwang): Also notify |web_cdm_| when we implement
1465 // setContentDecryptionModule(). See: http://crbug.com/224786
1467 decryptor_ready_cb_ = decryptor_ready_cb;
1470 void WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture(
1471 scoped_ptr<gpu::MailboxHolder> mailbox_holder) {
1472 DCHECK(main_thread_checker_.CalledOnValidThread());
1473 DCHECK(remote_playback_texture_id_);
1475 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1477 if (mailbox_holder->sync_point)
1478 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point);
1479 gl->DeleteTextures(1, &remote_playback_texture_id_);
1480 remote_playback_texture_id_ = 0;
1483 void WebMediaPlayerAndroid::enterFullscreen() {
1484 if (manager_->CanEnterFullscreen(frame_)) {
1485 manager_->EnterFullscreen(player_id_, frame_);
1486 SetNeedsEstablishPeer(false);
1490 void WebMediaPlayerAndroid::exitFullscreen() {
1491 manager_->ExitFullscreen(player_id_);
1494 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1495 return manager_->CanEnterFullscreen(frame_);
1498 } // namespace content