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/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "cc/layers/video_layer.h"
16 #include "content/public/common/content_client.h"
17 #include "content/renderer/media/android/proxy_media_keys.h"
18 #include "content/renderer/media/android/renderer_demuxer_android.h"
19 #include "content/renderer/media/android/renderer_media_player_manager.h"
20 #include "content/renderer/media/crypto/key_systems.h"
21 #include "content/renderer/media/webmediaplayer_delegate.h"
22 #include "content/renderer/media/webmediaplayer_util.h"
23 #include "content/renderer/render_thread_impl.h"
24 #include "gpu/GLES2/gl2extchromium.h"
25 #include "grit/content_resources.h"
26 #include "media/base/android/media_player_android.h"
27 #include "media/base/bind_to_loop.h"
28 #include "media/base/media_switches.h"
29 #include "media/base/video_frame.h"
30 #include "net/base/mime_util.h"
31 #include "third_party/WebKit/public/platform/WebString.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebFrame.h"
34 #include "third_party/WebKit/public/web/WebMediaPlayerClient.h"
35 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
36 #include "third_party/WebKit/public/web/WebView.h"
37 #include "third_party/skia/include/core/SkBitmap.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "third_party/skia/include/core/SkPaint.h"
40 #include "ui/gfx/image/image.h"
41 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
43 #if defined(GOOGLE_TV)
44 #include "content/renderer/media/media_stream_audio_renderer.h"
45 #include "content/renderer/media/media_stream_client.h"
48 static const uint32 kGLTextureExternalOES = 0x8D65;
50 using WebKit::WebMediaPlayer;
51 using WebKit::WebSize;
52 using WebKit::WebString;
53 using WebKit::WebTimeRanges;
55 using media::MediaPlayerAndroid;
56 using media::VideoFrame;
59 // Prefix for histograms related to Encrypted Media Extensions.
60 const char* kMediaEme = "Media.EME.";
66 void WebMediaPlayerAndroid::OnReleaseRemotePlaybackTexture(
67 const scoped_refptr<base::MessageLoopProxy>& main_loop,
68 const base::WeakPtr<WebMediaPlayerAndroid>& player,
72 base::Bind(&WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture,
77 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
78 WebKit::WebFrame* frame,
79 WebKit::WebMediaPlayerClient* client,
80 base::WeakPtr<WebMediaPlayerDelegate> delegate,
81 RendererMediaPlayerManager* manager,
82 StreamTextureFactory* factory,
83 const scoped_refptr<base::MessageLoopProxy>& media_loop,
84 media::MediaLog* media_log)
89 main_loop_(base::MessageLoopProxy::current()),
90 media_loop_(media_loop),
91 ignore_metadata_duration_change_(false),
94 did_loading_progress_(false),
96 network_state_(WebMediaPlayer::NetworkStateEmpty),
97 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
98 remote_playback_texture_id_(0),
100 texture_mailbox_sync_point_(0),
103 playing_started_(false),
104 needs_establish_peer_(true),
105 stream_texture_proxy_initialized_(false),
106 has_size_info_(false),
107 has_media_metadata_(false),
108 has_media_info_(false),
109 stream_texture_factory_(factory),
110 needs_external_surface_(false),
111 video_frame_provider_client_(NULL),
112 #if defined(GOOGLE_TV)
113 external_surface_threshold_(-1),
115 media_stream_client_(NULL),
116 #endif // defined(GOOGLE_TV)
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 // We want to be notified of |main_loop_| destruction.
128 base::MessageLoop::current()->AddDestructionObserver(this);
130 player_id_ = manager_->RegisterMediaPlayer(this);
132 #if defined(GOOGLE_TV)
133 if (CommandLine::ForCurrentProcess()->HasSwitch(
134 switches::kUseExternalVideoSurfaceThresholdInPixels)) {
135 if (!base::StringToInt(
136 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
137 switches::kUseExternalVideoSurfaceThresholdInPixels),
138 &external_surface_threshold_)) {
139 external_surface_threshold_ = -1;
143 // Defer stream texture creation until we are sure it's necessary.
145 needs_establish_peer_ = false;
146 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
148 TryCreateStreamTextureProxyIfNeeded();
150 if (WebKit::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled()) {
151 // TODO(xhwang): Report an error when there is encrypted stream but EME is
152 // not enabled. Currently the player just doesn't start and waits for ever.
153 decryptor_.reset(new ProxyDecryptor(
154 #if defined(ENABLE_PEPPER_CDMS)
159 player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are
160 // separated from WebMediaPlayer.
161 #endif // defined(ENABLE_PEPPER_CDMS)
162 // |decryptor_| is owned, so Unretained() is safe here.
163 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, base::Unretained(this)),
164 base::Bind(&WebMediaPlayerAndroid::OnKeyError, base::Unretained(this)),
165 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
166 base::Unretained(this))));
170 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
171 SetVideoFrameProviderClient(NULL);
172 client_->setWebLayer(NULL);
175 manager_->DestroyPlayer(player_id_);
176 manager_->UnregisterMediaPlayer(player_id_);
180 stream_texture_factory_->DestroyStreamTexture(texture_id_);
182 if (remote_playback_texture_id_) {
183 WebKit::WebGraphicsContext3D* context =
184 stream_texture_factory_->Context3d();
185 if (context->makeContextCurrent())
186 context->deleteTexture(remote_playback_texture_id_);
189 if (base::MessageLoop::current())
190 base::MessageLoop::current()->RemoveDestructionObserver(this);
192 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
193 delegate_->PlayerGone(this);
195 #if defined(GOOGLE_TV)
196 if (audio_renderer_) {
197 if (audio_renderer_->IsLocalRenderer()) {
198 audio_renderer_->Stop();
199 } else if (!paused()) {
200 // The |audio_renderer_| can be shared by multiple remote streams, and
201 // it will be stopped when WebRtcAudioDeviceImpl goes away. So we simply
202 // pause the |audio_renderer_| here to avoid re-creating the
203 // |audio_renderer_|.
204 audio_renderer_->Pause();
207 if (demuxer_ && !destroy_demuxer_cb_.is_null()) {
208 media_source_delegate_.reset();
209 destroy_demuxer_cb_.Run();
214 void WebMediaPlayerAndroid::load(LoadType load_type,
215 const WebKit::WebURL& url,
216 CORSMode cors_mode) {
219 player_type_ = MEDIA_PLAYER_TYPE_URL;
222 case LoadTypeMediaSource:
223 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
226 case LoadTypeMediaStream:
227 #if defined(GOOGLE_TV)
228 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_STREAM;
231 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
237 has_media_metadata_ = false;
238 has_media_info_ = false;
240 media::SetDecryptorReadyCB set_decryptor_ready_cb;
241 if (decryptor_) { // |decryptor_| can be NULL is EME if not enabled.
242 set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB,
243 base::Unretained(decryptor_.get()));
246 int demuxer_client_id = 0;
247 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
248 has_media_info_ = true;
250 RendererDemuxerAndroid* demuxer =
251 RenderThreadImpl::current()->renderer_demuxer();
252 demuxer_client_id = demuxer->GetNextDemuxerClientID();
254 media_source_delegate_.reset(new MediaSourceDelegate(
255 demuxer, demuxer_client_id, media_loop_, media_log_));
257 // |media_source_delegate_| is owned, so Unretained() is safe here.
258 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
259 media_source_delegate_->InitializeMediaSource(
260 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
261 weak_factory_.GetWeakPtr()),
262 base::Bind(&WebMediaPlayerAndroid::OnNeedKey, base::Unretained(this)),
263 set_decryptor_ready_cb,
264 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
265 weak_factory_.GetWeakPtr()),
266 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
267 weak_factory_.GetWeakPtr()));
269 #if defined(GOOGLE_TV)
270 // TODO(xhwang): Pass set_decryptor_ready_cb in InitializeMediaStream() to
271 // enable ClearKey support for Google TV.
272 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_STREAM) {
273 media_source_delegate_->InitializeMediaStream(
275 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
276 weak_factory_.GetWeakPtr()));
277 audio_renderer_ = media_stream_client_->GetAudioRenderer(url);
279 audio_renderer_->Start();
287 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
288 base::Unretained(this))));
289 info_loader_->Start(frame_);
293 GURL first_party_url = frame_->document().firstPartyForCookies();
294 manager_->Initialize(
295 player_type_, player_id_, url, first_party_url, demuxer_client_id);
297 if (manager_->IsInFullscreen(frame_))
298 manager_->EnterFullscreen(player_id_);
300 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
301 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
304 void WebMediaPlayerAndroid::DidLoadMediaInfo(
305 MediaInfoLoader::Status status) {
306 DCHECK(!media_source_delegate_);
307 if (status == MediaInfoLoader::kFailed) {
308 info_loader_.reset();
309 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
313 has_media_info_ = true;
314 if (has_media_metadata_ &&
315 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
316 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
317 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
319 // Android doesn't start fetching resources until an implementation-defined
320 // event (e.g. playback request) occurs. Sets the network state to IDLE
321 // if play is not requested yet.
322 if (!playing_started_)
323 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
326 void WebMediaPlayerAndroid::play() {
327 #if defined(GOOGLE_TV)
328 if (hasVideo() && needs_external_surface_ &&
329 !manager_->IsInFullscreen(frame_)) {
330 DCHECK(!needs_establish_peer_);
331 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
333 if (audio_renderer_ && paused())
334 audio_renderer_->Play();
337 TryCreateStreamTextureProxyIfNeeded();
338 if (hasVideo() && needs_establish_peer_)
339 EstablishSurfaceTexturePeer();
342 manager_->Start(player_id_);
343 UpdatePlayingState(true);
344 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
345 playing_started_ = true;
348 void WebMediaPlayerAndroid::pause() {
352 void WebMediaPlayerAndroid::pause(bool is_media_related_action) {
353 #if defined(GOOGLE_TV)
354 if (audio_renderer_ && !paused())
355 audio_renderer_->Pause();
357 manager_->Pause(player_id_, is_media_related_action);
358 UpdatePlayingState(false);
361 void WebMediaPlayerAndroid::seek(double seconds) {
362 DCHECK(main_loop_->BelongsToCurrentThread());
363 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
365 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
368 if (new_seek_time == seek_time_) {
369 if (media_source_delegate_) {
370 if (!pending_seek_) {
371 // If using media source demuxer, only suppress redundant seeks if
372 // there is no pending seek. This enforces that any pending seek that
373 // results in a demuxer seek is preceded by matching
374 // CancelPendingSeek() and StartWaitingForSeek() calls.
378 // Suppress all redundant seeks if unrestricted by media source
380 pending_seek_ = false;
385 pending_seek_ = true;
386 pending_seek_time_ = new_seek_time;
388 if (media_source_delegate_)
389 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
391 // Later, OnSeekComplete will trigger the pending seek.
396 seek_time_ = new_seek_time;
398 if (media_source_delegate_)
399 media_source_delegate_->StartWaitingForSeek(seek_time_);
401 // Kick off the asynchronous seek!
402 manager_->Seek(player_id_, seek_time_);
405 bool WebMediaPlayerAndroid::supportsFullscreen() const {
409 bool WebMediaPlayerAndroid::supportsSave() const {
413 void WebMediaPlayerAndroid::setRate(double rate) {
417 void WebMediaPlayerAndroid::setVolume(double volume) {
418 manager_->SetVolume(player_id_, volume);
421 bool WebMediaPlayerAndroid::hasVideo() const {
422 // If we have obtained video size information before, use it.
423 if (has_size_info_ && !natural_size_.isEmpty())
426 // TODO(qinmin): need a better method to determine whether the current media
427 // content contains video. Android does not provide any function to do
429 // We don't know whether the current media content has video unless
430 // the player is prepared. If the player is not prepared, we fall back
431 // to the mime-type. There may be no mime-type on a redirect URL.
432 // In that case, we conservatively assume it contains video so that
433 // enterfullscreen call will not fail.
434 if (!url_.has_path())
437 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
439 return mime.find("audio/") == std::string::npos;
442 bool WebMediaPlayerAndroid::hasAudio() const {
443 // TODO(hclam): Query status of audio and return the actual value.
447 bool WebMediaPlayerAndroid::paused() const {
451 bool WebMediaPlayerAndroid::seeking() const {
455 double WebMediaPlayerAndroid::duration() const {
456 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
457 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
458 return std::numeric_limits<double>::quiet_NaN();
460 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer
461 // considers unseekable, including kInfiniteDuration().
462 // See http://crbug.com/248396
463 return duration_.InSecondsF();
466 double WebMediaPlayerAndroid::currentTime() const {
467 // If the player is processing a seek, return the seek time.
468 // Blink may still query us if updatePlaybackState() occurs while seeking.
470 return pending_seek_ ?
471 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
474 return current_time_;
477 WebSize WebMediaPlayerAndroid::naturalSize() const {
478 return natural_size_;
481 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
482 return network_state_;
485 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
489 const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
490 if (media_source_delegate_)
491 return media_source_delegate_->Buffered();
495 double WebMediaPlayerAndroid::maxTimeSeekable() const {
496 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
497 // return 0 so that the seekable range is empty.
498 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
501 // TODO(hclam): If this stream is not seekable this should return 0.
505 bool WebMediaPlayerAndroid::didLoadingProgress() const {
506 bool ret = did_loading_progress_;
507 did_loading_progress_ = false;
511 void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas,
512 const WebKit::WebRect& rect,
513 unsigned char alpha) {
517 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
518 WebKit::WebGraphicsContext3D* web_graphics_context,
519 unsigned int texture,
521 unsigned int internal_format,
523 bool premultiply_alpha,
525 if (is_remote_ || !texture_id_)
528 // For hidden video element (with style "display:none"), ensure the texture
530 if (cached_stream_texture_size_.width != natural_size_.width ||
531 cached_stream_texture_size_.height != natural_size_.height) {
532 stream_texture_factory_->SetStreamTextureSize(
533 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
534 cached_stream_texture_size_ = natural_size_;
537 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
538 // an invalid texture target may be used for copy texture.
539 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
541 // The video is stored in an unmultiplied format, so premultiply if
543 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
546 // Application itself needs to take care of setting the right flip_y
547 // value down to get the expected result.
548 // flip_y==true means to reverse the video orientation while
549 // flip_y==false means to keep the intrinsic orientation.
550 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
551 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, texture_id_,
552 texture, level, internal_format,
554 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
555 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
558 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
562 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
564 return info_loader_->HasSingleOrigin();
565 // The info loader may have failed.
566 if (player_type_ == MEDIA_PLAYER_TYPE_URL)
571 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
573 return info_loader_->DidPassCORSAccessCheck();
577 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
578 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
581 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
582 if (media_source_delegate_)
583 return media_source_delegate_->DecodedFrameCount();
588 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
589 if (media_source_delegate_)
590 return media_source_delegate_->DroppedFrameCount();
595 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
596 if (media_source_delegate_)
597 return media_source_delegate_->AudioDecodedByteCount();
602 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
603 if (media_source_delegate_)
604 return media_source_delegate_->VideoDecodedByteCount();
609 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
610 const base::TimeDelta& duration, int width, int height, bool success) {
611 bool need_to_signal_duration_changed = false;
613 if (url_.SchemeIs("file"))
614 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
616 // Update duration, if necessary, prior to ready state updates that may
617 // cause duration() query.
618 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer
619 // considers unseekable, including kInfiniteDuration().
620 // See http://crbug.com/248396
621 if (!ignore_metadata_duration_change_ && duration_ != duration) {
622 duration_ = duration;
624 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
625 // already triggers a durationchanged event. If this is a different
626 // transition, remember to signal durationchanged.
627 // Do not ever signal durationchanged on metadata change in MSE case
628 // because OnDurationChanged() handles this.
629 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
630 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
631 need_to_signal_duration_changed = true;
635 has_media_metadata_ = true;
636 if (has_media_info_ &&
637 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
638 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
639 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
642 // TODO(wolenetz): Should we just abort early and set network state to an
643 // error if success == false? See http://crbug.com/248399
645 OnVideoSizeChanged(width, height);
647 if (hasVideo() && !video_weblayer_ && client_->needsWebLayerForVideo()) {
648 video_weblayer_.reset(
649 new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
650 client_->setWebLayer(video_weblayer_.get());
653 if (need_to_signal_duration_changed)
654 client_->durationChanged();
657 void WebMediaPlayerAndroid::OnPlaybackComplete() {
658 // When playback is about to finish, android media player often stops
659 // at a time which is smaller than the duration. This makes webkit never
660 // know that the playback has finished. To solve this, we set the
661 // current time to media duration when OnPlaybackComplete() get called.
662 OnTimeUpdate(duration_);
663 client_->timeChanged();
665 // if the loop attribute is set, timeChanged() will update the current time
666 // to 0. It will perform a seek to 0. As the requests to the renderer
667 // process are sequential, the OnSeekComplete() will only occur
668 // once OnPlaybackComplete() is done. As the playback can only be executed
669 // upon completion of OnSeekComplete(), the request needs to be saved.
671 if (seeking_ && seek_time_ == base::TimeDelta())
672 pending_playback_ = true;
675 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
676 buffered_[0].end = duration() * percentage / 100;
677 did_loading_progress_ = true;
680 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
681 DCHECK(main_loop_->BelongsToCurrentThread());
682 client_->requestSeek(time_to_seek.InSecondsF());
685 void WebMediaPlayerAndroid::OnSeekComplete(
686 const base::TimeDelta& current_time) {
687 DCHECK(main_loop_->BelongsToCurrentThread());
690 pending_seek_ = false;
691 seek(pending_seek_time_.InSecondsF());
695 OnTimeUpdate(current_time);
697 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
699 client_->timeChanged();
701 if (pending_playback_) {
703 pending_playback_ = false;
707 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
708 switch (error_type) {
709 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
710 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
712 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
713 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
715 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
716 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
718 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
724 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
725 has_size_info_ = true;
726 if (natural_size_.width == width && natural_size_.height == height)
729 #if defined(GOOGLE_TV)
730 if ((external_surface_threshold_ >= 0 &&
731 external_surface_threshold_ <= width * height) ||
732 // Use H/W surface for MSE as the content is protected.
733 media_source_delegate_) {
734 needs_external_surface_ = true;
735 if (!paused() && !manager_->IsInFullscreen(frame_))
736 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
737 } else if (stream_texture_factory_ && !stream_id_) {
738 // Do deferred stream texture creation finally.
739 DoCreateStreamTexture();
741 SetNeedsEstablishPeer(true);
743 EstablishSurfaceTexturePeer();
748 natural_size_.width = width;
749 natural_size_.height = height;
750 ReallocateVideoFrame();
753 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
754 DCHECK(main_loop_->BelongsToCurrentThread());
755 current_time_ = current_time.InSecondsF();
758 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice() {
759 DCHECK(main_thread_checker_.CalledOnValidThread());
760 DCHECK(!media_source_delegate_);
761 DrawRemotePlaybackIcon();
763 SetNeedsEstablishPeer(false);
766 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
767 DCHECK(main_thread_checker_.CalledOnValidThread());
768 DCHECK(!media_source_delegate_);
769 SetNeedsEstablishPeer(true);
771 EstablishSurfaceTexturePeer();
773 ReallocateVideoFrame();
776 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
777 if (!manager_->IsInFullscreen(frame_)) {
778 frame_->view()->willEnterFullScreen();
779 frame_->view()->didEnterFullScreen();
780 manager_->DidEnterFullscreen(frame_);
784 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
785 // |needs_external_surface_| is always false on non-TV devices.
786 if (!needs_external_surface_)
787 SetNeedsEstablishPeer(true);
788 // We had the fullscreen surface connected to Android MediaPlayer,
789 // so reconnect our surface texture for embedded playback.
790 if (!paused() && needs_establish_peer_)
791 EstablishSurfaceTexturePeer();
793 #if defined(GOOGLE_TV)
794 if (!paused() && needs_external_surface_)
795 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
798 frame_->view()->willExitFullScreen();
799 frame_->view()->didExitFullScreen();
800 manager_->DidExitFullscreen();
804 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
805 UpdatePlayingState(true);
806 client_->playbackStateChanged();
809 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
810 UpdatePlayingState(false);
811 client_->playbackStateChanged();
814 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
815 DCHECK(main_loop_->BelongsToCurrentThread());
816 // Only MSE |player_type_| registers this callback.
817 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
819 // Cache the new duration value and trust it over any subsequent duration
820 // values received in OnMediaMetadataChanged().
821 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer
822 // considers unseekable, including kInfiniteDuration().
823 // See http://crbug.com/248396
824 duration_ = duration;
825 ignore_metadata_duration_change_ = true;
827 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
828 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
829 client_->durationChanged();
832 void WebMediaPlayerAndroid::UpdateNetworkState(
833 WebMediaPlayer::NetworkState state) {
834 DCHECK(main_loop_->BelongsToCurrentThread());
835 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
836 (state == WebMediaPlayer::NetworkStateNetworkError ||
837 state == WebMediaPlayer::NetworkStateDecodeError)) {
838 // Any error that occurs before reaching ReadyStateHaveMetadata should
839 // be considered a format error.
840 network_state_ = WebMediaPlayer::NetworkStateFormatError;
842 network_state_ = state;
844 client_->networkStateChanged();
847 void WebMediaPlayerAndroid::UpdateReadyState(
848 WebMediaPlayer::ReadyState state) {
849 ready_state_ = state;
850 client_->readyStateChanged();
853 void WebMediaPlayerAndroid::OnPlayerReleased() {
854 // |needs_external_surface_| is always false on non-TV devices.
855 if (!needs_external_surface_)
856 needs_establish_peer_ = true;
858 #if defined(GOOGLE_TV)
859 last_computed_rect_ = gfx::RectF();
863 void WebMediaPlayerAndroid::ReleaseMediaResources() {
864 switch (network_state_) {
865 // Pause the media player and inform WebKit if the player is in a good
867 case WebMediaPlayer::NetworkStateIdle:
868 case WebMediaPlayer::NetworkStateLoading:
869 case WebMediaPlayer::NetworkStateLoaded:
871 client_->playbackStateChanged();
873 // If a WebMediaPlayer instance has entered into one of these states,
874 // the internal network state in HTMLMediaElement could be set to empty.
875 // And calling playbackStateChanged() could get this object deleted.
876 case WebMediaPlayer::NetworkStateEmpty:
877 case WebMediaPlayer::NetworkStateFormatError:
878 case WebMediaPlayer::NetworkStateNetworkError:
879 case WebMediaPlayer::NetworkStateDecodeError:
882 manager_->ReleaseResources(player_id_);
886 void WebMediaPlayerAndroid::WillDestroyCurrentMessageLoop() {
888 manager_->UnregisterMediaPlayer(player_id_);
892 void WebMediaPlayerAndroid::Detach() {
894 stream_texture_factory_->DestroyStreamTexture(texture_id_);
898 media_source_delegate_.reset();
900 base::AutoLock auto_lock(current_frame_lock_);
901 current_frame_ = NULL;
907 void WebMediaPlayerAndroid::DrawRemotePlaybackIcon() {
908 DCHECK(main_thread_checker_.CalledOnValidThread());
909 if (!video_weblayer_)
911 WebKit::WebGraphicsContext3D* context = stream_texture_factory_->Context3d();
912 if (!context->makeContextCurrent())
915 // TODO(johnme): Should redraw this frame if the layer bounds change; but
916 // there seems no easy way to listen for the layer resizing (as opposed to
917 // OnVideoSizeChanged, which is when the frame sizes of the video file
918 // change). Perhaps have to poll (on main thread of course)?
919 gfx::Size video_size_css_px = video_weblayer_->bounds();
920 float device_scale_factor = frame_->view()->deviceScaleFactor();
921 // canvas_size will be the size in device pixels when pageScaleFactor == 1
922 gfx::Size canvas_size(
923 static_cast<int>(video_size_css_px.width() * device_scale_factor),
924 static_cast<int>(video_size_css_px.height() * device_scale_factor));
928 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
929 bitmap.allocPixels();
931 SkCanvas canvas(bitmap);
932 canvas.drawColor(SK_ColorBLACK);
934 paint.setAntiAlias(true);
935 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
936 const SkBitmap* icon_bitmap =
937 content::GetContentClient()
938 ->GetNativeImageNamed(IDR_MEDIAPLAYER_REMOTE_PLAYBACK_ICON)
940 // In order to get a reasonable margin around the icon:
941 // - the icon should be under half the frame width
942 // - the icon should be at most 3/5 of the frame height
943 // Additionally, on very large screens, the icon size should be capped. A max
944 // width of 320 was arbitrarily chosen; since this is half the resource's
945 // pixel width, it should look crisp even on 2x deviceScaleFactor displays.
946 int icon_width = 320;
947 icon_width = std::min(icon_width, canvas_size.width() / 2);
948 icon_width = std::min(icon_width,
949 canvas_size.height() * icon_bitmap->width() /
950 icon_bitmap->height() * 3 / 5);
951 int icon_height = icon_width * icon_bitmap->height() / icon_bitmap->width();
952 // Center the icon within the frame
953 SkRect icon_rect = SkRect::MakeXYWH((canvas_size.width() - icon_width) / 2,
954 (canvas_size.height() - icon_height) / 2,
957 canvas.drawBitmapRectToRect(
958 *icon_bitmap, NULL /* src */, icon_rect /* dest */, &paint);
960 if (!remote_playback_texture_id_)
961 remote_playback_texture_id_ = context->createTexture();
962 unsigned texture_target = GL_TEXTURE_2D;
963 context->bindTexture(texture_target, remote_playback_texture_id_);
964 context->texParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
965 context->texParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
966 context->texParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
967 context->texParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
970 SkAutoLockPixels lock(bitmap);
971 context->texImage2D(texture_target,
973 GL_RGBA /* internalformat */,
977 GL_RGBA /* format */,
978 GL_UNSIGNED_BYTE /* type */,
982 gpu::Mailbox texture_mailbox;
983 context->genMailboxCHROMIUM(texture_mailbox.name);
984 context->produceTextureCHROMIUM(texture_target, texture_mailbox.name);
986 unsigned texture_mailbox_sync_point = context->insertSyncPoint();
988 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
989 new VideoFrame::MailboxHolder(
991 texture_mailbox_sync_point,
992 base::Bind(&WebMediaPlayerAndroid::OnReleaseRemotePlaybackTexture,
994 weak_factory_.GetWeakPtr())),
996 canvas_size /* coded_size */,
997 gfx::Rect(canvas_size) /* visible_rect */,
998 canvas_size /* natural_size */,
999 base::TimeDelta() /* timestamp */,
1000 VideoFrame::ReadPixelsCB(),
1001 base::Closure() /* no_longer_needed_cb */);
1002 SetCurrentFrameInternal(new_frame);
1005 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
1006 if (needs_external_surface_) {
1007 // VideoFrame::CreateHoleFrame is only defined under GOOGLE_TV.
1008 #if defined(GOOGLE_TV)
1009 if (!natural_size_.isEmpty()) {
1010 scoped_refptr<VideoFrame> new_frame =
1011 VideoFrame::CreateHoleFrame(natural_size_);
1012 SetCurrentFrameInternal(new_frame);
1013 // Force the client to grab the hole frame.
1017 NOTIMPLEMENTED() << "Hole punching not supported outside of Google TV";
1019 } else if (!is_remote_ && texture_id_) {
1020 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1021 new VideoFrame::MailboxHolder(
1023 texture_mailbox_sync_point_,
1024 VideoFrame::MailboxHolder::TextureNoLongerNeededCallback()),
1025 kGLTextureExternalOES, natural_size_,
1026 gfx::Rect(natural_size_), natural_size_, base::TimeDelta(),
1027 VideoFrame::ReadPixelsCB(),
1029 SetCurrentFrameInternal(new_frame);
1033 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1034 cc::VideoFrameProvider::Client* client) {
1035 // This is called from both the main renderer thread and the compositor
1036 // thread (when the main thread is blocked).
1037 if (video_frame_provider_client_)
1038 video_frame_provider_client_->StopUsingProvider();
1039 video_frame_provider_client_ = client;
1041 // Set the callback target when a frame is produced.
1042 if (stream_texture_proxy_)
1043 stream_texture_proxy_->SetClient(client);
1046 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1047 scoped_refptr<media::VideoFrame>& video_frame) {
1048 base::AutoLock auto_lock(current_frame_lock_);
1049 current_frame_ = video_frame;
1052 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1053 scoped_refptr<VideoFrame> video_frame;
1055 base::AutoLock auto_lock(current_frame_lock_);
1056 video_frame = current_frame_;
1059 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
1060 stream_id_ && !needs_external_surface_ && !is_remote_) {
1061 gfx::Size natural_size = video_frame->natural_size();
1062 // TODO(sievers): These variables are accessed on the wrong thread here.
1063 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1064 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1065 stream_texture_proxy_initialized_ = true;
1066 cached_stream_texture_size_ = natural_size;
1072 void WebMediaPlayerAndroid::PutCurrentFrame(
1073 const scoped_refptr<media::VideoFrame>& frame) {
1076 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1078 if (stream_texture_proxy_)
1081 // No factory to create proxy.
1082 if (!stream_texture_factory_)
1085 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1086 if (needs_establish_peer_ && stream_texture_proxy_) {
1087 DoCreateStreamTexture();
1088 ReallocateVideoFrame();
1091 if (stream_texture_proxy_ && video_frame_provider_client_)
1092 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1095 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1096 if (!stream_texture_proxy_)
1099 if (media_source_delegate_ && stream_texture_factory_) {
1100 // MediaCodec will release the old surface when it goes away, we need to
1101 // recreate a new one each time this is called.
1102 stream_texture_factory_->DestroyStreamTexture(texture_id_);
1105 texture_mailbox_ = gpu::Mailbox();
1106 texture_mailbox_sync_point_ = 0;
1107 DoCreateStreamTexture();
1108 ReallocateVideoFrame();
1109 stream_texture_proxy_initialized_ = false;
1111 if (stream_texture_factory_.get() && stream_id_)
1112 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1113 needs_establish_peer_ = false;
1116 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1117 DCHECK(!stream_id_);
1118 DCHECK(!texture_id_);
1119 DCHECK(!texture_mailbox_sync_point_);
1120 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1121 kGLTextureExternalOES,
1124 &texture_mailbox_sync_point_);
1127 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1128 needs_establish_peer_ = needs_establish_peer;
1131 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1132 is_playing_ = is_playing;
1136 delegate_->DidPlay(this);
1138 delegate_->DidPause(this);
1141 #if defined(GOOGLE_TV)
1142 bool WebMediaPlayerAndroid::RetrieveGeometryChange(gfx::RectF* rect) {
1143 if (!video_weblayer_)
1146 // Compute the geometry of video frame layer.
1147 cc::Layer* layer = video_weblayer_->layer();
1148 rect->set_size(layer->bounds());
1150 rect->Offset(layer->position().OffsetFromOrigin());
1151 layer = layer->parent();
1154 // Return false when the geometry hasn't been changed from the last time.
1155 if (last_computed_rect_ == *rect)
1158 // Store the changed geometry information when it is actually changed.
1159 last_computed_rect_ = *rect;
1164 // The following EME related code is copied from WebMediaPlayerImpl.
1165 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1166 // WebMediaPlayerImpl.
1167 // TODO(kjyoun): Update Google TV EME implementation to use IPC.
1169 // Helper functions to report media EME related stats to UMA. They follow the
1170 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1171 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1172 // that UMA_* macros require the names to be constant throughout the process'
1174 static void EmeUMAHistogramEnumeration(const WebKit::WebString& key_system,
1175 const std::string& method,
1177 int boundary_value) {
1178 base::LinearHistogram::FactoryGet(
1179 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1180 1, boundary_value, boundary_value + 1,
1181 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1184 static void EmeUMAHistogramCounts(const WebKit::WebString& key_system,
1185 const std::string& method,
1187 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1188 base::Histogram::FactoryGet(
1189 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1190 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1193 // Helper enum for reporting generateKeyRequest/addKey histograms.
1194 enum MediaKeyException {
1197 kKeySystemNotSupported,
1198 kInvalidPlayerState,
1199 kMaxMediaKeyException
1202 static MediaKeyException MediaKeyExceptionForUMA(
1203 WebMediaPlayer::MediaKeyException e) {
1205 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1206 return kKeySystemNotSupported;
1207 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1208 return kInvalidPlayerState;
1209 case WebMediaPlayer::MediaKeyExceptionNoError:
1212 return kUnknownResultId;
1216 // Helper for converting |key_system| name and exception |e| to a pair of enum
1217 // values from above, for reporting to UMA.
1218 static void ReportMediaKeyExceptionToUMA(
1219 const std::string& method,
1220 const WebString& key_system,
1221 WebMediaPlayer::MediaKeyException e) {
1222 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1223 DCHECK_NE(result_id, kUnknownResultId) << e;
1224 EmeUMAHistogramEnumeration(
1225 key_system, method, result_id, kMaxMediaKeyException);
1228 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1229 const WebString& key_system,
1230 const unsigned char* init_data,
1231 unsigned init_data_length) {
1232 WebMediaPlayer::MediaKeyException e =
1233 GenerateKeyRequestInternal(key_system, init_data, init_data_length);
1234 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e);
1238 WebMediaPlayer::MediaKeyException
1239 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1240 const WebString& key_system,
1241 const unsigned char* init_data,
1242 unsigned init_data_length) {
1243 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": "
1244 << std::string(reinterpret_cast<const char*>(init_data),
1245 static_cast<size_t>(init_data_length));
1247 if (!IsConcreteSupportedKeySystem(key_system))
1248 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1250 // We do not support run-time switching between key systems for now.
1251 if (current_key_system_.isEmpty()) {
1252 if (!decryptor_->InitializeCDM(key_system.utf8(), frame_->document().url()))
1253 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1254 current_key_system_ = key_system;
1255 } else if (key_system != current_key_system_) {
1256 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1259 // TODO(xhwang): We assume all streams are from the same container (thus have
1260 // the same "type") for now. In the future, the "type" should be passed down
1261 // from the application.
1262 if (!decryptor_->GenerateKeyRequest(init_data_type_,
1263 init_data, init_data_length)) {
1264 current_key_system_.reset();
1265 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1268 return WebMediaPlayer::MediaKeyExceptionNoError;
1271 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1272 const WebString& key_system,
1273 const unsigned char* key,
1274 unsigned key_length,
1275 const unsigned char* init_data,
1276 unsigned init_data_length,
1277 const WebString& session_id) {
1278 WebMediaPlayer::MediaKeyException e = AddKeyInternal(
1279 key_system, key, key_length, init_data, init_data_length, session_id);
1280 ReportMediaKeyExceptionToUMA("addKey", key_system, e);
1284 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1285 const WebString& key_system,
1286 const unsigned char* key,
1287 unsigned key_length,
1288 const unsigned char* init_data,
1289 unsigned init_data_length,
1290 const WebString& session_id) {
1292 DCHECK_GT(key_length, 0u);
1293 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": "
1294 << std::string(reinterpret_cast<const char*>(key),
1295 static_cast<size_t>(key_length)) << ", "
1296 << std::string(reinterpret_cast<const char*>(init_data),
1297 static_cast<size_t>(init_data_length))
1298 << " [" << session_id.utf8().data() << "]";
1300 if (!IsConcreteSupportedKeySystem(key_system))
1301 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1303 if (current_key_system_.isEmpty() || key_system != current_key_system_)
1304 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1306 decryptor_->AddKey(key, key_length, init_data, init_data_length,
1308 return WebMediaPlayer::MediaKeyExceptionNoError;
1311 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1312 const WebString& key_system,
1313 const WebString& session_id) {
1314 WebMediaPlayer::MediaKeyException e =
1315 CancelKeyRequestInternal(key_system, session_id);
1316 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e);
1320 WebMediaPlayer::MediaKeyException
1321 WebMediaPlayerAndroid::CancelKeyRequestInternal(
1322 const WebString& key_system,
1323 const WebString& session_id) {
1324 if (!IsConcreteSupportedKeySystem(key_system))
1325 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1327 if (current_key_system_.isEmpty() || key_system != current_key_system_)
1328 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1330 decryptor_->CancelKeyRequest(session_id.utf8());
1331 return WebMediaPlayer::MediaKeyExceptionNoError;
1334 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1335 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1337 #if defined(GOOGLE_TV)
1338 if (media_source_delegate_)
1339 media_source_delegate_->NotifyKeyAdded(current_key_system_.utf8());
1340 #endif // defined(GOOGLE_TV)
1342 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id));
1345 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1346 media::MediaKeys::KeyError error_code,
1348 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1349 error_code, media::MediaKeys::kMaxKeyError);
1352 current_key_system_,
1353 WebString::fromUTF8(session_id),
1354 static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1358 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1359 const std::vector<uint8>& message,
1360 const std::string& destination_url) {
1361 const GURL destination_url_gurl(destination_url);
1362 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid())
1363 << "Invalid URL in destination_url: " << destination_url;
1365 client_->keyMessage(current_key_system_,
1366 WebString::fromUTF8(session_id),
1367 message.empty() ? NULL : &message[0],
1369 destination_url_gurl);
1372 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1373 WebKit::WebMediaSource* web_media_source) {
1374 client_->mediaSourceOpened(web_media_source);
1377 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1378 const std::vector<uint8>& init_data) {
1379 DCHECK(main_loop_->BelongsToCurrentThread());
1380 // Do not fire NeedKey event if encrypted media is not enabled.
1381 if (!WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled() &&
1382 !WebKit::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled()) {
1386 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1388 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1389 if (init_data_type_.empty())
1390 init_data_type_ = type;
1392 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1393 // TODO(xhwang): Drop |keySystem| and |sessionId| in keyNeeded() call.
1394 client_->keyNeeded(WebString(),
1400 #if defined(GOOGLE_TV)
1401 bool WebMediaPlayerAndroid::InjectMediaStream(
1402 MediaStreamClient* media_stream_client,
1403 media::Demuxer* demuxer,
1404 const base::Closure& destroy_demuxer_cb) {
1406 media_stream_client_ = media_stream_client;
1408 destroy_demuxer_cb_ = destroy_demuxer_cb;
1413 void WebMediaPlayerAndroid::DoReleaseRemotePlaybackTexture(uint32 sync_point) {
1414 DCHECK(main_thread_checker_.CalledOnValidThread());
1415 DCHECK(remote_playback_texture_id_);
1417 WebKit::WebGraphicsContext3D* context =
1418 stream_texture_factory_->Context3d();
1421 context->waitSyncPoint(sync_point);
1422 context->deleteTexture(remote_playback_texture_id_);
1423 remote_playback_texture_id_ = 0;
1426 void WebMediaPlayerAndroid::enterFullscreen() {
1427 if (manager_->CanEnterFullscreen(frame_)) {
1428 manager_->EnterFullscreen(player_id_);
1429 SetNeedsEstablishPeer(false);
1433 void WebMediaPlayerAndroid::exitFullscreen() {
1434 manager_->ExitFullscreen(player_id_);
1437 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1438 return manager_->CanEnterFullscreen(frame_);
1441 } // namespace content