1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/android/webmediaplayer_android.h"
10 #include "base/android/build_info.h"
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "cc/layers/video_layer.h"
21 #include "content/public/common/content_client.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/renderer/render_frame.h"
24 #include "content/renderer/compositor_bindings/web_layer_impl.h"
25 #include "content/renderer/media/android/renderer_demuxer_android.h"
26 #include "content/renderer/media/android/renderer_media_player_manager.h"
27 #include "content/renderer/media/crypto/key_systems.h"
28 #include "content/renderer/media/crypto/renderer_cdm_manager.h"
29 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
30 #include "content/renderer/media/webmediaplayer_delegate.h"
31 #include "content/renderer/media/webmediaplayer_util.h"
32 #include "content/renderer/render_frame_impl.h"
33 #include "content/renderer/render_thread_impl.h"
34 #include "gpu/GLES2/gl2extchromium.h"
35 #include "gpu/command_buffer/client/gles2_interface.h"
36 #include "gpu/command_buffer/common/mailbox_holder.h"
37 #include "media/base/android/media_player_android.h"
38 #include "media/base/bind_to_current_loop.h"
39 // TODO(xhwang): Remove when we remove prefixed EME implementation.
40 #include "media/base/media_keys.h"
41 #include "media/base/media_switches.h"
42 #include "media/base/video_frame.h"
43 #include "net/base/mime_util.h"
44 #include "third_party/WebKit/public/platform/Platform.h"
45 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
46 #include "third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h"
47 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
48 #include "third_party/WebKit/public/platform/WebString.h"
49 #include "third_party/WebKit/public/platform/WebURL.h"
50 #include "third_party/WebKit/public/web/WebDocument.h"
51 #include "third_party/WebKit/public/web/WebFrame.h"
52 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
53 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
54 #include "third_party/WebKit/public/web/WebView.h"
55 #include "third_party/skia/include/core/SkCanvas.h"
56 #include "third_party/skia/include/core/SkPaint.h"
57 #include "third_party/skia/include/core/SkTypeface.h"
58 #include "ui/gfx/image/image.h"
60 static const uint32 kGLTextureExternalOES = 0x8D65;
61 static const int kSDKVersionToSupportSecurityOriginCheck = 20;
63 using blink::WebMediaPlayer;
65 using blink::WebString;
66 using blink::WebTimeRanges;
68 using gpu::gles2::GLES2Interface;
69 using media::MediaPlayerAndroid;
70 using media::VideoFrame;
73 // Prefix for histograms related to Encrypted Media Extensions.
74 const char* kMediaEme = "Media.EME.";
76 // File-static function is to allow it to run even after WMPA is deleted.
77 void OnReleaseTexture(
78 const scoped_refptr<content::StreamTextureFactory>& factories,
80 uint32 release_sync_point) {
81 GLES2Interface* gl = factories->ContextGL();
82 gl->WaitSyncPointCHROMIUM(release_sync_point);
83 gl->DeleteTextures(1, &texture_id);
86 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
88 explicit SyncPointClientImpl(
89 blink::WebGraphicsContext3D* web_graphics_context)
90 : web_graphics_context_(web_graphics_context) {}
91 virtual ~SyncPointClientImpl() {}
92 virtual uint32 InsertSyncPoint() OVERRIDE {
93 return web_graphics_context_->insertSyncPoint();
95 virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
96 web_graphics_context_->waitSyncPoint(sync_point);
100 blink::WebGraphicsContext3D* web_graphics_context_;
103 // Used for calls to decryptor_ready_cb_ where the result can be ignored.
104 void DoNothing(bool) {
111 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
112 blink::WebFrame* frame,
113 blink::WebMediaPlayerClient* client,
114 base::WeakPtr<WebMediaPlayerDelegate> delegate,
115 RendererMediaPlayerManager* player_manager,
116 RendererCdmManager* cdm_manager,
117 scoped_refptr<StreamTextureFactory> factory,
118 const scoped_refptr<base::MessageLoopProxy>& media_loop,
119 media::MediaLog* media_log)
120 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
124 buffered_(static_cast<size_t>(1)),
125 media_loop_(media_loop),
126 ignore_metadata_duration_change_(false),
127 pending_seek_(false),
129 did_loading_progress_(false),
130 player_manager_(player_manager),
131 cdm_manager_(cdm_manager),
132 network_state_(WebMediaPlayer::NetworkStateEmpty),
133 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
137 needs_establish_peer_(true),
138 stream_texture_proxy_initialized_(false),
139 has_size_info_(false),
140 stream_texture_factory_(factory),
141 needs_external_surface_(false),
142 video_frame_provider_client_(NULL),
143 pending_playback_(false),
144 player_type_(MEDIA_PLAYER_TYPE_URL),
147 media_log_(media_log),
149 allow_stored_credentials_(false),
150 weak_factory_(this) {
151 DCHECK(player_manager_);
152 DCHECK(cdm_manager_);
154 DCHECK(main_thread_checker_.CalledOnValidThread());
156 player_id_ = player_manager_->RegisterMediaPlayer(this);
158 #if defined(VIDEO_HOLE)
159 force_use_overlay_embedded_video_ = CommandLine::ForCurrentProcess()->
160 HasSwitch(switches::kForceUseOverlayEmbeddedVideo);
161 if (force_use_overlay_embedded_video_ ||
162 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo()) {
163 // Defer stream texture creation until we are sure it's necessary.
164 needs_establish_peer_ = false;
165 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
167 #endif // defined(VIDEO_HOLE)
168 TryCreateStreamTextureProxyIfNeeded();
171 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
172 DCHECK(main_thread_checker_.CalledOnValidThread());
173 SetVideoFrameProviderClient(NULL);
174 client_->setWebLayer(NULL);
176 if (player_manager_) {
177 player_manager_->DestroyPlayer(player_id_);
178 player_manager_->UnregisterMediaPlayer(player_id_);
182 GLES2Interface* gl = stream_texture_factory_->ContextGL();
183 gl->DeleteTextures(1, &texture_id_);
185 texture_mailbox_ = gpu::Mailbox();
190 base::AutoLock auto_lock(current_frame_lock_);
191 current_frame_ = NULL;
194 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
195 delegate_->PlayerGone(this);
198 void WebMediaPlayerAndroid::load(LoadType load_type,
199 const blink::WebURL& url,
200 CORSMode cors_mode) {
201 DCHECK(main_thread_checker_.CalledOnValidThread());
202 ReportMediaSchemeUma(GURL(url));
206 player_type_ = MEDIA_PLAYER_TYPE_URL;
209 case LoadTypeMediaSource:
210 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
213 case LoadTypeMediaStream:
214 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
220 int demuxer_client_id = 0;
221 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
222 RendererDemuxerAndroid* demuxer =
223 RenderThreadImpl::current()->renderer_demuxer();
224 demuxer_client_id = demuxer->GetNextDemuxerClientID();
226 media_source_delegate_.reset(new MediaSourceDelegate(
227 demuxer, demuxer_client_id, media_loop_, media_log_));
229 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
230 media::SetDecryptorReadyCB set_decryptor_ready_cb =
231 media::BindToCurrentLoop(
232 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
233 weak_factory_.GetWeakPtr()));
235 media_source_delegate_->InitializeMediaSource(
236 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
237 weak_factory_.GetWeakPtr()),
238 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
239 weak_factory_.GetWeakPtr()),
240 set_decryptor_ready_cb,
241 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
242 weak_factory_.GetWeakPtr()),
243 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
244 weak_factory_.GetWeakPtr()));
245 InitializePlayer(url_, frame_->document().firstPartyForCookies(),
246 true, demuxer_client_id);
253 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
254 weak_factory_.GetWeakPtr())));
255 info_loader_->Start(frame_);
258 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
259 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
262 void WebMediaPlayerAndroid::DidLoadMediaInfo(
263 MediaInfoLoader::Status status,
264 const GURL& redirected_url,
265 const GURL& first_party_for_cookies,
266 bool allow_stored_credentials) {
267 DCHECK(main_thread_checker_.CalledOnValidThread());
268 DCHECK(!media_source_delegate_);
269 if (status == MediaInfoLoader::kFailed) {
270 info_loader_.reset();
271 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
276 redirected_url, first_party_for_cookies, allow_stored_credentials, 0);
278 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
281 void WebMediaPlayerAndroid::play() {
282 DCHECK(main_thread_checker_.CalledOnValidThread());
283 #if defined(VIDEO_HOLE)
284 if (hasVideo() && needs_external_surface_ &&
285 !player_manager_->IsInFullscreen(frame_)) {
286 DCHECK(!needs_establish_peer_);
287 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
289 #endif // defined(VIDEO_HOLE)
291 TryCreateStreamTextureProxyIfNeeded();
292 // There is no need to establish the surface texture peer for fullscreen
294 if (hasVideo() && needs_establish_peer_ &&
295 !player_manager_->IsInFullscreen(frame_)) {
296 EstablishSurfaceTexturePeer();
300 player_manager_->Start(player_id_);
301 UpdatePlayingState(true);
302 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
305 void WebMediaPlayerAndroid::pause() {
306 DCHECK(main_thread_checker_.CalledOnValidThread());
310 void WebMediaPlayerAndroid::seek(double seconds) {
311 DCHECK(main_thread_checker_.CalledOnValidThread());
312 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
314 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
317 if (new_seek_time == seek_time_) {
318 if (media_source_delegate_) {
319 if (!pending_seek_) {
320 // If using media source demuxer, only suppress redundant seeks if
321 // there is no pending seek. This enforces that any pending seek that
322 // results in a demuxer seek is preceded by matching
323 // CancelPendingSeek() and StartWaitingForSeek() calls.
327 // Suppress all redundant seeks if unrestricted by media source
329 pending_seek_ = false;
334 pending_seek_ = true;
335 pending_seek_time_ = new_seek_time;
337 if (media_source_delegate_)
338 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
340 // Later, OnSeekComplete will trigger the pending seek.
345 seek_time_ = new_seek_time;
347 if (media_source_delegate_)
348 media_source_delegate_->StartWaitingForSeek(seek_time_);
350 // Kick off the asynchronous seek!
351 player_manager_->Seek(player_id_, seek_time_);
354 bool WebMediaPlayerAndroid::supportsSave() const {
358 void WebMediaPlayerAndroid::setRate(double rate) {
362 void WebMediaPlayerAndroid::setVolume(double volume) {
363 DCHECK(main_thread_checker_.CalledOnValidThread());
364 player_manager_->SetVolume(player_id_, volume);
367 bool WebMediaPlayerAndroid::hasVideo() const {
368 DCHECK(main_thread_checker_.CalledOnValidThread());
369 // If we have obtained video size information before, use it.
371 return !natural_size_.isEmpty();
373 // TODO(qinmin): need a better method to determine whether the current media
374 // content contains video. Android does not provide any function to do
376 // We don't know whether the current media content has video unless
377 // the player is prepared. If the player is not prepared, we fall back
378 // to the mime-type. There may be no mime-type on a redirect URL.
379 // In that case, we conservatively assume it contains video so that
380 // enterfullscreen call will not fail.
381 if (!url_.has_path())
384 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
386 return mime.find("audio/") == std::string::npos;
389 bool WebMediaPlayerAndroid::hasAudio() const {
390 DCHECK(main_thread_checker_.CalledOnValidThread());
391 if (!url_.has_path())
394 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
397 if (mime.find("audio/") != std::string::npos ||
398 mime.find("video/") != std::string::npos ||
399 mime.find("application/ogg") != std::string::npos) {
405 bool WebMediaPlayerAndroid::paused() const {
409 bool WebMediaPlayerAndroid::seeking() const {
413 double WebMediaPlayerAndroid::duration() const {
414 DCHECK(main_thread_checker_.CalledOnValidThread());
415 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
416 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
417 return std::numeric_limits<double>::quiet_NaN();
419 if (duration_ == media::kInfiniteDuration())
420 return std::numeric_limits<double>::infinity();
422 return duration_.InSecondsF();
425 double WebMediaPlayerAndroid::timelineOffset() const {
426 DCHECK(main_thread_checker_.CalledOnValidThread());
427 base::Time timeline_offset;
428 if (media_source_delegate_)
429 timeline_offset = media_source_delegate_->GetTimelineOffset();
431 if (timeline_offset.is_null())
432 return std::numeric_limits<double>::quiet_NaN();
434 return timeline_offset.ToJsTime();
437 double WebMediaPlayerAndroid::currentTime() const {
438 DCHECK(main_thread_checker_.CalledOnValidThread());
439 // If the player is processing a seek, return the seek time.
440 // Blink may still query us if updatePlaybackState() occurs while seeking.
442 return pending_seek_ ?
443 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
446 return current_time_;
449 WebSize WebMediaPlayerAndroid::naturalSize() const {
450 return natural_size_;
453 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
454 return network_state_;
457 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
461 WebTimeRanges WebMediaPlayerAndroid::buffered() const {
462 if (media_source_delegate_)
463 return media_source_delegate_->Buffered();
467 double WebMediaPlayerAndroid::maxTimeSeekable() const {
468 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
469 // return 0 so that the seekable range is empty.
470 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
476 bool WebMediaPlayerAndroid::didLoadingProgress() {
477 bool ret = did_loading_progress_;
478 did_loading_progress_ = false;
482 bool WebMediaPlayerAndroid::EnsureTextureBackedSkBitmap(GrContext* gr,
485 GrSurfaceOrigin origin,
486 GrPixelConfig config) {
487 DCHECK(main_thread_checker_.CalledOnValidThread());
488 if (!bitmap.getTexture() || bitmap.width() != size.width
489 || bitmap.height() != size.height) {
493 desc.fConfig = config;
494 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
496 desc.fOrigin = origin;
497 desc.fWidth = size.width;
498 desc.fHeight = size.height;
499 skia::RefPtr<GrTexture> texture;
500 texture = skia::AdoptRef(gr->createUncachedTexture(desc, 0, 0));
504 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight);
505 SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get()));
508 bitmap.setInfo(info);
509 bitmap.setPixelRef(pixelRef)->unref();
515 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
516 const blink::WebRect& rect,
517 unsigned char alpha) {
518 DCHECK(main_thread_checker_.CalledOnValidThread());
519 scoped_ptr<blink::WebGraphicsContext3DProvider> provider =
520 scoped_ptr<blink::WebGraphicsContext3DProvider>(blink::Platform::current(
521 )->createSharedOffscreenGraphicsContext3DProvider());
524 blink::WebGraphicsContext3D* context3D = provider->context3d();
525 if (!context3D || !context3D->makeContextCurrent())
528 // Copy video texture into a RGBA texture based bitmap first as video texture
529 // on Android is GL_TEXTURE_EXTERNAL_OES which is not supported by Skia yet.
530 // The bitmap's size needs to be the same as the video and use naturalSize()
531 // here. Check if we could reuse existing texture based bitmap.
532 // Otherwise, release existing texture based bitmap and allocate
533 // a new one based on video size.
534 if (!EnsureTextureBackedSkBitmap(provider->grContext(), bitmap_,
535 naturalSize(), kTopLeft_GrSurfaceOrigin, kSkia8888_GrPixelConfig)) {
539 unsigned textureId = static_cast<unsigned>(
540 (bitmap_.getTexture())->getTextureHandle());
541 if (!copyVideoTextureToPlatformTexture(context3D, textureId, 0,
542 GL_RGBA, GL_UNSIGNED_BYTE, true, false)) {
546 // Draw the texture based bitmap onto the Canvas. If the canvas is
547 // hardware based, this will do a GPU-GPU texture copy.
548 // If the canvas is software based, the texture based bitmap will be
549 // readbacked to system memory then draw onto the canvas.
551 dest.set(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
553 paint.setAlpha(alpha);
554 // It is not necessary to pass the dest into the drawBitmap call since all
555 // the context have been set up before calling paintCurrentFrameInContext.
556 canvas->drawBitmapRect(bitmap_, 0, dest, &paint);
559 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
560 blink::WebGraphicsContext3D* web_graphics_context,
561 unsigned int texture,
563 unsigned int internal_format,
565 bool premultiply_alpha,
567 DCHECK(main_thread_checker_.CalledOnValidThread());
568 // Don't allow clients to copy an encrypted video frame.
569 if (needs_external_surface_)
572 scoped_refptr<VideoFrame> video_frame;
574 base::AutoLock auto_lock(current_frame_lock_);
575 video_frame = current_frame_;
579 video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
581 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
582 DCHECK((!is_remote_ &&
583 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) ||
584 (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D));
586 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
588 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
589 // an invalid texture target may be used for copy texture.
590 uint32 src_texture = web_graphics_context->createAndConsumeTextureCHROMIUM(
591 mailbox_holder->texture_target, mailbox_holder->mailbox.name);
593 // The video is stored in an unmultiplied format, so premultiply if
595 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
598 // Application itself needs to take care of setting the right flip_y
599 // value down to get the expected result.
600 // flip_y==true means to reverse the video orientation while
601 // flip_y==false means to keep the intrinsic orientation.
602 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
603 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, src_texture,
604 texture, level, internal_format,
606 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
607 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
610 web_graphics_context->deleteTexture(src_texture);
611 web_graphics_context->flush();
613 SyncPointClientImpl client(web_graphics_context);
614 video_frame->UpdateReleaseSyncPoint(&client);
618 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
619 DCHECK(main_thread_checker_.CalledOnValidThread());
620 if (player_type_ != MEDIA_PLAYER_TYPE_URL)
623 if (!info_loader_ || !info_loader_->HasSingleOrigin())
626 // TODO(qinmin): The url might be redirected when android media player
627 // requests the stream. As a result, we cannot guarantee there is only
628 // a single origin. Only if the HTTP request was made without credentials,
629 // we will honor the return value from HasSingleSecurityOriginInternal()
630 // in pre-L android versions.
631 // Check http://crbug.com/334204.
632 if (!allow_stored_credentials_)
635 return base::android::BuildInfo::GetInstance()->sdk_int() >=
636 kSDKVersionToSupportSecurityOriginCheck;
639 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
640 DCHECK(main_thread_checker_.CalledOnValidThread());
642 return info_loader_->DidPassCORSAccessCheck();
646 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
647 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
650 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
651 if (media_source_delegate_)
652 return media_source_delegate_->DecodedFrameCount();
657 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
658 if (media_source_delegate_)
659 return media_source_delegate_->DroppedFrameCount();
664 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
665 if (media_source_delegate_)
666 return media_source_delegate_->AudioDecodedByteCount();
671 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
672 if (media_source_delegate_)
673 return media_source_delegate_->VideoDecodedByteCount();
678 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
679 const base::TimeDelta& duration, int width, int height, bool success) {
680 DCHECK(main_thread_checker_.CalledOnValidThread());
681 bool need_to_signal_duration_changed = false;
683 if (url_.SchemeIs("file") || url_.SchemeIs("app"))
684 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
686 // Update duration, if necessary, prior to ready state updates that may
687 // cause duration() query.
688 if (!ignore_metadata_duration_change_ && duration_ != duration) {
689 duration_ = duration;
691 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
692 // already triggers a durationchanged event. If this is a different
693 // transition, remember to signal durationchanged.
694 // Do not ever signal durationchanged on metadata change in MSE case
695 // because OnDurationChanged() handles this.
696 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
697 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
698 need_to_signal_duration_changed = true;
702 if (ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
703 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
704 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
707 // TODO(wolenetz): Should we just abort early and set network state to an
708 // error if success == false? See http://crbug.com/248399
710 OnVideoSizeChanged(width, height);
712 if (need_to_signal_duration_changed)
713 client_->durationChanged();
716 void WebMediaPlayerAndroid::OnPlaybackComplete() {
717 // When playback is about to finish, android media player often stops
718 // at a time which is smaller than the duration. This makes webkit never
719 // know that the playback has finished. To solve this, we set the
720 // current time to media duration when OnPlaybackComplete() get called.
721 OnTimeUpdate(duration_);
722 client_->timeChanged();
724 // if the loop attribute is set, timeChanged() will update the current time
725 // to 0. It will perform a seek to 0. As the requests to the renderer
726 // process are sequential, the OnSeekComplete() will only occur
727 // once OnPlaybackComplete() is done. As the playback can only be executed
728 // upon completion of OnSeekComplete(), the request needs to be saved.
730 if (seeking_ && seek_time_ == base::TimeDelta())
731 pending_playback_ = true;
734 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
735 buffered_[0].end = duration() * percentage / 100;
736 did_loading_progress_ = true;
739 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
740 DCHECK(main_thread_checker_.CalledOnValidThread());
741 client_->requestSeek(time_to_seek.InSecondsF());
744 void WebMediaPlayerAndroid::OnSeekComplete(
745 const base::TimeDelta& current_time) {
746 DCHECK(main_thread_checker_.CalledOnValidThread());
749 pending_seek_ = false;
750 seek(pending_seek_time_.InSecondsF());
754 OnTimeUpdate(current_time);
756 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
758 client_->timeChanged();
760 if (pending_playback_) {
762 pending_playback_ = false;
766 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
767 switch (error_type) {
768 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
769 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
771 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
772 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
774 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
775 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
777 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
783 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
784 DCHECK(main_thread_checker_.CalledOnValidThread());
785 has_size_info_ = true;
786 if (natural_size_.width == width && natural_size_.height == height)
789 #if defined(VIDEO_HOLE)
790 // Use H/W surface for encrypted video.
791 // TODO(qinmin): Change this so that only EME needs the H/W surface
792 if (force_use_overlay_embedded_video_ ||
793 (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted() &&
794 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo())) {
795 needs_external_surface_ = true;
796 if (!paused() && !player_manager_->IsInFullscreen(frame_))
797 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
798 } else if (stream_texture_proxy_ && !stream_id_) {
799 // Do deferred stream texture creation finally.
800 DoCreateStreamTexture();
801 SetNeedsEstablishPeer(true);
803 #endif // defined(VIDEO_HOLE)
804 natural_size_.width = width;
805 natural_size_.height = height;
807 // When play() gets called, |natural_size_| may still be empty and
808 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
809 // may play without a surface texture. When we finally get the valid video
810 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
811 // previously called.
812 if (!paused() && needs_establish_peer_)
813 EstablishSurfaceTexturePeer();
815 ReallocateVideoFrame();
817 // For hidden video element (with style "display:none"), ensure the texture
819 if (!is_remote_ && cached_stream_texture_size_ != natural_size_) {
820 stream_texture_factory_->SetStreamTextureSize(
821 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
822 cached_stream_texture_size_ = natural_size_;
825 // Lazily allocate compositing layer.
826 if (!video_weblayer_) {
827 video_weblayer_.reset(new WebLayerImpl(
828 cc::VideoLayer::Create(this, media::VIDEO_ROTATION_0)));
829 client_->setWebLayer(video_weblayer_.get());
832 // TODO(qinmin): This is a hack. We need the media element to stop showing the
833 // poster image by forcing it to call setDisplayMode(video). Should move the
834 // logic into HTMLMediaElement.cpp.
835 client_->timeChanged();
838 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
839 DCHECK(main_thread_checker_.CalledOnValidThread());
840 current_time_ = current_time.InSecondsF();
843 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
844 const std::string& remote_playback_message) {
845 DCHECK(main_thread_checker_.CalledOnValidThread());
846 DCHECK(!media_source_delegate_);
847 DrawRemotePlaybackText(remote_playback_message);
849 SetNeedsEstablishPeer(false);
852 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
853 DCHECK(main_thread_checker_.CalledOnValidThread());
854 DCHECK(!media_source_delegate_);
855 SetNeedsEstablishPeer(true);
857 EstablishSurfaceTexturePeer();
859 ReallocateVideoFrame();
862 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
863 if (!player_manager_->IsInFullscreen(frame_))
864 player_manager_->DidEnterFullscreen(frame_);
867 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
868 // |needs_external_surface_| is always false on non-TV devices.
869 if (!needs_external_surface_)
870 SetNeedsEstablishPeer(true);
871 // We had the fullscreen surface connected to Android MediaPlayer,
872 // so reconnect our surface texture for embedded playback.
873 if (!paused() && needs_establish_peer_)
874 EstablishSurfaceTexturePeer();
876 #if defined(VIDEO_HOLE)
877 if (!paused() && needs_external_surface_)
878 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
879 #endif // defined(VIDEO_HOLE)
881 player_manager_->DidExitFullscreen();
885 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
886 UpdatePlayingState(true);
887 client_->playbackStateChanged();
890 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
891 UpdatePlayingState(false);
892 client_->playbackStateChanged();
895 void WebMediaPlayerAndroid::OnRequestFullscreen() {
896 client_->requestFullscreen();
899 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
900 DCHECK(main_thread_checker_.CalledOnValidThread());
901 // Only MSE |player_type_| registers this callback.
902 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
904 // Cache the new duration value and trust it over any subsequent duration
905 // values received in OnMediaMetadataChanged().
906 duration_ = duration;
907 ignore_metadata_duration_change_ = true;
909 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
910 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
911 client_->durationChanged();
914 void WebMediaPlayerAndroid::UpdateNetworkState(
915 WebMediaPlayer::NetworkState state) {
916 DCHECK(main_thread_checker_.CalledOnValidThread());
917 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
918 (state == WebMediaPlayer::NetworkStateNetworkError ||
919 state == WebMediaPlayer::NetworkStateDecodeError)) {
920 // Any error that occurs before reaching ReadyStateHaveMetadata should
921 // be considered a format error.
922 network_state_ = WebMediaPlayer::NetworkStateFormatError;
924 network_state_ = state;
926 client_->networkStateChanged();
929 void WebMediaPlayerAndroid::UpdateReadyState(
930 WebMediaPlayer::ReadyState state) {
931 ready_state_ = state;
932 client_->readyStateChanged();
935 void WebMediaPlayerAndroid::OnPlayerReleased() {
936 // |needs_external_surface_| is always false on non-TV devices.
937 if (!needs_external_surface_)
938 needs_establish_peer_ = true;
941 OnMediaPlayerPause();
943 #if defined(VIDEO_HOLE)
944 last_computed_rect_ = gfx::RectF();
945 #endif // defined(VIDEO_HOLE)
948 void WebMediaPlayerAndroid::ReleaseMediaResources() {
949 switch (network_state_) {
950 // Pause the media player and inform WebKit if the player is in a good
952 case WebMediaPlayer::NetworkStateIdle:
953 case WebMediaPlayer::NetworkStateLoading:
954 case WebMediaPlayer::NetworkStateLoaded:
956 client_->playbackStateChanged();
958 // If a WebMediaPlayer instance has entered into one of these states,
959 // the internal network state in HTMLMediaElement could be set to empty.
960 // And calling playbackStateChanged() could get this object deleted.
961 case WebMediaPlayer::NetworkStateEmpty:
962 case WebMediaPlayer::NetworkStateFormatError:
963 case WebMediaPlayer::NetworkStateNetworkError:
964 case WebMediaPlayer::NetworkStateDecodeError:
967 player_manager_->ReleaseResources(player_id_);
968 if (!needs_external_surface_)
969 SetNeedsEstablishPeer(true);
972 void WebMediaPlayerAndroid::OnDestruct() {
973 NOTREACHED() << "WebMediaPlayer should be destroyed before any "
974 "RenderFrameObserver::OnDestruct() gets called when "
975 "the RenderFrame goes away.";
978 void WebMediaPlayerAndroid::InitializePlayer(
980 const GURL& first_party_for_cookies,
981 bool allow_stored_credentials,
982 int demuxer_client_id) {
983 allow_stored_credentials_ = allow_stored_credentials;
984 player_manager_->Initialize(
985 player_type_, player_id_, url, first_party_for_cookies, demuxer_client_id,
986 frame_->document().url(), allow_stored_credentials);
987 if (player_manager_->ShouldEnterFullscreen(frame_))
988 player_manager_->EnterFullscreen(player_id_, frame_);
991 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
992 player_manager_->Pause(player_id_, is_media_related_action);
993 UpdatePlayingState(false);
996 void WebMediaPlayerAndroid::DrawRemotePlaybackText(
997 const std::string& remote_playback_message) {
998 DCHECK(main_thread_checker_.CalledOnValidThread());
999 if (!video_weblayer_)
1002 // TODO(johnme): Should redraw this frame if the layer bounds change; but
1003 // there seems no easy way to listen for the layer resizing (as opposed to
1004 // OnVideoSizeChanged, which is when the frame sizes of the video file
1005 // change). Perhaps have to poll (on main thread of course)?
1006 gfx::Size video_size_css_px = video_weblayer_->bounds();
1007 float device_scale_factor = frame_->view()->deviceScaleFactor();
1008 // canvas_size will be the size in device pixels when pageScaleFactor == 1
1009 gfx::Size canvas_size(
1010 static_cast<int>(video_size_css_px.width() * device_scale_factor),
1011 static_cast<int>(video_size_css_px.height() * device_scale_factor));
1014 bitmap.allocN32Pixels(canvas_size.width(), canvas_size.height());
1016 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
1017 SkCanvas canvas(bitmap);
1018 canvas.drawColor(SK_ColorBLACK);
1020 const SkScalar kTextSize(40);
1021 const SkScalar kMinPadding(40);
1024 paint.setAntiAlias(true);
1025 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
1026 paint.setColor(SK_ColorWHITE);
1027 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
1028 paint.setTextSize(kTextSize);
1030 // Calculate the vertical margin from the top
1031 SkPaint::FontMetrics font_metrics;
1032 paint.getFontMetrics(&font_metrics);
1033 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
1035 // Measure the width of the entire text to display
1036 size_t display_text_width = paint.measureText(
1037 remote_playback_message.c_str(), remote_playback_message.size());
1038 std::string display_text(remote_playback_message);
1040 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
1041 // The text is too long to fit in one line, truncate it and append ellipsis
1044 // First, figure out how much of the canvas the '...' will take up.
1045 const std::string kTruncationEllipsis("\xE2\x80\xA6");
1046 SkScalar sk_ellipse_width = paint.measureText(
1047 kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
1049 // Then calculate how much of the text can be drawn with the '...' appended
1050 // to the end of the string.
1051 SkScalar sk_max_original_text_width(
1052 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
1053 size_t sk_max_original_text_length = paint.breakText(
1054 remote_playback_message.c_str(),
1055 remote_playback_message.size(),
1056 sk_max_original_text_width);
1058 // Remove the part of the string that doesn't fit and append '...'.
1059 display_text.erase(sk_max_original_text_length,
1060 remote_playback_message.size() - sk_max_original_text_length);
1061 display_text.append(kTruncationEllipsis);
1062 display_text_width = paint.measureText(
1063 display_text.c_str(), display_text.size());
1066 // Center the text horizontally.
1067 SkScalar sk_horizontal_margin =
1068 (canvas_size.width() - display_text_width) / 2.0;
1069 canvas.drawText(display_text.c_str(),
1070 display_text.size(),
1071 sk_horizontal_margin,
1075 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1076 GLuint remote_playback_texture_id = 0;
1077 gl->GenTextures(1, &remote_playback_texture_id);
1078 GLuint texture_target = GL_TEXTURE_2D;
1079 gl->BindTexture(texture_target, remote_playback_texture_id);
1080 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1081 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1082 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1083 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1086 SkAutoLockPixels lock(bitmap);
1087 gl->TexImage2D(texture_target,
1089 GL_RGBA /* internalformat */,
1093 GL_RGBA /* format */,
1094 GL_UNSIGNED_BYTE /* type */,
1095 bitmap.getPixels());
1098 gpu::Mailbox texture_mailbox;
1099 gl->GenMailboxCHROMIUM(texture_mailbox.name);
1100 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
1102 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1104 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1105 make_scoped_ptr(new gpu::MailboxHolder(
1106 texture_mailbox, texture_target, texture_mailbox_sync_point)),
1107 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture,
1108 stream_texture_factory_,
1109 remote_playback_texture_id)),
1110 canvas_size /* coded_size */,
1111 gfx::Rect(canvas_size) /* visible_rect */,
1112 canvas_size /* natural_size */,
1113 base::TimeDelta() /* timestamp */,
1114 VideoFrame::ReadPixelsCB());
1115 SetCurrentFrameInternal(new_frame);
1118 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
1119 DCHECK(main_thread_checker_.CalledOnValidThread());
1120 if (needs_external_surface_) {
1121 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
1122 #if defined(VIDEO_HOLE)
1123 if (!natural_size_.isEmpty()) {
1124 scoped_refptr<VideoFrame> new_frame =
1125 VideoFrame::CreateHoleFrame(natural_size_);
1126 SetCurrentFrameInternal(new_frame);
1127 // Force the client to grab the hole frame.
1131 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
1132 #endif // defined(VIDEO_HOLE)
1133 } else if (!is_remote_ && texture_id_) {
1134 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1135 GLuint texture_target = kGLTextureExternalOES;
1136 GLuint texture_id_ref = gl->CreateAndConsumeTextureCHROMIUM(
1137 texture_target, texture_mailbox_.name);
1139 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1141 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1142 make_scoped_ptr(new gpu::MailboxHolder(
1143 texture_mailbox_, texture_target, texture_mailbox_sync_point)),
1144 media::BindToCurrentLoop(base::Bind(
1145 &OnReleaseTexture, stream_texture_factory_, texture_id_ref)),
1147 gfx::Rect(natural_size_),
1150 VideoFrame::ReadPixelsCB());
1151 SetCurrentFrameInternal(new_frame);
1155 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1156 cc::VideoFrameProvider::Client* client) {
1157 // This is called from both the main renderer thread and the compositor
1158 // thread (when the main thread is blocked).
1159 if (video_frame_provider_client_)
1160 video_frame_provider_client_->StopUsingProvider();
1161 video_frame_provider_client_ = client;
1163 // Set the callback target when a frame is produced.
1164 if (stream_texture_proxy_) {
1165 stream_texture_proxy_->SetClient(client);
1166 // If client exists, the compositor thread calls it. At that time,
1167 // stream_id_, needs_external_surface_, is_remote_ can be accessed because
1168 // the main thread is blocked.
1169 if (client && !stream_texture_proxy_initialized_ && stream_id_ &&
1170 !needs_external_surface_ && !is_remote_) {
1171 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1172 stream_texture_proxy_initialized_ = true;
1177 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1178 scoped_refptr<media::VideoFrame>& video_frame) {
1179 DCHECK(main_thread_checker_.CalledOnValidThread());
1180 base::AutoLock auto_lock(current_frame_lock_);
1181 current_frame_ = video_frame;
1184 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1185 scoped_refptr<VideoFrame> video_frame;
1187 base::AutoLock auto_lock(current_frame_lock_);
1188 video_frame = current_frame_;
1194 void WebMediaPlayerAndroid::PutCurrentFrame(
1195 const scoped_refptr<media::VideoFrame>& frame) {
1198 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1199 DCHECK(main_thread_checker_.CalledOnValidThread());
1201 if (stream_texture_proxy_)
1204 // No factory to create proxy.
1205 if (!stream_texture_factory_)
1208 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1209 if (needs_establish_peer_ && stream_texture_proxy_) {
1210 DoCreateStreamTexture();
1211 ReallocateVideoFrame();
1214 if (stream_texture_proxy_ && video_frame_provider_client_)
1215 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1218 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1219 DCHECK(main_thread_checker_.CalledOnValidThread());
1220 if (!stream_texture_proxy_)
1223 if (stream_texture_factory_.get() && stream_id_)
1224 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1226 // Set the deferred size because the size was changed in remote mode.
1227 if (!is_remote_ && cached_stream_texture_size_ != natural_size_) {
1228 stream_texture_factory_->SetStreamTextureSize(
1229 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
1230 cached_stream_texture_size_ = natural_size_;
1233 needs_establish_peer_ = false;
1236 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1237 DCHECK(main_thread_checker_.CalledOnValidThread());
1238 DCHECK(!stream_id_);
1239 DCHECK(!texture_id_);
1240 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1241 kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
1244 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1245 needs_establish_peer_ = needs_establish_peer;
1248 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1249 player_manager_->SetPoster(player_id_, poster);
1252 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1253 is_playing_ = is_playing;
1257 delegate_->DidPlay(this);
1259 delegate_->DidPause(this);
1262 #if defined(VIDEO_HOLE)
1263 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1264 if (!video_weblayer_)
1267 // Compute the geometry of video frame layer.
1268 cc::Layer* layer = video_weblayer_->layer();
1269 gfx::RectF rect(layer->bounds());
1271 rect.Offset(layer->position().OffsetFromOrigin());
1272 layer = layer->parent();
1275 // Return false when the geometry hasn't been changed from the last time.
1276 if (last_computed_rect_ == rect)
1279 // Store the changed geometry information when it is actually changed.
1280 last_computed_rect_ = rect;
1284 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1285 return last_computed_rect_;
1289 // The following EME related code is copied from WebMediaPlayerImpl.
1290 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1291 // WebMediaPlayerImpl.
1293 // Convert a WebString to ASCII, falling back on an empty string in the case
1294 // of a non-ASCII string.
1295 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
1296 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
1300 // Helper functions to report media EME related stats to UMA. They follow the
1301 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1302 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1303 // that UMA_* macros require the names to be constant throughout the process'
1306 static void EmeUMAHistogramEnumeration(const std::string& key_system,
1307 const std::string& method,
1309 int boundary_value) {
1310 base::LinearHistogram::FactoryGet(
1311 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1312 1, boundary_value, boundary_value + 1,
1313 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1316 static void EmeUMAHistogramCounts(const std::string& key_system,
1317 const std::string& method,
1319 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1320 base::Histogram::FactoryGet(
1321 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1322 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1325 // Helper enum for reporting generateKeyRequest/addKey histograms.
1326 enum MediaKeyException {
1329 kKeySystemNotSupported,
1330 kInvalidPlayerState,
1331 kMaxMediaKeyException
1334 static MediaKeyException MediaKeyExceptionForUMA(
1335 WebMediaPlayer::MediaKeyException e) {
1337 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1338 return kKeySystemNotSupported;
1339 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1340 return kInvalidPlayerState;
1341 case WebMediaPlayer::MediaKeyExceptionNoError:
1344 return kUnknownResultId;
1348 // Helper for converting |key_system| name and exception |e| to a pair of enum
1349 // values from above, for reporting to UMA.
1350 static void ReportMediaKeyExceptionToUMA(const std::string& method,
1351 const std::string& key_system,
1352 WebMediaPlayer::MediaKeyException e) {
1353 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1354 DCHECK_NE(result_id, kUnknownResultId) << e;
1355 EmeUMAHistogramEnumeration(
1356 key_system, method, result_id, kMaxMediaKeyException);
1359 bool WebMediaPlayerAndroid::IsKeySystemSupported(
1360 const std::string& key_system) {
1361 // On Android, EME only works with MSE.
1362 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1363 IsConcreteSupportedKeySystem(key_system);
1366 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1367 const WebString& key_system,
1368 const unsigned char* init_data,
1369 unsigned init_data_length) {
1370 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
1371 << std::string(reinterpret_cast<const char*>(init_data),
1372 static_cast<size_t>(init_data_length));
1374 std::string ascii_key_system =
1375 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1377 WebMediaPlayer::MediaKeyException e =
1378 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
1379 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
1383 // Guess the type of |init_data|. This is only used to handle some corner cases
1384 // so we keep it as simple as possible without breaking major use cases.
1385 static std::string GuessInitDataType(const unsigned char* init_data,
1386 unsigned init_data_length) {
1387 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
1388 if (init_data_length == 16)
1389 return "video/webm";
1394 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1395 // not enabled. Currently the player just doesn't start and waits for
1397 WebMediaPlayer::MediaKeyException
1398 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1399 const std::string& key_system,
1400 const unsigned char* init_data,
1401 unsigned init_data_length) {
1402 if (!IsKeySystemSupported(key_system))
1403 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1405 // We do not support run-time switching between key systems for now.
1406 if (current_key_system_.empty()) {
1407 if (!proxy_decryptor_) {
1408 proxy_decryptor_.reset(new ProxyDecryptor(
1410 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1411 weak_factory_.GetWeakPtr()),
1412 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1413 weak_factory_.GetWeakPtr()),
1414 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1415 weak_factory_.GetWeakPtr())));
1418 GURL security_origin(frame_->document().securityOrigin().toString());
1419 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
1420 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1422 if (!decryptor_ready_cb_.is_null()) {
1423 base::ResetAndReturn(&decryptor_ready_cb_)
1424 .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing));
1427 // Only browser CDMs have CDM ID. Render side CDMs (e.g. ClearKey CDM) do
1428 // not have a CDM ID and there is no need to call player_manager_->SetCdm().
1429 if (proxy_decryptor_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1430 player_manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId());
1432 current_key_system_ = key_system;
1433 } else if (key_system != current_key_system_) {
1434 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1437 std::string init_data_type = init_data_type_;
1438 if (init_data_type.empty())
1439 init_data_type = GuessInitDataType(init_data, init_data_length);
1441 // TODO(xhwang): We assume all streams are from the same container (thus have
1442 // the same "type") for now. In the future, the "type" should be passed down
1443 // from the application.
1444 if (!proxy_decryptor_->GenerateKeyRequest(
1445 init_data_type, init_data, init_data_length)) {
1446 current_key_system_.clear();
1447 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1450 return WebMediaPlayer::MediaKeyExceptionNoError;
1453 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1454 const WebString& key_system,
1455 const unsigned char* key,
1456 unsigned key_length,
1457 const unsigned char* init_data,
1458 unsigned init_data_length,
1459 const WebString& session_id) {
1460 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
1461 << std::string(reinterpret_cast<const char*>(key),
1462 static_cast<size_t>(key_length)) << ", "
1463 << std::string(reinterpret_cast<const char*>(init_data),
1464 static_cast<size_t>(init_data_length)) << " ["
1465 << base::string16(session_id) << "]";
1467 std::string ascii_key_system =
1468 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1469 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1471 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
1477 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
1481 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1482 const std::string& key_system,
1483 const unsigned char* key,
1484 unsigned key_length,
1485 const unsigned char* init_data,
1486 unsigned init_data_length,
1487 const std::string& session_id) {
1489 DCHECK_GT(key_length, 0u);
1491 if (!IsKeySystemSupported(key_system))
1492 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1494 if (current_key_system_.empty() || key_system != current_key_system_)
1495 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1497 proxy_decryptor_->AddKey(
1498 key, key_length, init_data, init_data_length, session_id);
1499 return WebMediaPlayer::MediaKeyExceptionNoError;
1502 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1503 const WebString& key_system,
1504 const WebString& session_id) {
1505 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
1506 << " [" << base::string16(session_id) << "]";
1508 std::string ascii_key_system =
1509 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1510 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1512 WebMediaPlayer::MediaKeyException e =
1513 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
1514 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
1518 WebMediaPlayer::MediaKeyException
1519 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
1520 const std::string& session_id) {
1521 if (!IsKeySystemSupported(key_system))
1522 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1524 if (current_key_system_.empty() || key_system != current_key_system_)
1525 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1527 proxy_decryptor_->CancelKeyRequest(session_id);
1528 return WebMediaPlayer::MediaKeyExceptionNoError;
1531 void WebMediaPlayerAndroid::setContentDecryptionModule(
1532 blink::WebContentDecryptionModule* cdm) {
1533 DCHECK(main_thread_checker_.CalledOnValidThread());
1535 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1539 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1543 if (!decryptor_ready_cb_.is_null()) {
1544 base::ResetAndReturn(&decryptor_ready_cb_)
1545 .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
1548 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1549 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1552 void WebMediaPlayerAndroid::setContentDecryptionModule(
1553 blink::WebContentDecryptionModule* cdm,
1554 blink::WebContentDecryptionModuleResult result) {
1555 DCHECK(main_thread_checker_.CalledOnValidThread());
1557 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1559 result.completeWithError(
1560 blink::WebContentDecryptionModuleExceptionNotSupportedError,
1562 "Null MediaKeys object is not supported.");
1566 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1569 if (!decryptor_ready_cb_.is_null()) {
1570 base::ResetAndReturn(&decryptor_ready_cb_).Run(
1571 web_cdm_->GetDecryptor(),
1572 media::BindToCurrentLoop(
1573 base::Bind(&WebMediaPlayerAndroid::ContentDecryptionModuleAttached,
1574 weak_factory_.GetWeakPtr(),
1577 // No pipeline/decoder connected, so resolve the promise. When something
1578 // is connected, setting the CDM will happen in SetDecryptorReadyCB().
1579 ContentDecryptionModuleAttached(result, true);
1582 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1583 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1586 void WebMediaPlayerAndroid::setContentDecryptionModuleSync(
1587 blink::WebContentDecryptionModule* cdm) {
1588 DCHECK(main_thread_checker_.CalledOnValidThread());
1590 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1594 DCHECK(decryptor_ready_cb_.is_null());
1596 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1597 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1598 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1601 void WebMediaPlayerAndroid::ContentDecryptionModuleAttached(
1602 blink::WebContentDecryptionModuleResult result,
1609 result.completeWithError(
1610 blink::WebContentDecryptionModuleExceptionNotSupportedError,
1612 "Unable to set MediaKeys object");
1615 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1616 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1619 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1620 WebString::fromUTF8(session_id));
1623 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1624 media::MediaKeys::KeyError error_code,
1625 uint32 system_code) {
1626 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1627 error_code, media::MediaKeys::kMaxKeyError);
1629 unsigned short short_system_code = 0;
1630 if (system_code > std::numeric_limits<unsigned short>::max()) {
1631 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1632 short_system_code = std::numeric_limits<unsigned short>::max();
1634 short_system_code = static_cast<unsigned short>(system_code);
1638 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1639 WebString::fromUTF8(session_id),
1640 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1644 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1645 const std::vector<uint8>& message,
1646 const GURL& destination_url) {
1647 DCHECK(destination_url.is_empty() || destination_url.is_valid());
1649 client_->keyMessage(
1650 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1651 WebString::fromUTF8(session_id),
1652 message.empty() ? NULL : &message[0],
1657 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1658 blink::WebMediaSource* web_media_source) {
1659 client_->mediaSourceOpened(web_media_source);
1662 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1663 const std::vector<uint8>& init_data) {
1664 DCHECK(main_thread_checker_.CalledOnValidThread());
1666 // Do not fire NeedKey event if encrypted media is not enabled.
1667 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1668 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1672 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1674 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1675 if (init_data_type_.empty())
1676 init_data_type_ = type;
1678 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1680 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1683 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1684 const media::DecryptorReadyCB& decryptor_ready_cb) {
1685 DCHECK(main_thread_checker_.CalledOnValidThread());
1687 // Cancels the previous decryptor request.
1688 if (decryptor_ready_cb.is_null()) {
1689 if (!decryptor_ready_cb_.is_null()) {
1690 base::ResetAndReturn(&decryptor_ready_cb_)
1691 .Run(NULL, base::Bind(DoNothing));
1696 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1697 // video and audio). The current implementation is okay for the current
1698 // media pipeline since we initialize audio and video decoders in sequence.
1699 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1701 DCHECK(decryptor_ready_cb_.is_null());
1703 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1704 DCHECK(!proxy_decryptor_ || !web_cdm_);
1706 if (proxy_decryptor_) {
1707 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(),
1708 base::Bind(DoNothing));
1713 decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
1717 decryptor_ready_cb_ = decryptor_ready_cb;
1720 void WebMediaPlayerAndroid::enterFullscreen() {
1721 if (player_manager_->CanEnterFullscreen(frame_)) {
1722 player_manager_->EnterFullscreen(player_id_, frame_);
1723 SetNeedsEstablishPeer(false);
1727 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1728 return player_manager_->CanEnterFullscreen(frame_);
1731 } // namespace content