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 "cc/layers/video_layer.h"
19 #include "content/public/common/content_client.h"
20 #include "content/public/renderer/render_frame.h"
21 #include "content/renderer/media/android/proxy_media_keys.h"
22 #include "content/renderer/media/android/renderer_demuxer_android.h"
23 #include "content/renderer/media/android/renderer_media_player_manager.h"
24 #include "content/renderer/media/crypto/key_systems.h"
25 #include "content/renderer/media/webmediaplayer_delegate.h"
26 #include "content/renderer/media/webmediaplayer_util.h"
27 #include "content/renderer/render_frame_impl.h"
28 #include "content/renderer/render_thread_impl.h"
29 #include "gpu/GLES2/gl2extchromium.h"
30 #include "gpu/command_buffer/client/gles2_interface.h"
31 #include "gpu/command_buffer/common/mailbox_holder.h"
32 #include "grit/content_resources.h"
33 #include "media/base/android/media_player_android.h"
34 #include "media/base/bind_to_current_loop.h"
35 #include "media/base/media_switches.h"
36 #include "media/base/video_frame.h"
37 #include "net/base/mime_util.h"
38 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
39 #include "third_party/WebKit/public/platform/WebString.h"
40 #include "third_party/WebKit/public/platform/WebURL.h"
41 #include "third_party/WebKit/public/web/WebDocument.h"
42 #include "third_party/WebKit/public/web/WebFrame.h"
43 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
44 #include "third_party/WebKit/public/web/WebView.h"
45 #include "third_party/skia/include/core/SkBitmap.h"
46 #include "third_party/skia/include/core/SkCanvas.h"
47 #include "third_party/skia/include/core/SkPaint.h"
48 #include "ui/gfx/image/image.h"
49 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
51 static const uint32 kGLTextureExternalOES = 0x8D65;
53 using blink::WebMediaPlayer;
55 using blink::WebString;
56 using blink::WebTimeRanges;
58 using gpu::gles2::GLES2Interface;
59 using media::MediaPlayerAndroid;
60 using media::VideoFrame;
63 // Prefix for histograms related to Encrypted Media Extensions.
64 const char* kMediaEme = "Media.EME.";
70 void WebMediaPlayerAndroid::OnReleaseRemotePlaybackTexture(
71 const scoped_refptr<base::MessageLoopProxy>& main_loop,
72 const base::WeakPtr<WebMediaPlayerAndroid>& player,
73 scoped_ptr<gpu::MailboxHolder> mailbox_holder) {
76 base::Bind(&WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture,
78 mailbox_holder->sync_point));
81 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
82 blink::WebFrame* frame,
83 blink::WebMediaPlayerClient* client,
84 base::WeakPtr<WebMediaPlayerDelegate> delegate,
85 RendererMediaPlayerManager* manager,
86 StreamTextureFactory* factory,
87 const scoped_refptr<base::MessageLoopProxy>& media_loop,
88 media::MediaLog* media_log)
89 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
94 main_loop_(base::MessageLoopProxy::current()),
95 media_loop_(media_loop),
96 ignore_metadata_duration_change_(false),
99 did_loading_progress_(false),
101 network_state_(WebMediaPlayer::NetworkStateEmpty),
102 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
103 remote_playback_texture_id_(0),
105 texture_mailbox_sync_point_(0),
108 playing_started_(false),
109 needs_establish_peer_(true),
110 stream_texture_proxy_initialized_(false),
111 has_size_info_(false),
112 has_media_metadata_(false),
113 has_media_info_(false),
114 stream_texture_factory_(factory),
115 needs_external_surface_(false),
116 video_frame_provider_client_(NULL),
117 pending_playback_(false),
118 player_type_(MEDIA_PLAYER_TYPE_URL),
121 media_log_(media_log),
122 weak_factory_(this) {
125 DCHECK(main_thread_checker_.CalledOnValidThread());
127 player_id_ = manager_->RegisterMediaPlayer(this);
129 #if defined(VIDEO_HOLE)
130 // Defer stream texture creation until we are sure it's necessary.
131 needs_establish_peer_ = false;
132 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
133 #endif // defined(VIDEO_HOLE)
134 TryCreateStreamTextureProxyIfNeeded();
137 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
138 SetVideoFrameProviderClient(NULL);
139 client_->setWebLayer(NULL);
142 manager_->DestroyPlayer(player_id_);
143 manager_->UnregisterMediaPlayer(player_id_);
147 stream_texture_factory_->DestroyStreamTexture(texture_id_);
149 if (remote_playback_texture_id_) {
150 stream_texture_factory_->ContextGL()->
151 DeleteTextures(1, &remote_playback_texture_id_);
154 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
155 delegate_->PlayerGone(this);
158 void WebMediaPlayerAndroid::load(LoadType load_type,
159 const blink::WebURL& url,
160 CORSMode cors_mode) {
163 player_type_ = MEDIA_PLAYER_TYPE_URL;
166 case LoadTypeMediaSource:
167 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
170 case LoadTypeMediaStream:
171 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
176 has_media_metadata_ = false;
177 has_media_info_ = false;
179 int demuxer_client_id = 0;
180 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
181 has_media_info_ = true;
183 RendererDemuxerAndroid* demuxer =
184 RenderThreadImpl::current()->renderer_demuxer();
185 demuxer_client_id = demuxer->GetNextDemuxerClientID();
187 media_source_delegate_.reset(new MediaSourceDelegate(
188 demuxer, demuxer_client_id, media_loop_, media_log_));
190 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
191 media::SetDecryptorReadyCB set_decryptor_ready_cb =
192 media::BindToCurrentLoop(
193 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
194 weak_factory_.GetWeakPtr()));
196 media_source_delegate_->InitializeMediaSource(
197 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
198 weak_factory_.GetWeakPtr()),
199 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
200 weak_factory_.GetWeakPtr()),
201 set_decryptor_ready_cb,
202 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
203 weak_factory_.GetWeakPtr()),
204 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
205 weak_factory_.GetWeakPtr()));
212 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
213 weak_factory_.GetWeakPtr())));
214 info_loader_->Start(frame_);
218 GURL first_party_url = frame_->document().firstPartyForCookies();
219 manager_->Initialize(
220 player_type_, player_id_, url, first_party_url, demuxer_client_id);
222 if (manager_->ShouldEnterFullscreen(frame_))
223 manager_->EnterFullscreen(player_id_, frame_);
225 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
226 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
229 void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
230 DCHECK(!media_source_delegate_);
231 if (status == MediaInfoLoader::kFailed) {
232 info_loader_.reset();
233 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
237 has_media_info_ = true;
238 if (has_media_metadata_ &&
239 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
240 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
241 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
243 // Android doesn't start fetching resources until an implementation-defined
244 // event (e.g. playback request) occurs. Sets the network state to IDLE
245 // if play is not requested yet.
246 if (!playing_started_)
247 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
250 void WebMediaPlayerAndroid::play() {
251 #if defined(VIDEO_HOLE)
252 if (hasVideo() && needs_external_surface_ &&
253 !manager_->IsInFullscreen(frame_)) {
254 DCHECK(!needs_establish_peer_);
255 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
257 #endif // defined(VIDEO_HOLE)
259 TryCreateStreamTextureProxyIfNeeded();
260 if (hasVideo() && needs_establish_peer_)
261 EstablishSurfaceTexturePeer();
264 manager_->Start(player_id_);
265 UpdatePlayingState(true);
266 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
267 playing_started_ = true;
270 void WebMediaPlayerAndroid::pause() {
274 void WebMediaPlayerAndroid::seek(double seconds) {
275 DCHECK(main_loop_->BelongsToCurrentThread());
276 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
278 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
281 if (new_seek_time == seek_time_) {
282 if (media_source_delegate_) {
283 if (!pending_seek_) {
284 // If using media source demuxer, only suppress redundant seeks if
285 // there is no pending seek. This enforces that any pending seek that
286 // results in a demuxer seek is preceded by matching
287 // CancelPendingSeek() and StartWaitingForSeek() calls.
291 // Suppress all redundant seeks if unrestricted by media source
293 pending_seek_ = false;
298 pending_seek_ = true;
299 pending_seek_time_ = new_seek_time;
301 if (media_source_delegate_)
302 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
304 // Later, OnSeekComplete will trigger the pending seek.
309 seek_time_ = new_seek_time;
311 if (media_source_delegate_)
312 media_source_delegate_->StartWaitingForSeek(seek_time_);
314 // Kick off the asynchronous seek!
315 manager_->Seek(player_id_, seek_time_);
318 bool WebMediaPlayerAndroid::supportsSave() const {
322 void WebMediaPlayerAndroid::setRate(double rate) {
326 void WebMediaPlayerAndroid::setVolume(double volume) {
327 manager_->SetVolume(player_id_, volume);
330 bool WebMediaPlayerAndroid::hasVideo() const {
331 // If we have obtained video size information before, use it.
333 return !natural_size_.isEmpty();
335 // TODO(qinmin): need a better method to determine whether the current media
336 // content contains video. Android does not provide any function to do
338 // We don't know whether the current media content has video unless
339 // the player is prepared. If the player is not prepared, we fall back
340 // to the mime-type. There may be no mime-type on a redirect URL.
341 // In that case, we conservatively assume it contains video so that
342 // enterfullscreen call will not fail.
343 if (!url_.has_path())
346 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
348 return mime.find("audio/") == std::string::npos;
351 bool WebMediaPlayerAndroid::hasAudio() const {
352 if (!url_.has_path())
355 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
358 if (mime.find("audio/") != std::string::npos ||
359 mime.find("video/") != std::string::npos ||
360 mime.find("application/ogg") != std::string::npos) {
366 bool WebMediaPlayerAndroid::paused() const {
370 bool WebMediaPlayerAndroid::seeking() const {
374 double WebMediaPlayerAndroid::duration() const {
375 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
376 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
377 return std::numeric_limits<double>::quiet_NaN();
379 if (duration_ == media::kInfiniteDuration())
380 return std::numeric_limits<double>::infinity();
382 return duration_.InSecondsF();
385 double WebMediaPlayerAndroid::currentTime() const {
386 // If the player is processing a seek, return the seek time.
387 // Blink may still query us if updatePlaybackState() occurs while seeking.
389 return pending_seek_ ?
390 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
393 return current_time_;
396 WebSize WebMediaPlayerAndroid::naturalSize() const {
397 return natural_size_;
400 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
401 return network_state_;
404 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
408 const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
409 if (media_source_delegate_)
410 return media_source_delegate_->Buffered();
414 double WebMediaPlayerAndroid::maxTimeSeekable() const {
415 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
416 // return 0 so that the seekable range is empty.
417 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
420 if (duration() == std::numeric_limits<double>::infinity())
423 return std::min(std::numeric_limits<int32>::max() / 1000.0, duration());
426 bool WebMediaPlayerAndroid::didLoadingProgress() const {
427 bool ret = did_loading_progress_;
428 did_loading_progress_ = false;
432 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
433 const blink::WebRect& rect,
434 unsigned char alpha) {
438 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
439 blink::WebGraphicsContext3D* web_graphics_context,
440 unsigned int texture,
442 unsigned int internal_format,
444 bool premultiply_alpha,
446 if (is_remote_ || !texture_id_)
449 // For hidden video element (with style "display:none"), ensure the texture
451 if (cached_stream_texture_size_.width != natural_size_.width ||
452 cached_stream_texture_size_.height != natural_size_.height) {
453 stream_texture_factory_->SetStreamTextureSize(
454 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
455 cached_stream_texture_size_ = natural_size_;
458 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
459 // an invalid texture target may be used for copy texture.
460 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
462 // The video is stored in an unmultiplied format, so premultiply if
464 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
467 // Application itself needs to take care of setting the right flip_y
468 // value down to get the expected result.
469 // flip_y==true means to reverse the video orientation while
470 // flip_y==false means to keep the intrinsic orientation.
471 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
472 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, texture_id_,
473 texture, level, internal_format,
475 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
476 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
479 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
483 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
485 return info_loader_->HasSingleOrigin();
486 // The info loader may have failed.
487 if (player_type_ == MEDIA_PLAYER_TYPE_URL)
492 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
494 return info_loader_->DidPassCORSAccessCheck();
498 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
499 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
502 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
503 if (media_source_delegate_)
504 return media_source_delegate_->DecodedFrameCount();
509 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
510 if (media_source_delegate_)
511 return media_source_delegate_->DroppedFrameCount();
516 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
517 if (media_source_delegate_)
518 return media_source_delegate_->AudioDecodedByteCount();
523 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
524 if (media_source_delegate_)
525 return media_source_delegate_->VideoDecodedByteCount();
530 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
531 const base::TimeDelta& duration, int width, int height, bool success) {
532 bool need_to_signal_duration_changed = false;
534 if (url_.SchemeIs("file") || url_.SchemeIs("app"))
535 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
537 // Update duration, if necessary, prior to ready state updates that may
538 // cause duration() query.
539 if (!ignore_metadata_duration_change_ && duration_ != duration) {
540 duration_ = duration;
542 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
543 // already triggers a durationchanged event. If this is a different
544 // transition, remember to signal durationchanged.
545 // Do not ever signal durationchanged on metadata change in MSE case
546 // because OnDurationChanged() handles this.
547 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
548 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
549 need_to_signal_duration_changed = true;
553 has_media_metadata_ = true;
554 if (has_media_info_ &&
555 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
556 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
557 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
560 // TODO(wolenetz): Should we just abort early and set network state to an
561 // error if success == false? See http://crbug.com/248399
563 OnVideoSizeChanged(width, height);
565 if (need_to_signal_duration_changed)
566 client_->durationChanged();
569 void WebMediaPlayerAndroid::OnPlaybackComplete() {
570 // When playback is about to finish, android media player often stops
571 // at a time which is smaller than the duration. This makes webkit never
572 // know that the playback has finished. To solve this, we set the
573 // current time to media duration when OnPlaybackComplete() get called.
574 OnTimeUpdate(duration_);
575 client_->timeChanged();
577 // if the loop attribute is set, timeChanged() will update the current time
578 // to 0. It will perform a seek to 0. As the requests to the renderer
579 // process are sequential, the OnSeekComplete() will only occur
580 // once OnPlaybackComplete() is done. As the playback can only be executed
581 // upon completion of OnSeekComplete(), the request needs to be saved.
583 if (seeking_ && seek_time_ == base::TimeDelta())
584 pending_playback_ = true;
587 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
588 buffered_[0].end = duration() * percentage / 100;
589 did_loading_progress_ = true;
592 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
593 DCHECK(main_loop_->BelongsToCurrentThread());
594 client_->requestSeek(time_to_seek.InSecondsF());
597 void WebMediaPlayerAndroid::OnSeekComplete(
598 const base::TimeDelta& current_time) {
599 DCHECK(main_loop_->BelongsToCurrentThread());
602 pending_seek_ = false;
603 seek(pending_seek_time_.InSecondsF());
607 OnTimeUpdate(current_time);
609 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
611 client_->timeChanged();
613 if (pending_playback_) {
615 pending_playback_ = false;
619 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
620 switch (error_type) {
621 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
622 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
624 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
625 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
627 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
628 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
630 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
636 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
637 has_size_info_ = true;
638 if (natural_size_.width == width && natural_size_.height == height)
641 #if defined(VIDEO_HOLE)
642 // Use H/W surface for encrypted video.
643 // TODO(qinmin): Change this so that only EME needs the H/W surface
644 if (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted()) {
645 needs_external_surface_ = true;
646 if (!paused() && !manager_->IsInFullscreen(frame_))
647 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
648 } else if (stream_texture_factory_ && !stream_id_) {
649 // Do deferred stream texture creation finally.
650 DoCreateStreamTexture();
652 SetNeedsEstablishPeer(true);
654 EstablishSurfaceTexturePeer();
658 // When play() gets called, |natural_size_| may still be empty and
659 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
660 // may play without a surface texture. When we finally get the valid video
661 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
662 // previously called.
663 if (!paused() && needs_establish_peer_)
664 EstablishSurfaceTexturePeer();
665 #endif // defined(VIDEO_HOLE)
667 natural_size_.width = width;
668 natural_size_.height = height;
669 ReallocateVideoFrame();
670 CreateWebLayerIfNeeded();
671 // TODO(qinmin): This is a hack. We need the media element to stop showing the
672 // poster image by forcing it to call setDisplayMode(video). Should move the
673 // logic into HTMLMediaElement.cpp.
674 client_->timeChanged();
677 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
678 DCHECK(main_loop_->BelongsToCurrentThread());
679 current_time_ = current_time.InSecondsF();
682 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice() {
683 DCHECK(main_thread_checker_.CalledOnValidThread());
684 DCHECK(!media_source_delegate_);
685 DrawRemotePlaybackIcon();
687 SetNeedsEstablishPeer(false);
690 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
691 DCHECK(main_thread_checker_.CalledOnValidThread());
692 DCHECK(!media_source_delegate_);
693 SetNeedsEstablishPeer(true);
695 EstablishSurfaceTexturePeer();
697 ReallocateVideoFrame();
700 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
701 if (!manager_->IsInFullscreen(frame_)) {
702 frame_->view()->willEnterFullScreen();
703 frame_->view()->didEnterFullScreen();
704 manager_->DidEnterFullscreen(frame_);
708 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
709 // |needs_external_surface_| is always false on non-TV devices.
710 if (!needs_external_surface_)
711 SetNeedsEstablishPeer(true);
712 // We had the fullscreen surface connected to Android MediaPlayer,
713 // so reconnect our surface texture for embedded playback.
714 if (!paused() && needs_establish_peer_)
715 EstablishSurfaceTexturePeer();
717 #if defined(VIDEO_HOLE)
718 if (!paused() && needs_external_surface_)
719 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
720 #endif // defined(VIDEO_HOLE)
722 frame_->view()->willExitFullScreen();
723 frame_->view()->didExitFullScreen();
724 manager_->DidExitFullscreen();
728 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
729 UpdatePlayingState(true);
730 client_->playbackStateChanged();
733 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
734 UpdatePlayingState(false);
735 client_->playbackStateChanged();
738 void WebMediaPlayerAndroid::OnRequestFullscreen() {
739 client_->requestFullscreen();
742 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
743 DCHECK(main_loop_->BelongsToCurrentThread());
744 // Only MSE |player_type_| registers this callback.
745 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
747 // Cache the new duration value and trust it over any subsequent duration
748 // values received in OnMediaMetadataChanged().
749 duration_ = duration;
750 ignore_metadata_duration_change_ = true;
752 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
753 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
754 client_->durationChanged();
757 void WebMediaPlayerAndroid::UpdateNetworkState(
758 WebMediaPlayer::NetworkState state) {
759 DCHECK(main_loop_->BelongsToCurrentThread());
760 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
761 (state == WebMediaPlayer::NetworkStateNetworkError ||
762 state == WebMediaPlayer::NetworkStateDecodeError)) {
763 // Any error that occurs before reaching ReadyStateHaveMetadata should
764 // be considered a format error.
765 network_state_ = WebMediaPlayer::NetworkStateFormatError;
767 network_state_ = state;
769 client_->networkStateChanged();
772 void WebMediaPlayerAndroid::UpdateReadyState(
773 WebMediaPlayer::ReadyState state) {
774 ready_state_ = state;
775 client_->readyStateChanged();
778 void WebMediaPlayerAndroid::OnPlayerReleased() {
779 // |needs_external_surface_| is always false on non-TV devices.
780 if (!needs_external_surface_)
781 needs_establish_peer_ = true;
784 OnMediaPlayerPause();
786 #if defined(VIDEO_HOLE)
787 last_computed_rect_ = gfx::RectF();
788 #endif // defined(VIDEO_HOLE)
791 void WebMediaPlayerAndroid::ReleaseMediaResources() {
792 switch (network_state_) {
793 // Pause the media player and inform WebKit if the player is in a good
795 case WebMediaPlayer::NetworkStateIdle:
796 case WebMediaPlayer::NetworkStateLoading:
797 case WebMediaPlayer::NetworkStateLoaded:
799 client_->playbackStateChanged();
801 // If a WebMediaPlayer instance has entered into one of these states,
802 // the internal network state in HTMLMediaElement could be set to empty.
803 // And calling playbackStateChanged() could get this object deleted.
804 case WebMediaPlayer::NetworkStateEmpty:
805 case WebMediaPlayer::NetworkStateFormatError:
806 case WebMediaPlayer::NetworkStateNetworkError:
807 case WebMediaPlayer::NetworkStateDecodeError:
810 manager_->ReleaseResources(player_id_);
814 void WebMediaPlayerAndroid::OnDestruct() {
816 manager_->UnregisterMediaPlayer(player_id_);
820 void WebMediaPlayerAndroid::Detach() {
822 stream_texture_factory_->DestroyStreamTexture(texture_id_);
826 media_source_delegate_.reset();
828 base::AutoLock auto_lock(current_frame_lock_);
829 current_frame_ = NULL;
835 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
836 manager_->Pause(player_id_, is_media_related_action);
837 UpdatePlayingState(false);
840 void WebMediaPlayerAndroid::DrawRemotePlaybackIcon() {
841 DCHECK(main_thread_checker_.CalledOnValidThread());
842 if (!video_weblayer_)
845 // TODO(johnme): Should redraw this frame if the layer bounds change; but
846 // there seems no easy way to listen for the layer resizing (as opposed to
847 // OnVideoSizeChanged, which is when the frame sizes of the video file
848 // change). Perhaps have to poll (on main thread of course)?
849 gfx::Size video_size_css_px = video_weblayer_->bounds();
850 float device_scale_factor = frame_->view()->deviceScaleFactor();
851 // canvas_size will be the size in device pixels when pageScaleFactor == 1
852 gfx::Size canvas_size(
853 static_cast<int>(video_size_css_px.width() * device_scale_factor),
854 static_cast<int>(video_size_css_px.height() * device_scale_factor));
858 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
859 bitmap.allocPixels();
861 SkCanvas canvas(bitmap);
862 canvas.drawColor(SK_ColorBLACK);
864 paint.setAntiAlias(true);
865 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
866 const SkBitmap* icon_bitmap =
868 ->GetNativeImageNamed(IDR_MEDIAPLAYER_REMOTE_PLAYBACK_ICON)
870 // In order to get a reasonable margin around the icon:
871 // - the icon should be under half the frame width
872 // - the icon should be at most 3/5 of the frame height
873 // Additionally, on very large screens, the icon size should be capped. A max
874 // width of 320 was arbitrarily chosen; since this is half the resource's
875 // pixel width, it should look crisp even on 2x deviceScaleFactor displays.
876 int icon_width = 320;
877 icon_width = std::min(icon_width, canvas_size.width() / 2);
878 icon_width = std::min(icon_width,
879 canvas_size.height() * icon_bitmap->width() /
880 icon_bitmap->height() * 3 / 5);
881 int icon_height = icon_width * icon_bitmap->height() / icon_bitmap->width();
882 // Center the icon within the frame
883 SkRect icon_rect = SkRect::MakeXYWH((canvas_size.width() - icon_width) / 2,
884 (canvas_size.height() - icon_height) / 2,
887 canvas.drawBitmapRectToRect(
888 *icon_bitmap, NULL /* src */, icon_rect /* dest */, &paint);
890 GLES2Interface* gl = stream_texture_factory_->ContextGL();
892 if (!remote_playback_texture_id_)
893 gl->GenTextures(1, &remote_playback_texture_id_);
894 GLuint texture_target = GL_TEXTURE_2D;
895 gl->BindTexture(texture_target, remote_playback_texture_id_);
896 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
897 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
898 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
899 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
902 SkAutoLockPixels lock(bitmap);
903 gl->TexImage2D(texture_target,
905 GL_RGBA /* internalformat */,
909 GL_RGBA /* format */,
910 GL_UNSIGNED_BYTE /* type */,
914 gpu::Mailbox texture_mailbox;
915 gl->GenMailboxCHROMIUM(texture_mailbox.name);
916 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
918 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
920 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
921 make_scoped_ptr(new gpu::MailboxHolder(
922 texture_mailbox, texture_target, texture_mailbox_sync_point)),
923 base::Bind(&WebMediaPlayerAndroid::OnReleaseRemotePlaybackTexture,
925 weak_factory_.GetWeakPtr()),
926 canvas_size /* coded_size */,
927 gfx::Rect(canvas_size) /* visible_rect */,
928 canvas_size /* natural_size */,
929 base::TimeDelta() /* timestamp */,
930 VideoFrame::ReadPixelsCB());
931 SetCurrentFrameInternal(new_frame);
934 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
935 if (needs_external_surface_) {
936 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
937 #if defined(VIDEO_HOLE)
938 if (!natural_size_.isEmpty()) {
939 scoped_refptr<VideoFrame> new_frame =
940 VideoFrame::CreateHoleFrame(natural_size_);
941 SetCurrentFrameInternal(new_frame);
942 // Force the client to grab the hole frame.
946 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
947 #endif // defined(VIDEO_HOLE)
948 } else if (!is_remote_ && texture_id_) {
949 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
950 make_scoped_ptr(new gpu::MailboxHolder(texture_mailbox_,
951 kGLTextureExternalOES,
952 texture_mailbox_sync_point_)),
953 media::VideoFrame::ReleaseMailboxCB(),
955 gfx::Rect(natural_size_),
958 VideoFrame::ReadPixelsCB());
959 SetCurrentFrameInternal(new_frame);
963 void WebMediaPlayerAndroid::CreateWebLayerIfNeeded() {
964 if (!hasVideo() || video_weblayer_ || !client_->needsWebLayerForVideo())
966 video_weblayer_.reset(new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
967 client_->setWebLayer(video_weblayer_.get());
970 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
971 cc::VideoFrameProvider::Client* client) {
972 // This is called from both the main renderer thread and the compositor
973 // thread (when the main thread is blocked).
974 if (video_frame_provider_client_)
975 video_frame_provider_client_->StopUsingProvider();
976 video_frame_provider_client_ = client;
978 // Set the callback target when a frame is produced.
979 if (stream_texture_proxy_)
980 stream_texture_proxy_->SetClient(client);
983 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
984 scoped_refptr<media::VideoFrame>& video_frame) {
985 base::AutoLock auto_lock(current_frame_lock_);
986 current_frame_ = video_frame;
989 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
990 scoped_refptr<VideoFrame> video_frame;
992 base::AutoLock auto_lock(current_frame_lock_);
993 video_frame = current_frame_;
996 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
997 stream_id_ && !needs_external_surface_ && !is_remote_) {
998 gfx::Size natural_size = video_frame->natural_size();
999 // TODO(sievers): These variables are accessed on the wrong thread here.
1000 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1001 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1002 stream_texture_proxy_initialized_ = true;
1003 cached_stream_texture_size_ = natural_size;
1009 void WebMediaPlayerAndroid::PutCurrentFrame(
1010 const scoped_refptr<media::VideoFrame>& frame) {
1013 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1015 if (stream_texture_proxy_)
1018 // No factory to create proxy.
1019 if (!stream_texture_factory_)
1022 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1023 if (needs_establish_peer_ && stream_texture_proxy_) {
1024 DoCreateStreamTexture();
1025 ReallocateVideoFrame();
1028 if (stream_texture_proxy_ && video_frame_provider_client_)
1029 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1032 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1033 if (!stream_texture_proxy_)
1036 if (stream_texture_factory_.get() && stream_id_)
1037 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1038 needs_establish_peer_ = false;
1041 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1042 DCHECK(!stream_id_);
1043 DCHECK(!texture_id_);
1044 DCHECK(!texture_mailbox_sync_point_);
1045 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1046 kGLTextureExternalOES,
1049 &texture_mailbox_sync_point_);
1052 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1053 needs_establish_peer_ = needs_establish_peer;
1056 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1057 manager_->SetPoster(player_id_, poster);
1060 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1061 is_playing_ = is_playing;
1065 delegate_->DidPlay(this);
1067 delegate_->DidPause(this);
1070 #if defined(VIDEO_HOLE)
1071 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1072 if (!video_weblayer_)
1075 // Compute the geometry of video frame layer.
1076 cc::Layer* layer = video_weblayer_->layer();
1077 gfx::RectF rect(layer->bounds());
1079 rect.Offset(layer->position().OffsetFromOrigin());
1080 layer = layer->parent();
1083 // Return false when the geometry hasn't been changed from the last time.
1084 if (last_computed_rect_ == rect)
1087 // Store the changed geometry information when it is actually changed.
1088 last_computed_rect_ = rect;
1092 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1093 return last_computed_rect_;
1097 // The following EME related code is copied from WebMediaPlayerImpl.
1098 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1099 // WebMediaPlayerImpl.
1100 // TODO(kjyoun): Update Google TV EME implementation to use IPC.
1102 // Helper functions to report media EME related stats to UMA. They follow the
1103 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1104 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1105 // that UMA_* macros require the names to be constant throughout the process'
1107 static void EmeUMAHistogramEnumeration(const blink::WebString& key_system,
1108 const std::string& method,
1110 int boundary_value) {
1111 base::LinearHistogram::FactoryGet(
1112 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1113 1, boundary_value, boundary_value + 1,
1114 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1117 static void EmeUMAHistogramCounts(const blink::WebString& key_system,
1118 const std::string& method,
1120 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1121 base::Histogram::FactoryGet(
1122 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1123 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1126 // Helper enum for reporting generateKeyRequest/addKey histograms.
1127 enum MediaKeyException {
1130 kKeySystemNotSupported,
1131 kInvalidPlayerState,
1132 kMaxMediaKeyException
1135 static MediaKeyException MediaKeyExceptionForUMA(
1136 WebMediaPlayer::MediaKeyException e) {
1138 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1139 return kKeySystemNotSupported;
1140 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1141 return kInvalidPlayerState;
1142 case WebMediaPlayer::MediaKeyExceptionNoError:
1145 return kUnknownResultId;
1149 // Helper for converting |key_system| name and exception |e| to a pair of enum
1150 // values from above, for reporting to UMA.
1151 static void ReportMediaKeyExceptionToUMA(
1152 const std::string& method,
1153 const WebString& key_system,
1154 WebMediaPlayer::MediaKeyException e) {
1155 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1156 DCHECK_NE(result_id, kUnknownResultId) << e;
1157 EmeUMAHistogramEnumeration(
1158 key_system, method, result_id, kMaxMediaKeyException);
1161 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1162 const WebString& key_system,
1163 const unsigned char* init_data,
1164 unsigned init_data_length) {
1165 WebMediaPlayer::MediaKeyException e =
1166 GenerateKeyRequestInternal(key_system, init_data, init_data_length);
1167 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e);
1171 bool WebMediaPlayerAndroid::IsKeySystemSupported(const WebString& key_system) {
1172 // On Android, EME only works with MSE.
1173 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1174 IsConcreteSupportedKeySystem(key_system);
1177 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1178 // not enabled. Currently the player just doesn't start and waits for
1180 WebMediaPlayer::MediaKeyException
1181 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1182 const WebString& key_system,
1183 const unsigned char* init_data,
1184 unsigned init_data_length) {
1185 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": "
1186 << std::string(reinterpret_cast<const char*>(init_data),
1187 static_cast<size_t>(init_data_length));
1189 if (!IsKeySystemSupported(key_system))
1190 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1192 // We do not support run-time switching between key systems for now.
1193 if (current_key_system_.isEmpty()) {
1194 if (!proxy_decryptor_) {
1195 proxy_decryptor_.reset(new ProxyDecryptor(
1196 #if defined(ENABLE_PEPPER_CDMS)
1201 player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are
1202 // separated from WebMediaPlayer.
1203 #endif // defined(ENABLE_PEPPER_CDMS)
1204 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1205 weak_factory_.GetWeakPtr()),
1206 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1207 weak_factory_.GetWeakPtr()),
1208 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1209 weak_factory_.GetWeakPtr())));
1212 if (!proxy_decryptor_->InitializeCDM(key_system.utf8(),
1213 frame_->document().url())) {
1214 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1217 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
1218 base::ResetAndReturn(&decryptor_ready_cb_)
1219 .Run(proxy_decryptor_->GetDecryptor());
1222 current_key_system_ = key_system;
1223 } else if (key_system != current_key_system_) {
1224 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1227 // TODO(xhwang): We assume all streams are from the same container (thus have
1228 // the same "type") for now. In the future, the "type" should be passed down
1229 // from the application.
1230 if (!proxy_decryptor_->GenerateKeyRequest(
1231 init_data_type_, init_data, init_data_length)) {
1232 current_key_system_.reset();
1233 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1236 return WebMediaPlayer::MediaKeyExceptionNoError;
1239 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1240 const WebString& key_system,
1241 const unsigned char* key,
1242 unsigned key_length,
1243 const unsigned char* init_data,
1244 unsigned init_data_length,
1245 const WebString& session_id) {
1246 WebMediaPlayer::MediaKeyException e = AddKeyInternal(
1247 key_system, key, key_length, init_data, init_data_length, session_id);
1248 ReportMediaKeyExceptionToUMA("addKey", key_system, e);
1252 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1253 const WebString& key_system,
1254 const unsigned char* key,
1255 unsigned key_length,
1256 const unsigned char* init_data,
1257 unsigned init_data_length,
1258 const WebString& session_id) {
1260 DCHECK_GT(key_length, 0u);
1261 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": "
1262 << std::string(reinterpret_cast<const char*>(key),
1263 static_cast<size_t>(key_length)) << ", "
1264 << std::string(reinterpret_cast<const char*>(init_data),
1265 static_cast<size_t>(init_data_length))
1266 << " [" << session_id.utf8().data() << "]";
1268 if (!IsKeySystemSupported(key_system))
1269 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1271 if (current_key_system_.isEmpty() || key_system != current_key_system_)
1272 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1274 proxy_decryptor_->AddKey(
1275 key, key_length, init_data, init_data_length, session_id.utf8());
1276 return WebMediaPlayer::MediaKeyExceptionNoError;
1279 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1280 const WebString& key_system,
1281 const WebString& session_id) {
1282 WebMediaPlayer::MediaKeyException e =
1283 CancelKeyRequestInternal(key_system, session_id);
1284 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e);
1288 WebMediaPlayer::MediaKeyException
1289 WebMediaPlayerAndroid::CancelKeyRequestInternal(
1290 const WebString& key_system,
1291 const WebString& session_id) {
1292 if (!IsKeySystemSupported(key_system))
1293 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1295 if (current_key_system_.isEmpty() || key_system != current_key_system_)
1296 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1298 proxy_decryptor_->CancelKeyRequest(session_id.utf8());
1299 return WebMediaPlayer::MediaKeyExceptionNoError;
1302 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1303 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1305 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id));
1308 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1309 media::MediaKeys::KeyError error_code,
1311 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1312 error_code, media::MediaKeys::kMaxKeyError);
1315 current_key_system_,
1316 WebString::fromUTF8(session_id),
1317 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1321 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1322 const std::vector<uint8>& message,
1323 const std::string& destination_url) {
1324 const GURL destination_url_gurl(destination_url);
1325 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid())
1326 << "Invalid URL in destination_url: " << destination_url;
1328 client_->keyMessage(current_key_system_,
1329 WebString::fromUTF8(session_id),
1330 message.empty() ? NULL : &message[0],
1332 destination_url_gurl);
1335 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1336 blink::WebMediaSource* web_media_source) {
1337 client_->mediaSourceOpened(web_media_source);
1340 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1341 const std::vector<uint8>& init_data) {
1342 DCHECK(main_loop_->BelongsToCurrentThread());
1344 // Do not fire NeedKey event if encrypted media is not enabled.
1345 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1346 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1350 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1352 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1353 if (init_data_type_.empty())
1354 init_data_type_ = type;
1356 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1358 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1361 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1362 const media::DecryptorReadyCB& decryptor_ready_cb) {
1363 DCHECK(main_loop_->BelongsToCurrentThread());
1365 // Cancels the previous decryptor request.
1366 if (decryptor_ready_cb.is_null()) {
1367 if (!decryptor_ready_cb_.is_null())
1368 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1372 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1373 // video and audio). The current implementation is okay for the current
1374 // media pipeline since we initialize audio and video decoders in sequence.
1375 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1377 DCHECK(decryptor_ready_cb_.is_null());
1379 if (proxy_decryptor_) {
1380 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1384 // TODO(xhwang): Also notify |web_cdm_| when we implement
1385 // setContentDecryptionModule(). See: http://crbug.com/224786
1387 decryptor_ready_cb_ = decryptor_ready_cb;
1390 void WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture(uint32 sync_point) {
1391 DCHECK(main_thread_checker_.CalledOnValidThread());
1392 DCHECK(remote_playback_texture_id_);
1394 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1397 gl->WaitSyncPointCHROMIUM(sync_point);
1398 gl->DeleteTextures(1, &remote_playback_texture_id_);
1399 remote_playback_texture_id_ = 0;
1402 void WebMediaPlayerAndroid::enterFullscreen() {
1403 if (manager_->CanEnterFullscreen(frame_)) {
1404 manager_->EnterFullscreen(player_id_, frame_);
1405 SetNeedsEstablishPeer(false);
1409 void WebMediaPlayerAndroid::exitFullscreen() {
1410 manager_->ExitFullscreen(player_id_);
1413 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1414 return manager_->CanEnterFullscreen(frame_);
1417 } // namespace content