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/common/content_switches.h"
22 #include "content/public/renderer/render_frame.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/webcontentdecryptionmodule_impl.h"
27 #include "content/renderer/media/webmediaplayer_delegate.h"
28 #include "content/renderer/media/webmediaplayer_util.h"
29 #include "content/renderer/render_frame_impl.h"
30 #include "content/renderer/render_thread_impl.h"
31 #include "gpu/GLES2/gl2extchromium.h"
32 #include "gpu/command_buffer/client/gles2_interface.h"
33 #include "gpu/command_buffer/common/mailbox_holder.h"
34 #include "media/base/android/media_player_android.h"
35 #include "media/base/bind_to_current_loop.h"
36 // TODO(xhwang): Remove when we remove prefixed EME implementation.
37 #include "media/base/media_keys.h"
38 #include "media/base/media_switches.h"
39 #include "media/base/video_frame.h"
40 #include "net/base/mime_util.h"
41 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
42 #include "third_party/WebKit/public/platform/WebString.h"
43 #include "third_party/WebKit/public/platform/WebURL.h"
44 #include "third_party/WebKit/public/web/WebDocument.h"
45 #include "third_party/WebKit/public/web/WebFrame.h"
46 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
47 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
48 #include "third_party/WebKit/public/web/WebView.h"
49 #include "third_party/skia/include/core/SkBitmap.h"
50 #include "third_party/skia/include/core/SkCanvas.h"
51 #include "third_party/skia/include/core/SkPaint.h"
52 #include "third_party/skia/include/core/SkTypeface.h"
53 #include "ui/gfx/image/image.h"
54 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
56 static const uint32 kGLTextureExternalOES = 0x8D65;
58 using blink::WebMediaPlayer;
60 using blink::WebString;
61 using blink::WebTimeRanges;
63 using gpu::gles2::GLES2Interface;
64 using media::MediaPlayerAndroid;
65 using media::VideoFrame;
68 // Prefix for histograms related to Encrypted Media Extensions.
69 const char* kMediaEme = "Media.EME.";
71 // File-static function is to allow it to run even after WMPA is deleted.
72 void OnReleaseTexture(
73 const scoped_refptr<content::StreamTextureFactory>& factories,
75 const std::vector<uint32>& release_sync_points) {
76 GLES2Interface* gl = factories->ContextGL();
77 for (size_t i = 0; i < release_sync_points.size(); i++)
78 gl->WaitSyncPointCHROMIUM(release_sync_points[i]);
79 gl->DeleteTextures(1, &texture_id);
85 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
86 blink::WebFrame* frame,
87 blink::WebMediaPlayerClient* client,
88 base::WeakPtr<WebMediaPlayerDelegate> delegate,
89 RendererMediaPlayerManager* manager,
90 scoped_refptr<StreamTextureFactory> factory,
91 const scoped_refptr<base::MessageLoopProxy>& media_loop,
92 media::MediaLog* media_log)
93 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
97 buffered_(static_cast<size_t>(1)),
98 media_loop_(media_loop),
99 ignore_metadata_duration_change_(false),
100 pending_seek_(false),
102 did_loading_progress_(false),
104 network_state_(WebMediaPlayer::NetworkStateEmpty),
105 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
109 playing_started_(false),
110 needs_establish_peer_(true),
111 stream_texture_proxy_initialized_(false),
112 has_size_info_(false),
113 has_media_metadata_(false),
114 has_media_info_(false),
115 stream_texture_factory_(factory),
116 needs_external_surface_(false),
117 video_frame_provider_client_(NULL),
118 pending_playback_(false),
119 player_type_(MEDIA_PLAYER_TYPE_URL),
122 media_log_(media_log),
124 weak_factory_(this) {
127 DCHECK(main_thread_checker_.CalledOnValidThread());
129 player_id_ = manager_->RegisterMediaPlayer(this);
131 #if defined(VIDEO_HOLE)
132 if (manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo()) {
133 // Defer stream texture creation until we are sure it's necessary.
134 needs_establish_peer_ = false;
135 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
137 force_use_overlay_embedded_video_ = CommandLine::ForCurrentProcess()->
138 HasSwitch(switches::kForceUseOverlayEmbeddedVideo);
139 #endif // defined(VIDEO_HOLE)
140 TryCreateStreamTextureProxyIfNeeded();
143 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
144 SetVideoFrameProviderClient(NULL);
145 client_->setWebLayer(NULL);
148 manager_->DestroyPlayer(player_id_);
149 manager_->UnregisterMediaPlayer(player_id_);
153 GLES2Interface* gl = stream_texture_factory_->ContextGL();
154 gl->DeleteTextures(1, &texture_id_);
156 texture_mailbox_ = gpu::Mailbox();
161 base::AutoLock auto_lock(current_frame_lock_);
162 current_frame_ = NULL;
165 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
166 delegate_->PlayerGone(this);
169 void WebMediaPlayerAndroid::load(LoadType load_type,
170 const blink::WebURL& url,
171 CORSMode cors_mode) {
174 player_type_ = MEDIA_PLAYER_TYPE_URL;
177 case LoadTypeMediaSource:
178 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
181 case LoadTypeMediaStream:
182 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
187 has_media_metadata_ = false;
188 has_media_info_ = false;
190 int demuxer_client_id = 0;
191 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
192 has_media_info_ = true;
194 RendererDemuxerAndroid* demuxer =
195 RenderThreadImpl::current()->renderer_demuxer();
196 demuxer_client_id = demuxer->GetNextDemuxerClientID();
198 media_source_delegate_.reset(new MediaSourceDelegate(
199 demuxer, demuxer_client_id, media_loop_, media_log_));
201 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
202 media::SetDecryptorReadyCB set_decryptor_ready_cb =
203 media::BindToCurrentLoop(
204 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
205 weak_factory_.GetWeakPtr()));
207 media_source_delegate_->InitializeMediaSource(
208 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
209 weak_factory_.GetWeakPtr()),
210 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
211 weak_factory_.GetWeakPtr()),
212 set_decryptor_ready_cb,
213 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
214 weak_factory_.GetWeakPtr()),
215 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
216 weak_factory_.GetWeakPtr()));
223 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
224 weak_factory_.GetWeakPtr())));
225 // TODO(qinmin): The url might be redirected when android media player
226 // requests the stream. As a result, we cannot guarantee there is only
227 // a single origin. Remove the following line when b/12573548 is fixed.
228 // Check http://crbug.com/334204.
229 info_loader_->set_single_origin(false);
230 info_loader_->Start(frame_);
234 GURL first_party_url = frame_->document().firstPartyForCookies();
235 manager_->Initialize(
236 player_type_, player_id_, url, first_party_url, demuxer_client_id);
238 if (manager_->ShouldEnterFullscreen(frame_))
239 manager_->EnterFullscreen(player_id_, frame_);
241 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
242 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
245 void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
246 DCHECK(!media_source_delegate_);
247 if (status == MediaInfoLoader::kFailed) {
248 info_loader_.reset();
249 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
253 has_media_info_ = true;
254 if (has_media_metadata_ &&
255 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
256 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
257 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
259 // Android doesn't start fetching resources until an implementation-defined
260 // event (e.g. playback request) occurs. Sets the network state to IDLE
261 // if play is not requested yet.
262 if (!playing_started_)
263 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
266 void WebMediaPlayerAndroid::play() {
267 #if defined(VIDEO_HOLE)
268 if (hasVideo() && needs_external_surface_ &&
269 !manager_->IsInFullscreen(frame_)) {
270 DCHECK(!needs_establish_peer_);
271 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
273 #endif // defined(VIDEO_HOLE)
275 TryCreateStreamTextureProxyIfNeeded();
276 // There is no need to establish the surface texture peer for fullscreen
278 if (hasVideo() && needs_establish_peer_ && !manager_->IsInFullscreen(frame_))
279 EstablishSurfaceTexturePeer();
282 manager_->Start(player_id_);
283 UpdatePlayingState(true);
284 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
285 playing_started_ = true;
288 void WebMediaPlayerAndroid::pause() {
292 void WebMediaPlayerAndroid::seek(double seconds) {
293 DCHECK(main_thread_checker_.CalledOnValidThread());
294 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
296 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
299 if (new_seek_time == seek_time_) {
300 if (media_source_delegate_) {
301 if (!pending_seek_) {
302 // If using media source demuxer, only suppress redundant seeks if
303 // there is no pending seek. This enforces that any pending seek that
304 // results in a demuxer seek is preceded by matching
305 // CancelPendingSeek() and StartWaitingForSeek() calls.
309 // Suppress all redundant seeks if unrestricted by media source
311 pending_seek_ = false;
316 pending_seek_ = true;
317 pending_seek_time_ = new_seek_time;
319 if (media_source_delegate_)
320 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
322 // Later, OnSeekComplete will trigger the pending seek.
327 seek_time_ = new_seek_time;
329 if (media_source_delegate_)
330 media_source_delegate_->StartWaitingForSeek(seek_time_);
332 // Kick off the asynchronous seek!
333 manager_->Seek(player_id_, seek_time_);
336 bool WebMediaPlayerAndroid::supportsSave() const {
340 void WebMediaPlayerAndroid::setRate(double rate) {
344 void WebMediaPlayerAndroid::setVolume(double volume) {
345 manager_->SetVolume(player_id_, volume);
348 bool WebMediaPlayerAndroid::hasVideo() const {
349 // If we have obtained video size information before, use it.
351 return !natural_size_.isEmpty();
353 // TODO(qinmin): need a better method to determine whether the current media
354 // content contains video. Android does not provide any function to do
356 // We don't know whether the current media content has video unless
357 // the player is prepared. If the player is not prepared, we fall back
358 // to the mime-type. There may be no mime-type on a redirect URL.
359 // In that case, we conservatively assume it contains video so that
360 // enterfullscreen call will not fail.
361 if (!url_.has_path())
364 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
366 return mime.find("audio/") == std::string::npos;
369 bool WebMediaPlayerAndroid::hasAudio() const {
370 if (!url_.has_path())
373 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
376 if (mime.find("audio/") != std::string::npos ||
377 mime.find("video/") != std::string::npos ||
378 mime.find("application/ogg") != std::string::npos) {
384 bool WebMediaPlayerAndroid::paused() const {
388 bool WebMediaPlayerAndroid::seeking() const {
392 double WebMediaPlayerAndroid::duration() const {
393 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
394 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
395 return std::numeric_limits<double>::quiet_NaN();
397 if (duration_ == media::kInfiniteDuration())
398 return std::numeric_limits<double>::infinity();
400 return duration_.InSecondsF();
403 double WebMediaPlayerAndroid::timelineOffset() const {
404 base::Time timeline_offset;
405 if (media_source_delegate_)
406 timeline_offset = media_source_delegate_->GetTimelineOffset();
408 if (timeline_offset.is_null())
409 return std::numeric_limits<double>::quiet_NaN();
411 return timeline_offset.ToJsTime();
414 double WebMediaPlayerAndroid::currentTime() const {
415 // If the player is processing a seek, return the seek time.
416 // Blink may still query us if updatePlaybackState() occurs while seeking.
418 return pending_seek_ ?
419 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
422 return current_time_;
425 WebSize WebMediaPlayerAndroid::naturalSize() const {
426 return natural_size_;
429 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
430 return network_state_;
433 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
437 const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
438 if (media_source_delegate_)
439 return media_source_delegate_->Buffered();
443 double WebMediaPlayerAndroid::maxTimeSeekable() const {
444 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
445 // return 0 so that the seekable range is empty.
446 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
452 bool WebMediaPlayerAndroid::didLoadingProgress() const {
453 bool ret = did_loading_progress_;
454 did_loading_progress_ = false;
458 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
459 const blink::WebRect& rect,
460 unsigned char alpha) {
464 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
465 blink::WebGraphicsContext3D* web_graphics_context,
466 unsigned int texture,
468 unsigned int internal_format,
470 bool premultiply_alpha,
472 // Don't allow clients to copy an encrypted video frame.
473 if (needs_external_surface_)
476 scoped_refptr<VideoFrame> video_frame;
478 base::AutoLock auto_lock(current_frame_lock_);
479 video_frame = current_frame_;
483 video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
485 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
486 DCHECK((!is_remote_ &&
487 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) ||
488 (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D));
490 // For hidden video element (with style "display:none"), ensure the texture
493 (cached_stream_texture_size_.width != natural_size_.width ||
494 cached_stream_texture_size_.height != natural_size_.height)) {
495 stream_texture_factory_->SetStreamTextureSize(
496 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
497 cached_stream_texture_size_ = natural_size_;
500 uint32 source_texture = web_graphics_context->createTexture();
501 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
503 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
504 // an invalid texture target may be used for copy texture.
505 web_graphics_context->bindTexture(mailbox_holder->texture_target,
507 web_graphics_context->consumeTextureCHROMIUM(mailbox_holder->texture_target,
508 mailbox_holder->mailbox.name);
510 // The video is stored in an unmultiplied format, so premultiply if
512 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
515 // Application itself needs to take care of setting the right flip_y
516 // value down to get the expected result.
517 // flip_y==true means to reverse the video orientation while
518 // flip_y==false means to keep the intrinsic orientation.
519 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
520 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture,
521 texture, level, internal_format,
523 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
524 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
527 if (mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES)
528 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
530 web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
531 web_graphics_context->deleteTexture(source_texture);
532 web_graphics_context->flush();
533 video_frame->AppendReleaseSyncPoint(web_graphics_context->insertSyncPoint());
537 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
539 return info_loader_->HasSingleOrigin();
540 // The info loader may have failed.
541 if (player_type_ == MEDIA_PLAYER_TYPE_URL)
546 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
548 return info_loader_->DidPassCORSAccessCheck();
552 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
553 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
556 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
557 if (media_source_delegate_)
558 return media_source_delegate_->DecodedFrameCount();
563 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
564 if (media_source_delegate_)
565 return media_source_delegate_->DroppedFrameCount();
570 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
571 if (media_source_delegate_)
572 return media_source_delegate_->AudioDecodedByteCount();
577 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
578 if (media_source_delegate_)
579 return media_source_delegate_->VideoDecodedByteCount();
584 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
585 const base::TimeDelta& duration, int width, int height, bool success) {
586 bool need_to_signal_duration_changed = false;
588 if (url_.SchemeIs("file") || url_.SchemeIs("app"))
589 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
591 // Update duration, if necessary, prior to ready state updates that may
592 // cause duration() query.
593 if (!ignore_metadata_duration_change_ && duration_ != duration) {
594 duration_ = duration;
596 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
597 // already triggers a durationchanged event. If this is a different
598 // transition, remember to signal durationchanged.
599 // Do not ever signal durationchanged on metadata change in MSE case
600 // because OnDurationChanged() handles this.
601 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
602 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
603 need_to_signal_duration_changed = true;
607 has_media_metadata_ = true;
608 if (has_media_info_ &&
609 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
610 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
611 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
614 // TODO(wolenetz): Should we just abort early and set network state to an
615 // error if success == false? See http://crbug.com/248399
617 OnVideoSizeChanged(width, height);
619 if (need_to_signal_duration_changed)
620 client_->durationChanged();
623 void WebMediaPlayerAndroid::OnPlaybackComplete() {
624 // When playback is about to finish, android media player often stops
625 // at a time which is smaller than the duration. This makes webkit never
626 // know that the playback has finished. To solve this, we set the
627 // current time to media duration when OnPlaybackComplete() get called.
628 OnTimeUpdate(duration_);
629 client_->timeChanged();
631 // if the loop attribute is set, timeChanged() will update the current time
632 // to 0. It will perform a seek to 0. As the requests to the renderer
633 // process are sequential, the OnSeekComplete() will only occur
634 // once OnPlaybackComplete() is done. As the playback can only be executed
635 // upon completion of OnSeekComplete(), the request needs to be saved.
637 if (seeking_ && seek_time_ == base::TimeDelta())
638 pending_playback_ = true;
641 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
642 buffered_[0].end = duration() * percentage / 100;
643 did_loading_progress_ = true;
646 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
647 DCHECK(main_thread_checker_.CalledOnValidThread());
648 client_->requestSeek(time_to_seek.InSecondsF());
651 void WebMediaPlayerAndroid::OnSeekComplete(
652 const base::TimeDelta& current_time) {
653 DCHECK(main_thread_checker_.CalledOnValidThread());
656 pending_seek_ = false;
657 seek(pending_seek_time_.InSecondsF());
661 OnTimeUpdate(current_time);
663 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
665 client_->timeChanged();
667 if (pending_playback_) {
669 pending_playback_ = false;
673 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
674 switch (error_type) {
675 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
676 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
678 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
679 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
681 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
682 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
684 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
690 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
691 has_size_info_ = true;
692 if (natural_size_.width == width && natural_size_.height == height)
695 #if defined(VIDEO_HOLE)
696 // Use H/W surface for encrypted video.
697 // TODO(qinmin): Change this so that only EME needs the H/W surface
698 if (force_use_overlay_embedded_video_ ||
699 (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted() &&
700 manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo())) {
701 needs_external_surface_ = true;
702 if (!paused() && !manager_->IsInFullscreen(frame_))
703 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
704 } else if (stream_texture_proxy_ && !stream_id_) {
705 // Do deferred stream texture creation finally.
706 DoCreateStreamTexture();
707 SetNeedsEstablishPeer(true);
709 #endif // defined(VIDEO_HOLE)
710 // When play() gets called, |natural_size_| may still be empty and
711 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
712 // may play without a surface texture. When we finally get the valid video
713 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
714 // previously called.
715 if (!paused() && needs_establish_peer_)
716 EstablishSurfaceTexturePeer();
718 natural_size_.width = width;
719 natural_size_.height = height;
720 ReallocateVideoFrame();
722 // Lazily allocate compositing layer.
723 if (!video_weblayer_) {
724 video_weblayer_.reset(
725 new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
726 client_->setWebLayer(video_weblayer_.get());
729 // TODO(qinmin): This is a hack. We need the media element to stop showing the
730 // poster image by forcing it to call setDisplayMode(video). Should move the
731 // logic into HTMLMediaElement.cpp.
732 client_->timeChanged();
735 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
736 DCHECK(main_thread_checker_.CalledOnValidThread());
737 current_time_ = current_time.InSecondsF();
740 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
741 const std::string& remote_playback_message) {
742 DCHECK(main_thread_checker_.CalledOnValidThread());
743 DCHECK(!media_source_delegate_);
744 DrawRemotePlaybackText(remote_playback_message);
746 SetNeedsEstablishPeer(false);
749 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
750 DCHECK(main_thread_checker_.CalledOnValidThread());
751 DCHECK(!media_source_delegate_);
752 SetNeedsEstablishPeer(true);
754 EstablishSurfaceTexturePeer();
756 ReallocateVideoFrame();
759 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
760 if (!manager_->IsInFullscreen(frame_)) {
761 frame_->view()->willEnterFullScreen();
762 frame_->view()->didEnterFullScreen();
763 manager_->DidEnterFullscreen(frame_);
767 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
768 // |needs_external_surface_| is always false on non-TV devices.
769 if (!needs_external_surface_)
770 SetNeedsEstablishPeer(true);
771 // We had the fullscreen surface connected to Android MediaPlayer,
772 // so reconnect our surface texture for embedded playback.
773 if (!paused() && needs_establish_peer_)
774 EstablishSurfaceTexturePeer();
776 #if defined(VIDEO_HOLE)
777 if (!paused() && needs_external_surface_)
778 manager_->RequestExternalSurface(player_id_, last_computed_rect_);
779 #endif // defined(VIDEO_HOLE)
781 frame_->view()->willExitFullScreen();
782 frame_->view()->didExitFullScreen();
783 manager_->DidExitFullscreen();
787 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
788 UpdatePlayingState(true);
789 client_->playbackStateChanged();
792 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
793 UpdatePlayingState(false);
794 client_->playbackStateChanged();
797 void WebMediaPlayerAndroid::OnRequestFullscreen() {
798 client_->requestFullscreen();
801 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
802 DCHECK(main_thread_checker_.CalledOnValidThread());
803 // Only MSE |player_type_| registers this callback.
804 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
806 // Cache the new duration value and trust it over any subsequent duration
807 // values received in OnMediaMetadataChanged().
808 duration_ = duration;
809 ignore_metadata_duration_change_ = true;
811 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
812 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
813 client_->durationChanged();
816 void WebMediaPlayerAndroid::UpdateNetworkState(
817 WebMediaPlayer::NetworkState state) {
818 DCHECK(main_thread_checker_.CalledOnValidThread());
819 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
820 (state == WebMediaPlayer::NetworkStateNetworkError ||
821 state == WebMediaPlayer::NetworkStateDecodeError)) {
822 // Any error that occurs before reaching ReadyStateHaveMetadata should
823 // be considered a format error.
824 network_state_ = WebMediaPlayer::NetworkStateFormatError;
826 network_state_ = state;
828 client_->networkStateChanged();
831 void WebMediaPlayerAndroid::UpdateReadyState(
832 WebMediaPlayer::ReadyState state) {
833 ready_state_ = state;
834 client_->readyStateChanged();
837 void WebMediaPlayerAndroid::OnPlayerReleased() {
838 // |needs_external_surface_| is always false on non-TV devices.
839 if (!needs_external_surface_)
840 needs_establish_peer_ = true;
843 OnMediaPlayerPause();
845 #if defined(VIDEO_HOLE)
846 last_computed_rect_ = gfx::RectF();
847 #endif // defined(VIDEO_HOLE)
850 void WebMediaPlayerAndroid::ReleaseMediaResources() {
851 switch (network_state_) {
852 // Pause the media player and inform WebKit if the player is in a good
854 case WebMediaPlayer::NetworkStateIdle:
855 case WebMediaPlayer::NetworkStateLoading:
856 case WebMediaPlayer::NetworkStateLoaded:
858 client_->playbackStateChanged();
860 // If a WebMediaPlayer instance has entered into one of these states,
861 // the internal network state in HTMLMediaElement could be set to empty.
862 // And calling playbackStateChanged() could get this object deleted.
863 case WebMediaPlayer::NetworkStateEmpty:
864 case WebMediaPlayer::NetworkStateFormatError:
865 case WebMediaPlayer::NetworkStateNetworkError:
866 case WebMediaPlayer::NetworkStateDecodeError:
869 manager_->ReleaseResources(player_id_);
873 void WebMediaPlayerAndroid::OnDestruct() {
875 manager_->UnregisterMediaPlayer(player_id_);
879 void WebMediaPlayerAndroid::Detach() {
881 GLES2Interface* gl = stream_texture_factory_->ContextGL();
882 gl->DeleteTextures(1, &texture_id_);
884 texture_mailbox_ = gpu::Mailbox();
888 media_source_delegate_.reset();
890 base::AutoLock auto_lock(current_frame_lock_);
891 current_frame_ = NULL;
897 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
898 manager_->Pause(player_id_, is_media_related_action);
899 UpdatePlayingState(false);
902 void WebMediaPlayerAndroid::DrawRemotePlaybackText(
903 const std::string& remote_playback_message) {
905 DCHECK(main_thread_checker_.CalledOnValidThread());
906 if (!video_weblayer_)
909 // TODO(johnme): Should redraw this frame if the layer bounds change; but
910 // there seems no easy way to listen for the layer resizing (as opposed to
911 // OnVideoSizeChanged, which is when the frame sizes of the video file
912 // change). Perhaps have to poll (on main thread of course)?
913 gfx::Size video_size_css_px = video_weblayer_->bounds();
914 float device_scale_factor = frame_->view()->deviceScaleFactor();
915 // canvas_size will be the size in device pixels when pageScaleFactor == 1
916 gfx::Size canvas_size(
917 static_cast<int>(video_size_css_px.width() * device_scale_factor),
918 static_cast<int>(video_size_css_px.height() * device_scale_factor));
922 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
923 bitmap.allocPixels();
925 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
926 SkCanvas canvas(bitmap);
927 canvas.drawColor(SK_ColorBLACK);
929 const SkScalar kTextSize(40);
930 const SkScalar kMinPadding(40);
933 paint.setAntiAlias(true);
934 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
935 paint.setColor(SK_ColorWHITE);
936 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
937 paint.setTextSize(kTextSize);
939 // Calculate the vertical margin from the top
940 SkPaint::FontMetrics font_metrics;
941 paint.getFontMetrics(&font_metrics);
942 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
944 // Measure the width of the entire text to display
945 size_t display_text_width = paint.measureText(
946 remote_playback_message.c_str(), remote_playback_message.size());
947 std::string display_text(remote_playback_message);
949 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
950 // The text is too long to fit in one line, truncate it and append ellipsis
953 // First, figure out how much of the canvas the '...' will take up.
954 const std::string kTruncationEllipsis("\xE2\x80\xA6");
955 SkScalar sk_ellipse_width = paint.measureText(
956 kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
958 // Then calculate how much of the text can be drawn with the '...' appended
959 // to the end of the string.
960 SkScalar sk_max_original_text_width(
961 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
962 size_t sk_max_original_text_length = paint.breakText(
963 remote_playback_message.c_str(),
964 remote_playback_message.size(),
965 sk_max_original_text_width);
967 // Remove the part of the string that doesn't fit and append '...'.
968 display_text.erase(sk_max_original_text_length,
969 remote_playback_message.size() - sk_max_original_text_length);
970 display_text.append(kTruncationEllipsis);
971 display_text_width = paint.measureText(
972 display_text.c_str(), display_text.size());
975 // Center the text horizontally.
976 SkScalar sk_horizontal_margin =
977 (canvas_size.width() - display_text_width) / 2.0;
978 canvas.drawText(display_text.c_str(),
980 sk_horizontal_margin,
984 GLES2Interface* gl = stream_texture_factory_->ContextGL();
985 GLuint remote_playback_texture_id = 0;
986 gl->GenTextures(1, &remote_playback_texture_id);
987 GLuint texture_target = GL_TEXTURE_2D;
988 gl->BindTexture(texture_target, remote_playback_texture_id);
989 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
990 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
991 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
992 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
995 SkAutoLockPixels lock(bitmap);
996 gl->TexImage2D(texture_target,
998 GL_RGBA /* internalformat */,
1002 GL_RGBA /* format */,
1003 GL_UNSIGNED_BYTE /* type */,
1004 bitmap.getPixels());
1007 gpu::Mailbox texture_mailbox;
1008 gl->GenMailboxCHROMIUM(texture_mailbox.name);
1009 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
1011 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1013 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1014 make_scoped_ptr(new gpu::MailboxHolder(
1015 texture_mailbox, texture_target, texture_mailbox_sync_point)),
1016 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture,
1017 stream_texture_factory_,
1018 remote_playback_texture_id)),
1019 canvas_size /* coded_size */,
1020 gfx::Rect(canvas_size) /* visible_rect */,
1021 canvas_size /* natural_size */,
1022 base::TimeDelta() /* timestamp */,
1023 VideoFrame::ReadPixelsCB());
1024 SetCurrentFrameInternal(new_frame);
1027 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
1028 if (needs_external_surface_) {
1029 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
1030 #if defined(VIDEO_HOLE)
1031 if (!natural_size_.isEmpty()) {
1032 scoped_refptr<VideoFrame> new_frame =
1033 VideoFrame::CreateHoleFrame(natural_size_);
1034 SetCurrentFrameInternal(new_frame);
1035 // Force the client to grab the hole frame.
1039 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
1040 #endif // defined(VIDEO_HOLE)
1041 } else if (!is_remote_ && texture_id_) {
1042 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1043 GLuint texture_id_ref = 0;
1044 gl->GenTextures(1, &texture_id_ref);
1045 GLuint texture_target = kGLTextureExternalOES;
1046 gl->BindTexture(texture_target, texture_id_ref);
1047 gl->ConsumeTextureCHROMIUM(texture_target, texture_mailbox_.name);
1049 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1051 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1052 make_scoped_ptr(new gpu::MailboxHolder(
1053 texture_mailbox_, texture_target, texture_mailbox_sync_point)),
1054 media::BindToCurrentLoop(base::Bind(
1055 &OnReleaseTexture, stream_texture_factory_, texture_id_ref)),
1057 gfx::Rect(natural_size_),
1060 VideoFrame::ReadPixelsCB());
1061 SetCurrentFrameInternal(new_frame);
1065 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1066 cc::VideoFrameProvider::Client* client) {
1067 // This is called from both the main renderer thread and the compositor
1068 // thread (when the main thread is blocked).
1069 if (video_frame_provider_client_)
1070 video_frame_provider_client_->StopUsingProvider();
1071 video_frame_provider_client_ = client;
1073 // Set the callback target when a frame is produced.
1074 if (stream_texture_proxy_)
1075 stream_texture_proxy_->SetClient(client);
1078 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1079 scoped_refptr<media::VideoFrame>& video_frame) {
1080 base::AutoLock auto_lock(current_frame_lock_);
1081 current_frame_ = video_frame;
1084 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1085 scoped_refptr<VideoFrame> video_frame;
1087 base::AutoLock auto_lock(current_frame_lock_);
1088 video_frame = current_frame_;
1091 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
1092 stream_id_ && !needs_external_surface_ && !is_remote_) {
1093 gfx::Size natural_size = video_frame->natural_size();
1094 // TODO(sievers): These variables are accessed on the wrong thread here.
1095 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1096 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1097 stream_texture_proxy_initialized_ = true;
1098 cached_stream_texture_size_ = natural_size;
1104 void WebMediaPlayerAndroid::PutCurrentFrame(
1105 const scoped_refptr<media::VideoFrame>& frame) {
1108 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1110 if (stream_texture_proxy_)
1113 // No factory to create proxy.
1114 if (!stream_texture_factory_)
1117 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1118 if (needs_establish_peer_ && stream_texture_proxy_) {
1119 DoCreateStreamTexture();
1120 ReallocateVideoFrame();
1123 if (stream_texture_proxy_ && video_frame_provider_client_)
1124 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1127 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1128 if (!stream_texture_proxy_)
1131 if (stream_texture_factory_.get() && stream_id_)
1132 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1133 needs_establish_peer_ = false;
1136 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1137 DCHECK(!stream_id_);
1138 DCHECK(!texture_id_);
1139 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1140 kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
1143 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1144 needs_establish_peer_ = needs_establish_peer;
1147 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1148 manager_->SetPoster(player_id_, poster);
1151 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1152 is_playing_ = is_playing;
1156 delegate_->DidPlay(this);
1158 delegate_->DidPause(this);
1161 #if defined(VIDEO_HOLE)
1162 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1163 if (!video_weblayer_)
1166 // Compute the geometry of video frame layer.
1167 cc::Layer* layer = video_weblayer_->layer();
1168 gfx::RectF rect(layer->bounds());
1170 rect.Offset(layer->position().OffsetFromOrigin());
1171 layer = layer->parent();
1174 // Return false when the geometry hasn't been changed from the last time.
1175 if (last_computed_rect_ == rect)
1178 // Store the changed geometry information when it is actually changed.
1179 last_computed_rect_ = rect;
1183 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1184 return last_computed_rect_;
1188 // The following EME related code is copied from WebMediaPlayerImpl.
1189 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1190 // WebMediaPlayerImpl.
1192 // Convert a WebString to ASCII, falling back on an empty string in the case
1193 // of a non-ASCII string.
1194 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
1195 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
1199 // Helper functions to report media EME related stats to UMA. They follow the
1200 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1201 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1202 // that UMA_* macros require the names to be constant throughout the process'
1205 static void EmeUMAHistogramEnumeration(const std::string& key_system,
1206 const std::string& method,
1208 int boundary_value) {
1209 base::LinearHistogram::FactoryGet(
1210 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1211 1, boundary_value, boundary_value + 1,
1212 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1215 static void EmeUMAHistogramCounts(const std::string& key_system,
1216 const std::string& method,
1218 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1219 base::Histogram::FactoryGet(
1220 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1221 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1224 // Helper enum for reporting generateKeyRequest/addKey histograms.
1225 enum MediaKeyException {
1228 kKeySystemNotSupported,
1229 kInvalidPlayerState,
1230 kMaxMediaKeyException
1233 static MediaKeyException MediaKeyExceptionForUMA(
1234 WebMediaPlayer::MediaKeyException e) {
1236 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1237 return kKeySystemNotSupported;
1238 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1239 return kInvalidPlayerState;
1240 case WebMediaPlayer::MediaKeyExceptionNoError:
1243 return kUnknownResultId;
1247 // Helper for converting |key_system| name and exception |e| to a pair of enum
1248 // values from above, for reporting to UMA.
1249 static void ReportMediaKeyExceptionToUMA(const std::string& method,
1250 const std::string& key_system,
1251 WebMediaPlayer::MediaKeyException e) {
1252 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1253 DCHECK_NE(result_id, kUnknownResultId) << e;
1254 EmeUMAHistogramEnumeration(
1255 key_system, method, result_id, kMaxMediaKeyException);
1258 bool WebMediaPlayerAndroid::IsKeySystemSupported(
1259 const std::string& key_system) {
1260 // On Android, EME only works with MSE.
1261 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1262 IsConcreteSupportedKeySystem(key_system);
1265 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1266 const WebString& key_system,
1267 const unsigned char* init_data,
1268 unsigned init_data_length) {
1269 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
1270 << std::string(reinterpret_cast<const char*>(init_data),
1271 static_cast<size_t>(init_data_length));
1273 std::string ascii_key_system =
1274 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1276 WebMediaPlayer::MediaKeyException e =
1277 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
1278 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
1282 // Guess the type of |init_data|. This is only used to handle some corner cases
1283 // so we keep it as simple as possible without breaking major use cases.
1284 static std::string GuessInitDataType(const unsigned char* init_data,
1285 unsigned init_data_length) {
1286 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
1287 if (init_data_length == 16)
1288 return "video/webm";
1293 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1294 // not enabled. Currently the player just doesn't start and waits for
1296 WebMediaPlayer::MediaKeyException
1297 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1298 const std::string& key_system,
1299 const unsigned char* init_data,
1300 unsigned init_data_length) {
1301 if (!IsKeySystemSupported(key_system))
1302 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1304 // We do not support run-time switching between key systems for now.
1305 if (current_key_system_.empty()) {
1306 if (!proxy_decryptor_) {
1307 proxy_decryptor_.reset(new ProxyDecryptor(
1309 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1310 weak_factory_.GetWeakPtr()),
1311 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1312 weak_factory_.GetWeakPtr()),
1313 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1314 weak_factory_.GetWeakPtr())));
1317 GURL security_origin(frame_->document().securityOrigin().toString());
1318 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
1319 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1321 if (!decryptor_ready_cb_.is_null()) {
1322 base::ResetAndReturn(&decryptor_ready_cb_)
1323 .Run(proxy_decryptor_->GetDecryptor());
1326 manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId());
1327 current_key_system_ = key_system;
1328 } else if (key_system != current_key_system_) {
1329 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1332 std::string init_data_type = init_data_type_;
1333 if (init_data_type.empty())
1334 init_data_type = GuessInitDataType(init_data, init_data_length);
1336 // TODO(xhwang): We assume all streams are from the same container (thus have
1337 // the same "type") for now. In the future, the "type" should be passed down
1338 // from the application.
1339 if (!proxy_decryptor_->GenerateKeyRequest(
1340 init_data_type, init_data, init_data_length)) {
1341 current_key_system_.clear();
1342 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1345 return WebMediaPlayer::MediaKeyExceptionNoError;
1348 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1349 const WebString& key_system,
1350 const unsigned char* key,
1351 unsigned key_length,
1352 const unsigned char* init_data,
1353 unsigned init_data_length,
1354 const WebString& session_id) {
1355 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
1356 << std::string(reinterpret_cast<const char*>(key),
1357 static_cast<size_t>(key_length)) << ", "
1358 << std::string(reinterpret_cast<const char*>(init_data),
1359 static_cast<size_t>(init_data_length)) << " ["
1360 << base::string16(session_id) << "]";
1362 std::string ascii_key_system =
1363 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1364 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1366 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
1372 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
1376 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1377 const std::string& key_system,
1378 const unsigned char* key,
1379 unsigned key_length,
1380 const unsigned char* init_data,
1381 unsigned init_data_length,
1382 const std::string& session_id) {
1384 DCHECK_GT(key_length, 0u);
1386 if (!IsKeySystemSupported(key_system))
1387 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1389 if (current_key_system_.empty() || key_system != current_key_system_)
1390 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1392 proxy_decryptor_->AddKey(
1393 key, key_length, init_data, init_data_length, session_id);
1394 return WebMediaPlayer::MediaKeyExceptionNoError;
1397 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1398 const WebString& key_system,
1399 const WebString& session_id) {
1400 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
1401 << " [" << base::string16(session_id) << "]";
1403 std::string ascii_key_system =
1404 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1405 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1407 WebMediaPlayer::MediaKeyException e =
1408 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
1409 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
1413 WebMediaPlayer::MediaKeyException
1414 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
1415 const std::string& session_id) {
1416 if (!IsKeySystemSupported(key_system))
1417 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1419 if (current_key_system_.empty() || key_system != current_key_system_)
1420 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1422 proxy_decryptor_->CancelKeyRequest(session_id);
1423 return WebMediaPlayer::MediaKeyExceptionNoError;
1426 void WebMediaPlayerAndroid::setContentDecryptionModule(
1427 blink::WebContentDecryptionModule* cdm) {
1428 DCHECK(main_thread_checker_.CalledOnValidThread());
1430 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1434 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1438 if (!decryptor_ready_cb_.is_null())
1439 base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
1441 manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1444 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1445 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1448 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1449 WebString::fromUTF8(session_id));
1452 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1453 media::MediaKeys::KeyError error_code,
1454 uint32 system_code) {
1455 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1456 error_code, media::MediaKeys::kMaxKeyError);
1458 unsigned short short_system_code = 0;
1459 if (system_code > std::numeric_limits<unsigned short>::max()) {
1460 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1461 short_system_code = std::numeric_limits<unsigned short>::max();
1463 short_system_code = static_cast<unsigned short>(system_code);
1467 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1468 WebString::fromUTF8(session_id),
1469 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1473 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1474 const std::vector<uint8>& message,
1475 const std::string& destination_url) {
1476 const GURL destination_url_gurl(destination_url);
1477 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid())
1478 << "Invalid URL in destination_url: " << destination_url;
1480 client_->keyMessage(
1481 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1482 WebString::fromUTF8(session_id),
1483 message.empty() ? NULL : &message[0],
1485 destination_url_gurl);
1488 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1489 blink::WebMediaSource* web_media_source) {
1490 client_->mediaSourceOpened(web_media_source);
1493 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1494 const std::vector<uint8>& init_data) {
1495 DCHECK(main_thread_checker_.CalledOnValidThread());
1497 // Do not fire NeedKey event if encrypted media is not enabled.
1498 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1499 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1503 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1505 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1506 if (init_data_type_.empty())
1507 init_data_type_ = type;
1509 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1511 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1514 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1515 const media::DecryptorReadyCB& decryptor_ready_cb) {
1516 DCHECK(main_thread_checker_.CalledOnValidThread());
1518 // Cancels the previous decryptor request.
1519 if (decryptor_ready_cb.is_null()) {
1520 if (!decryptor_ready_cb_.is_null())
1521 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1525 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1526 // video and audio). The current implementation is okay for the current
1527 // media pipeline since we initialize audio and video decoders in sequence.
1528 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1530 DCHECK(decryptor_ready_cb_.is_null());
1532 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1533 DCHECK(!proxy_decryptor_ || !web_cdm_);
1535 if (proxy_decryptor_) {
1536 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1541 decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
1545 decryptor_ready_cb_ = decryptor_ready_cb;
1548 void WebMediaPlayerAndroid::enterFullscreen() {
1549 if (manager_->CanEnterFullscreen(frame_)) {
1550 manager_->EnterFullscreen(player_id_, frame_);
1551 SetNeedsEstablishPeer(false);
1555 void WebMediaPlayerAndroid::exitFullscreen() {
1556 manager_->ExitFullscreen(player_id_);
1559 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1560 return manager_->CanEnterFullscreen(frame_);
1563 } // namespace content