chrome.bluetoothSocket: clean-up Listen functions
[chromium-blink-merge.git] / content / renderer / media / android / webmediaplayer_android.cc
blob7de5f3b734137b09b1dfa91a86f94419d171d1d3
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"
7 #include <limits>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "cc/layers/video_layer.h"
19 #include "content/public/common/content_client.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/renderer/render_frame.h"
22 #include "content/renderer/media/android/renderer_demuxer_android.h"
23 #include "content/renderer/media/android/renderer_media_player_manager.h"
24 #include "content/renderer/media/crypto/key_systems.h"
25 #include "content/renderer/media/crypto/renderer_cdm_manager.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;
59 using blink::WebSize;
60 using blink::WebString;
61 using blink::WebTimeRanges;
62 using blink::WebURL;
63 using gpu::gles2::GLES2Interface;
64 using media::MediaPlayerAndroid;
65 using media::VideoFrame;
67 namespace {
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,
74 uint32 texture_id,
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);
81 } // namespace
83 namespace content {
85 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
86 blink::WebFrame* frame,
87 blink::WebMediaPlayerClient* client,
88 base::WeakPtr<WebMediaPlayerDelegate> delegate,
89 RendererMediaPlayerManager* player_manager,
90 RendererCdmManager* cdm_manager,
91 scoped_refptr<StreamTextureFactory> factory,
92 const scoped_refptr<base::MessageLoopProxy>& media_loop,
93 media::MediaLog* media_log)
94 : RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
95 frame_(frame),
96 client_(client),
97 delegate_(delegate),
98 buffered_(static_cast<size_t>(1)),
99 media_loop_(media_loop),
100 ignore_metadata_duration_change_(false),
101 pending_seek_(false),
102 seeking_(false),
103 did_loading_progress_(false),
104 player_manager_(player_manager),
105 cdm_manager_(cdm_manager),
106 network_state_(WebMediaPlayer::NetworkStateEmpty),
107 ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
108 texture_id_(0),
109 stream_id_(0),
110 is_playing_(false),
111 playing_started_(false),
112 needs_establish_peer_(true),
113 stream_texture_proxy_initialized_(false),
114 has_size_info_(false),
115 has_media_metadata_(false),
116 has_media_info_(false),
117 stream_texture_factory_(factory),
118 needs_external_surface_(false),
119 video_frame_provider_client_(NULL),
120 pending_playback_(false),
121 player_type_(MEDIA_PLAYER_TYPE_URL),
122 current_time_(0),
123 is_remote_(false),
124 media_log_(media_log),
125 web_cdm_(NULL),
126 weak_factory_(this) {
127 DCHECK(player_manager_);
128 DCHECK(cdm_manager_);
130 DCHECK(main_thread_checker_.CalledOnValidThread());
132 player_id_ = player_manager_->RegisterMediaPlayer(this);
134 #if defined(VIDEO_HOLE)
135 force_use_overlay_embedded_video_ = CommandLine::ForCurrentProcess()->
136 HasSwitch(switches::kForceUseOverlayEmbeddedVideo);
137 if (force_use_overlay_embedded_video_ ||
138 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo()) {
139 // Defer stream texture creation until we are sure it's necessary.
140 needs_establish_peer_ = false;
141 current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
143 #endif // defined(VIDEO_HOLE)
144 TryCreateStreamTextureProxyIfNeeded();
147 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
148 SetVideoFrameProviderClient(NULL);
149 client_->setWebLayer(NULL);
151 if (player_manager_) {
152 player_manager_->DestroyPlayer(player_id_);
153 player_manager_->UnregisterMediaPlayer(player_id_);
156 if (stream_id_) {
157 GLES2Interface* gl = stream_texture_factory_->ContextGL();
158 gl->DeleteTextures(1, &texture_id_);
159 texture_id_ = 0;
160 texture_mailbox_ = gpu::Mailbox();
161 stream_id_ = 0;
165 base::AutoLock auto_lock(current_frame_lock_);
166 current_frame_ = NULL;
169 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
170 delegate_->PlayerGone(this);
173 void WebMediaPlayerAndroid::load(LoadType load_type,
174 const blink::WebURL& url,
175 CORSMode cors_mode) {
176 ReportMediaSchemeUma(GURL(url));
178 switch (load_type) {
179 case LoadTypeURL:
180 player_type_ = MEDIA_PLAYER_TYPE_URL;
181 break;
183 case LoadTypeMediaSource:
184 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
185 break;
187 case LoadTypeMediaStream:
188 CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
189 "this platform";
190 return;
193 has_media_metadata_ = false;
194 has_media_info_ = false;
196 int demuxer_client_id = 0;
197 if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
198 has_media_info_ = true;
200 RendererDemuxerAndroid* demuxer =
201 RenderThreadImpl::current()->renderer_demuxer();
202 demuxer_client_id = demuxer->GetNextDemuxerClientID();
204 media_source_delegate_.reset(new MediaSourceDelegate(
205 demuxer, demuxer_client_id, media_loop_, media_log_));
207 if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
208 media::SetDecryptorReadyCB set_decryptor_ready_cb =
209 media::BindToCurrentLoop(
210 base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
211 weak_factory_.GetWeakPtr()));
213 media_source_delegate_->InitializeMediaSource(
214 base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
215 weak_factory_.GetWeakPtr()),
216 base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
217 weak_factory_.GetWeakPtr()),
218 set_decryptor_ready_cb,
219 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
220 weak_factory_.GetWeakPtr()),
221 base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
222 weak_factory_.GetWeakPtr()));
224 } else {
225 info_loader_.reset(
226 new MediaInfoLoader(
227 url,
228 cors_mode,
229 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
230 weak_factory_.GetWeakPtr())));
231 // TODO(qinmin): The url might be redirected when android media player
232 // requests the stream. As a result, we cannot guarantee there is only
233 // a single origin. Remove the following line when b/12573548 is fixed.
234 // Check http://crbug.com/334204.
235 info_loader_->set_single_origin(false);
236 info_loader_->Start(frame_);
239 url_ = url;
240 GURL first_party_url = frame_->document().firstPartyForCookies();
241 player_manager_->Initialize(
242 player_type_, player_id_, url, first_party_url, demuxer_client_id);
244 if (player_manager_->ShouldEnterFullscreen(frame_))
245 player_manager_->EnterFullscreen(player_id_, frame_);
247 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
248 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
251 void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
252 DCHECK(!media_source_delegate_);
253 if (status == MediaInfoLoader::kFailed) {
254 info_loader_.reset();
255 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
256 return;
259 has_media_info_ = true;
260 if (has_media_metadata_ &&
261 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
262 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
263 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
265 // Android doesn't start fetching resources until an implementation-defined
266 // event (e.g. playback request) occurs. Sets the network state to IDLE
267 // if play is not requested yet.
268 if (!playing_started_)
269 UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
272 void WebMediaPlayerAndroid::play() {
273 #if defined(VIDEO_HOLE)
274 if (hasVideo() && needs_external_surface_ &&
275 !player_manager_->IsInFullscreen(frame_)) {
276 DCHECK(!needs_establish_peer_);
277 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
279 #endif // defined(VIDEO_HOLE)
281 TryCreateStreamTextureProxyIfNeeded();
282 // There is no need to establish the surface texture peer for fullscreen
283 // video.
284 if (hasVideo() && needs_establish_peer_ &&
285 !player_manager_->IsInFullscreen(frame_)) {
286 EstablishSurfaceTexturePeer();
289 if (paused())
290 player_manager_->Start(player_id_);
291 UpdatePlayingState(true);
292 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
293 playing_started_ = true;
296 void WebMediaPlayerAndroid::pause() {
297 Pause(true);
300 void WebMediaPlayerAndroid::seek(double seconds) {
301 DCHECK(main_thread_checker_.CalledOnValidThread());
302 DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
304 base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
306 if (seeking_) {
307 if (new_seek_time == seek_time_) {
308 if (media_source_delegate_) {
309 if (!pending_seek_) {
310 // If using media source demuxer, only suppress redundant seeks if
311 // there is no pending seek. This enforces that any pending seek that
312 // results in a demuxer seek is preceded by matching
313 // CancelPendingSeek() and StartWaitingForSeek() calls.
314 return;
316 } else {
317 // Suppress all redundant seeks if unrestricted by media source
318 // demuxer API.
319 pending_seek_ = false;
320 return;
324 pending_seek_ = true;
325 pending_seek_time_ = new_seek_time;
327 if (media_source_delegate_)
328 media_source_delegate_->CancelPendingSeek(pending_seek_time_);
330 // Later, OnSeekComplete will trigger the pending seek.
331 return;
334 seeking_ = true;
335 seek_time_ = new_seek_time;
337 if (media_source_delegate_)
338 media_source_delegate_->StartWaitingForSeek(seek_time_);
340 // Kick off the asynchronous seek!
341 player_manager_->Seek(player_id_, seek_time_);
344 bool WebMediaPlayerAndroid::supportsSave() const {
345 return false;
348 void WebMediaPlayerAndroid::setRate(double rate) {
349 NOTIMPLEMENTED();
352 void WebMediaPlayerAndroid::setVolume(double volume) {
353 player_manager_->SetVolume(player_id_, volume);
356 bool WebMediaPlayerAndroid::hasVideo() const {
357 // If we have obtained video size information before, use it.
358 if (has_size_info_)
359 return !natural_size_.isEmpty();
361 // TODO(qinmin): need a better method to determine whether the current media
362 // content contains video. Android does not provide any function to do
363 // this.
364 // We don't know whether the current media content has video unless
365 // the player is prepared. If the player is not prepared, we fall back
366 // to the mime-type. There may be no mime-type on a redirect URL.
367 // In that case, we conservatively assume it contains video so that
368 // enterfullscreen call will not fail.
369 if (!url_.has_path())
370 return false;
371 std::string mime;
372 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
373 return true;
374 return mime.find("audio/") == std::string::npos;
377 bool WebMediaPlayerAndroid::hasAudio() const {
378 if (!url_.has_path())
379 return false;
380 std::string mime;
381 if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
382 return true;
384 if (mime.find("audio/") != std::string::npos ||
385 mime.find("video/") != std::string::npos ||
386 mime.find("application/ogg") != std::string::npos) {
387 return true;
389 return false;
392 bool WebMediaPlayerAndroid::paused() const {
393 return !is_playing_;
396 bool WebMediaPlayerAndroid::seeking() const {
397 return seeking_;
400 double WebMediaPlayerAndroid::duration() const {
401 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING
402 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
403 return std::numeric_limits<double>::quiet_NaN();
405 if (duration_ == media::kInfiniteDuration())
406 return std::numeric_limits<double>::infinity();
408 return duration_.InSecondsF();
411 double WebMediaPlayerAndroid::timelineOffset() const {
412 base::Time timeline_offset;
413 if (media_source_delegate_)
414 timeline_offset = media_source_delegate_->GetTimelineOffset();
416 if (timeline_offset.is_null())
417 return std::numeric_limits<double>::quiet_NaN();
419 return timeline_offset.ToJsTime();
422 double WebMediaPlayerAndroid::currentTime() const {
423 // If the player is processing a seek, return the seek time.
424 // Blink may still query us if updatePlaybackState() occurs while seeking.
425 if (seeking()) {
426 return pending_seek_ ?
427 pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
430 return current_time_;
433 WebSize WebMediaPlayerAndroid::naturalSize() const {
434 return natural_size_;
437 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
438 return network_state_;
441 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
442 return ready_state_;
445 WebTimeRanges WebMediaPlayerAndroid::buffered() const {
446 if (media_source_delegate_)
447 return media_source_delegate_->Buffered();
448 return buffered_;
451 double WebMediaPlayerAndroid::maxTimeSeekable() const {
452 // If we haven't even gotten to ReadyStateHaveMetadata yet then just
453 // return 0 so that the seekable range is empty.
454 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
455 return 0.0;
457 return duration();
460 bool WebMediaPlayerAndroid::didLoadingProgress() {
461 bool ret = did_loading_progress_;
462 did_loading_progress_ = false;
463 return ret;
466 void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
467 const blink::WebRect& rect,
468 unsigned char alpha) {
469 NOTIMPLEMENTED();
472 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
473 blink::WebGraphicsContext3D* web_graphics_context,
474 unsigned int texture,
475 unsigned int level,
476 unsigned int internal_format,
477 unsigned int type,
478 bool premultiply_alpha,
479 bool flip_y) {
480 // Don't allow clients to copy an encrypted video frame.
481 if (needs_external_surface_)
482 return false;
484 scoped_refptr<VideoFrame> video_frame;
486 base::AutoLock auto_lock(current_frame_lock_);
487 video_frame = current_frame_;
490 if (!video_frame ||
491 video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
492 return false;
493 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
494 DCHECK((!is_remote_ &&
495 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) ||
496 (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D));
498 // For hidden video element (with style "display:none"), ensure the texture
499 // size is set.
500 if (!is_remote_ &&
501 (cached_stream_texture_size_.width != natural_size_.width ||
502 cached_stream_texture_size_.height != natural_size_.height)) {
503 stream_texture_factory_->SetStreamTextureSize(
504 stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
505 cached_stream_texture_size_ = natural_size_;
508 uint32 source_texture = web_graphics_context->createTexture();
509 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
511 // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise
512 // an invalid texture target may be used for copy texture.
513 web_graphics_context->bindTexture(mailbox_holder->texture_target,
514 source_texture);
515 web_graphics_context->consumeTextureCHROMIUM(mailbox_holder->texture_target,
516 mailbox_holder->mailbox.name);
518 // The video is stored in an unmultiplied format, so premultiply if
519 // necessary.
520 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
521 premultiply_alpha);
523 // Application itself needs to take care of setting the right flip_y
524 // value down to get the expected result.
525 // flip_y==true means to reverse the video orientation while
526 // flip_y==false means to keep the intrinsic orientation.
527 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
528 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture,
529 texture, level, internal_format,
530 type);
531 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
532 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
533 false);
535 if (mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES)
536 web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
537 else
538 web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
539 web_graphics_context->deleteTexture(source_texture);
540 web_graphics_context->flush();
541 video_frame->AppendReleaseSyncPoint(web_graphics_context->insertSyncPoint());
542 return true;
545 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
546 if (info_loader_)
547 return info_loader_->HasSingleOrigin();
548 // The info loader may have failed.
549 if (player_type_ == MEDIA_PLAYER_TYPE_URL)
550 return false;
551 return true;
554 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
555 if (info_loader_)
556 return info_loader_->DidPassCORSAccessCheck();
557 return false;
560 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
561 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
564 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
565 if (media_source_delegate_)
566 return media_source_delegate_->DecodedFrameCount();
567 NOTIMPLEMENTED();
568 return 0;
571 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
572 if (media_source_delegate_)
573 return media_source_delegate_->DroppedFrameCount();
574 NOTIMPLEMENTED();
575 return 0;
578 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
579 if (media_source_delegate_)
580 return media_source_delegate_->AudioDecodedByteCount();
581 NOTIMPLEMENTED();
582 return 0;
585 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
586 if (media_source_delegate_)
587 return media_source_delegate_->VideoDecodedByteCount();
588 NOTIMPLEMENTED();
589 return 0;
592 void WebMediaPlayerAndroid::OnMediaMetadataChanged(
593 const base::TimeDelta& duration, int width, int height, bool success) {
594 bool need_to_signal_duration_changed = false;
596 if (url_.SchemeIs("file"))
597 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
599 // Update duration, if necessary, prior to ready state updates that may
600 // cause duration() query.
601 if (!ignore_metadata_duration_change_ && duration_ != duration) {
602 duration_ = duration;
604 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA
605 // already triggers a durationchanged event. If this is a different
606 // transition, remember to signal durationchanged.
607 // Do not ever signal durationchanged on metadata change in MSE case
608 // because OnDurationChanged() handles this.
609 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
610 player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
611 need_to_signal_duration_changed = true;
615 has_media_metadata_ = true;
616 if (has_media_info_ &&
617 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
618 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
619 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
622 // TODO(wolenetz): Should we just abort early and set network state to an
623 // error if success == false? See http://crbug.com/248399
624 if (success)
625 OnVideoSizeChanged(width, height);
627 if (need_to_signal_duration_changed)
628 client_->durationChanged();
631 void WebMediaPlayerAndroid::OnPlaybackComplete() {
632 // When playback is about to finish, android media player often stops
633 // at a time which is smaller than the duration. This makes webkit never
634 // know that the playback has finished. To solve this, we set the
635 // current time to media duration when OnPlaybackComplete() get called.
636 OnTimeUpdate(duration_);
637 client_->timeChanged();
639 // if the loop attribute is set, timeChanged() will update the current time
640 // to 0. It will perform a seek to 0. As the requests to the renderer
641 // process are sequential, the OnSeekComplete() will only occur
642 // once OnPlaybackComplete() is done. As the playback can only be executed
643 // upon completion of OnSeekComplete(), the request needs to be saved.
644 is_playing_ = false;
645 if (seeking_ && seek_time_ == base::TimeDelta())
646 pending_playback_ = true;
649 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
650 buffered_[0].end = duration() * percentage / 100;
651 did_loading_progress_ = true;
654 void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
655 DCHECK(main_thread_checker_.CalledOnValidThread());
656 client_->requestSeek(time_to_seek.InSecondsF());
659 void WebMediaPlayerAndroid::OnSeekComplete(
660 const base::TimeDelta& current_time) {
661 DCHECK(main_thread_checker_.CalledOnValidThread());
662 seeking_ = false;
663 if (pending_seek_) {
664 pending_seek_ = false;
665 seek(pending_seek_time_.InSecondsF());
666 return;
669 OnTimeUpdate(current_time);
671 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
673 client_->timeChanged();
675 if (pending_playback_) {
676 play();
677 pending_playback_ = false;
681 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
682 switch (error_type) {
683 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
684 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
685 break;
686 case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
687 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
688 break;
689 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
690 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
691 break;
692 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
693 break;
695 client_->repaint();
698 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
699 has_size_info_ = true;
700 if (natural_size_.width == width && natural_size_.height == height)
701 return;
703 #if defined(VIDEO_HOLE)
704 // Use H/W surface for encrypted video.
705 // TODO(qinmin): Change this so that only EME needs the H/W surface
706 if (force_use_overlay_embedded_video_ ||
707 (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted() &&
708 player_manager_->ShouldUseVideoOverlayForEmbeddedEncryptedVideo())) {
709 needs_external_surface_ = true;
710 if (!paused() && !player_manager_->IsInFullscreen(frame_))
711 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
712 } else if (stream_texture_proxy_ && !stream_id_) {
713 // Do deferred stream texture creation finally.
714 DoCreateStreamTexture();
715 SetNeedsEstablishPeer(true);
717 #endif // defined(VIDEO_HOLE)
718 // When play() gets called, |natural_size_| may still be empty and
719 // EstablishSurfaceTexturePeer() will not get called. As a result, the video
720 // may play without a surface texture. When we finally get the valid video
721 // size here, we should call EstablishSurfaceTexturePeer() if it has not been
722 // previously called.
723 if (!paused() && needs_establish_peer_)
724 EstablishSurfaceTexturePeer();
726 natural_size_.width = width;
727 natural_size_.height = height;
728 ReallocateVideoFrame();
730 // Lazily allocate compositing layer.
731 if (!video_weblayer_) {
732 video_weblayer_.reset(
733 new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
734 client_->setWebLayer(video_weblayer_.get());
737 // TODO(qinmin): This is a hack. We need the media element to stop showing the
738 // poster image by forcing it to call setDisplayMode(video). Should move the
739 // logic into HTMLMediaElement.cpp.
740 client_->timeChanged();
743 void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
744 DCHECK(main_thread_checker_.CalledOnValidThread());
745 current_time_ = current_time.InSecondsF();
748 void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
749 const std::string& remote_playback_message) {
750 DCHECK(main_thread_checker_.CalledOnValidThread());
751 DCHECK(!media_source_delegate_);
752 DrawRemotePlaybackText(remote_playback_message);
753 is_remote_ = true;
754 SetNeedsEstablishPeer(false);
757 void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
758 DCHECK(main_thread_checker_.CalledOnValidThread());
759 DCHECK(!media_source_delegate_);
760 SetNeedsEstablishPeer(true);
761 if (!paused())
762 EstablishSurfaceTexturePeer();
763 is_remote_ = false;
764 ReallocateVideoFrame();
767 void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
768 if (!player_manager_->IsInFullscreen(frame_)) {
769 frame_->view()->willEnterFullScreen();
770 frame_->view()->didEnterFullScreen();
771 player_manager_->DidEnterFullscreen(frame_);
775 void WebMediaPlayerAndroid::OnDidExitFullscreen() {
776 // |needs_external_surface_| is always false on non-TV devices.
777 if (!needs_external_surface_)
778 SetNeedsEstablishPeer(true);
779 // We had the fullscreen surface connected to Android MediaPlayer,
780 // so reconnect our surface texture for embedded playback.
781 if (!paused() && needs_establish_peer_)
782 EstablishSurfaceTexturePeer();
784 #if defined(VIDEO_HOLE)
785 if (!paused() && needs_external_surface_)
786 player_manager_->RequestExternalSurface(player_id_, last_computed_rect_);
787 #endif // defined(VIDEO_HOLE)
789 frame_->view()->willExitFullScreen();
790 frame_->view()->didExitFullScreen();
791 player_manager_->DidExitFullscreen();
792 client_->repaint();
795 void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
796 UpdatePlayingState(true);
797 client_->playbackStateChanged();
800 void WebMediaPlayerAndroid::OnMediaPlayerPause() {
801 UpdatePlayingState(false);
802 client_->playbackStateChanged();
805 void WebMediaPlayerAndroid::OnRequestFullscreen() {
806 client_->requestFullscreen();
809 void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
810 DCHECK(main_thread_checker_.CalledOnValidThread());
811 // Only MSE |player_type_| registers this callback.
812 DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
814 // Cache the new duration value and trust it over any subsequent duration
815 // values received in OnMediaMetadataChanged().
816 duration_ = duration;
817 ignore_metadata_duration_change_ = true;
819 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING.
820 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
821 client_->durationChanged();
824 void WebMediaPlayerAndroid::UpdateNetworkState(
825 WebMediaPlayer::NetworkState state) {
826 DCHECK(main_thread_checker_.CalledOnValidThread());
827 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
828 (state == WebMediaPlayer::NetworkStateNetworkError ||
829 state == WebMediaPlayer::NetworkStateDecodeError)) {
830 // Any error that occurs before reaching ReadyStateHaveMetadata should
831 // be considered a format error.
832 network_state_ = WebMediaPlayer::NetworkStateFormatError;
833 } else {
834 network_state_ = state;
836 client_->networkStateChanged();
839 void WebMediaPlayerAndroid::UpdateReadyState(
840 WebMediaPlayer::ReadyState state) {
841 ready_state_ = state;
842 client_->readyStateChanged();
845 void WebMediaPlayerAndroid::OnPlayerReleased() {
846 // |needs_external_surface_| is always false on non-TV devices.
847 if (!needs_external_surface_)
848 needs_establish_peer_ = true;
850 if (is_playing_)
851 OnMediaPlayerPause();
853 #if defined(VIDEO_HOLE)
854 last_computed_rect_ = gfx::RectF();
855 #endif // defined(VIDEO_HOLE)
858 void WebMediaPlayerAndroid::ReleaseMediaResources() {
859 switch (network_state_) {
860 // Pause the media player and inform WebKit if the player is in a good
861 // shape.
862 case WebMediaPlayer::NetworkStateIdle:
863 case WebMediaPlayer::NetworkStateLoading:
864 case WebMediaPlayer::NetworkStateLoaded:
865 Pause(false);
866 client_->playbackStateChanged();
867 break;
868 // If a WebMediaPlayer instance has entered into one of these states,
869 // the internal network state in HTMLMediaElement could be set to empty.
870 // And calling playbackStateChanged() could get this object deleted.
871 case WebMediaPlayer::NetworkStateEmpty:
872 case WebMediaPlayer::NetworkStateFormatError:
873 case WebMediaPlayer::NetworkStateNetworkError:
874 case WebMediaPlayer::NetworkStateDecodeError:
875 break;
877 player_manager_->ReleaseResources(player_id_);
878 OnPlayerReleased();
881 void WebMediaPlayerAndroid::OnDestruct() {
882 if (player_manager_)
883 player_manager_->UnregisterMediaPlayer(player_id_);
884 Detach();
887 void WebMediaPlayerAndroid::Detach() {
888 if (stream_id_) {
889 GLES2Interface* gl = stream_texture_factory_->ContextGL();
890 gl->DeleteTextures(1, &texture_id_);
891 texture_id_ = 0;
892 texture_mailbox_ = gpu::Mailbox();
893 stream_id_ = 0;
896 media_source_delegate_.reset();
898 base::AutoLock auto_lock(current_frame_lock_);
899 current_frame_ = NULL;
901 is_remote_ = false;
902 player_manager_ = NULL;
905 void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
906 player_manager_->Pause(player_id_, is_media_related_action);
907 UpdatePlayingState(false);
910 void WebMediaPlayerAndroid::DrawRemotePlaybackText(
911 const std::string& remote_playback_message) {
913 DCHECK(main_thread_checker_.CalledOnValidThread());
914 if (!video_weblayer_)
915 return;
917 // TODO(johnme): Should redraw this frame if the layer bounds change; but
918 // there seems no easy way to listen for the layer resizing (as opposed to
919 // OnVideoSizeChanged, which is when the frame sizes of the video file
920 // change). Perhaps have to poll (on main thread of course)?
921 gfx::Size video_size_css_px = video_weblayer_->bounds();
922 float device_scale_factor = frame_->view()->deviceScaleFactor();
923 // canvas_size will be the size in device pixels when pageScaleFactor == 1
924 gfx::Size canvas_size(
925 static_cast<int>(video_size_css_px.width() * device_scale_factor),
926 static_cast<int>(video_size_css_px.height() * device_scale_factor));
928 SkBitmap bitmap;
929 bitmap.setConfig(
930 SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
931 bitmap.allocPixels();
933 // Create the canvas and draw the "Casting to <Chromecast>" text on it.
934 SkCanvas canvas(bitmap);
935 canvas.drawColor(SK_ColorBLACK);
937 const SkScalar kTextSize(40);
938 const SkScalar kMinPadding(40);
940 SkPaint paint;
941 paint.setAntiAlias(true);
942 paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
943 paint.setColor(SK_ColorWHITE);
944 paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
945 paint.setTextSize(kTextSize);
947 // Calculate the vertical margin from the top
948 SkPaint::FontMetrics font_metrics;
949 paint.getFontMetrics(&font_metrics);
950 SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
952 // Measure the width of the entire text to display
953 size_t display_text_width = paint.measureText(
954 remote_playback_message.c_str(), remote_playback_message.size());
955 std::string display_text(remote_playback_message);
957 if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
958 // The text is too long to fit in one line, truncate it and append ellipsis
959 // to the end.
961 // First, figure out how much of the canvas the '...' will take up.
962 const std::string kTruncationEllipsis("\xE2\x80\xA6");
963 SkScalar sk_ellipse_width = paint.measureText(
964 kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
966 // Then calculate how much of the text can be drawn with the '...' appended
967 // to the end of the string.
968 SkScalar sk_max_original_text_width(
969 canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
970 size_t sk_max_original_text_length = paint.breakText(
971 remote_playback_message.c_str(),
972 remote_playback_message.size(),
973 sk_max_original_text_width);
975 // Remove the part of the string that doesn't fit and append '...'.
976 display_text.erase(sk_max_original_text_length,
977 remote_playback_message.size() - sk_max_original_text_length);
978 display_text.append(kTruncationEllipsis);
979 display_text_width = paint.measureText(
980 display_text.c_str(), display_text.size());
983 // Center the text horizontally.
984 SkScalar sk_horizontal_margin =
985 (canvas_size.width() - display_text_width) / 2.0;
986 canvas.drawText(display_text.c_str(),
987 display_text.size(),
988 sk_horizontal_margin,
989 sk_vertical_margin,
990 paint);
992 GLES2Interface* gl = stream_texture_factory_->ContextGL();
993 GLuint remote_playback_texture_id = 0;
994 gl->GenTextures(1, &remote_playback_texture_id);
995 GLuint texture_target = GL_TEXTURE_2D;
996 gl->BindTexture(texture_target, remote_playback_texture_id);
997 gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
998 gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
999 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1000 gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1003 SkAutoLockPixels lock(bitmap);
1004 gl->TexImage2D(texture_target,
1005 0 /* level */,
1006 GL_RGBA /* internalformat */,
1007 bitmap.width(),
1008 bitmap.height(),
1009 0 /* border */,
1010 GL_RGBA /* format */,
1011 GL_UNSIGNED_BYTE /* type */,
1012 bitmap.getPixels());
1015 gpu::Mailbox texture_mailbox;
1016 gl->GenMailboxCHROMIUM(texture_mailbox.name);
1017 gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
1018 gl->Flush();
1019 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1021 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1022 make_scoped_ptr(new gpu::MailboxHolder(
1023 texture_mailbox, texture_target, texture_mailbox_sync_point)),
1024 media::BindToCurrentLoop(base::Bind(&OnReleaseTexture,
1025 stream_texture_factory_,
1026 remote_playback_texture_id)),
1027 canvas_size /* coded_size */,
1028 gfx::Rect(canvas_size) /* visible_rect */,
1029 canvas_size /* natural_size */,
1030 base::TimeDelta() /* timestamp */,
1031 VideoFrame::ReadPixelsCB());
1032 SetCurrentFrameInternal(new_frame);
1035 void WebMediaPlayerAndroid::ReallocateVideoFrame() {
1036 if (needs_external_surface_) {
1037 // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE.
1038 #if defined(VIDEO_HOLE)
1039 if (!natural_size_.isEmpty()) {
1040 scoped_refptr<VideoFrame> new_frame =
1041 VideoFrame::CreateHoleFrame(natural_size_);
1042 SetCurrentFrameInternal(new_frame);
1043 // Force the client to grab the hole frame.
1044 client_->repaint();
1046 #else
1047 NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
1048 #endif // defined(VIDEO_HOLE)
1049 } else if (!is_remote_ && texture_id_) {
1050 GLES2Interface* gl = stream_texture_factory_->ContextGL();
1051 GLuint texture_id_ref = 0;
1052 gl->GenTextures(1, &texture_id_ref);
1053 GLuint texture_target = kGLTextureExternalOES;
1054 gl->BindTexture(texture_target, texture_id_ref);
1055 gl->ConsumeTextureCHROMIUM(texture_target, texture_mailbox_.name);
1056 gl->Flush();
1057 GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
1059 scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
1060 make_scoped_ptr(new gpu::MailboxHolder(
1061 texture_mailbox_, texture_target, texture_mailbox_sync_point)),
1062 media::BindToCurrentLoop(base::Bind(
1063 &OnReleaseTexture, stream_texture_factory_, texture_id_ref)),
1064 natural_size_,
1065 gfx::Rect(natural_size_),
1066 natural_size_,
1067 base::TimeDelta(),
1068 VideoFrame::ReadPixelsCB());
1069 SetCurrentFrameInternal(new_frame);
1073 void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
1074 cc::VideoFrameProvider::Client* client) {
1075 // This is called from both the main renderer thread and the compositor
1076 // thread (when the main thread is blocked).
1077 if (video_frame_provider_client_)
1078 video_frame_provider_client_->StopUsingProvider();
1079 video_frame_provider_client_ = client;
1081 // Set the callback target when a frame is produced.
1082 if (stream_texture_proxy_)
1083 stream_texture_proxy_->SetClient(client);
1086 void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1087 scoped_refptr<media::VideoFrame>& video_frame) {
1088 base::AutoLock auto_lock(current_frame_lock_);
1089 current_frame_ = video_frame;
1092 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
1093 scoped_refptr<VideoFrame> video_frame;
1095 base::AutoLock auto_lock(current_frame_lock_);
1096 video_frame = current_frame_;
1099 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
1100 stream_id_ && !needs_external_surface_ && !is_remote_) {
1101 gfx::Size natural_size = video_frame->natural_size();
1102 // TODO(sievers): These variables are accessed on the wrong thread here.
1103 stream_texture_proxy_->BindToCurrentThread(stream_id_);
1104 stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
1105 stream_texture_proxy_initialized_ = true;
1106 cached_stream_texture_size_ = natural_size;
1109 return video_frame;
1112 void WebMediaPlayerAndroid::PutCurrentFrame(
1113 const scoped_refptr<media::VideoFrame>& frame) {
1116 void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
1117 // Already created.
1118 if (stream_texture_proxy_)
1119 return;
1121 // No factory to create proxy.
1122 if (!stream_texture_factory_)
1123 return;
1125 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
1126 if (needs_establish_peer_ && stream_texture_proxy_) {
1127 DoCreateStreamTexture();
1128 ReallocateVideoFrame();
1131 if (stream_texture_proxy_ && video_frame_provider_client_)
1132 stream_texture_proxy_->SetClient(video_frame_provider_client_);
1135 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
1136 if (!stream_texture_proxy_)
1137 return;
1139 if (stream_texture_factory_.get() && stream_id_)
1140 stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
1141 needs_establish_peer_ = false;
1144 void WebMediaPlayerAndroid::DoCreateStreamTexture() {
1145 DCHECK(!stream_id_);
1146 DCHECK(!texture_id_);
1147 stream_id_ = stream_texture_factory_->CreateStreamTexture(
1148 kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
1151 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1152 needs_establish_peer_ = needs_establish_peer;
1155 void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1156 player_manager_->SetPoster(player_id_, poster);
1159 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
1160 is_playing_ = is_playing;
1161 if (!delegate_)
1162 return;
1163 if (is_playing)
1164 delegate_->DidPlay(this);
1165 else
1166 delegate_->DidPause(this);
1169 #if defined(VIDEO_HOLE)
1170 bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
1171 if (!video_weblayer_)
1172 return false;
1174 // Compute the geometry of video frame layer.
1175 cc::Layer* layer = video_weblayer_->layer();
1176 gfx::RectF rect(layer->bounds());
1177 while (layer) {
1178 rect.Offset(layer->position().OffsetFromOrigin());
1179 layer = layer->parent();
1182 // Return false when the geometry hasn't been changed from the last time.
1183 if (last_computed_rect_ == rect)
1184 return false;
1186 // Store the changed geometry information when it is actually changed.
1187 last_computed_rect_ = rect;
1188 return true;
1191 const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
1192 return last_computed_rect_;
1194 #endif
1196 // The following EME related code is copied from WebMediaPlayerImpl.
1197 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and
1198 // WebMediaPlayerImpl.
1200 // Convert a WebString to ASCII, falling back on an empty string in the case
1201 // of a non-ASCII string.
1202 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
1203 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
1204 : std::string();
1207 // Helper functions to report media EME related stats to UMA. They follow the
1208 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
1209 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
1210 // that UMA_* macros require the names to be constant throughout the process'
1211 // lifetime.
1213 static void EmeUMAHistogramEnumeration(const std::string& key_system,
1214 const std::string& method,
1215 int sample,
1216 int boundary_value) {
1217 base::LinearHistogram::FactoryGet(
1218 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1219 1, boundary_value, boundary_value + 1,
1220 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1223 static void EmeUMAHistogramCounts(const std::string& key_system,
1224 const std::string& method,
1225 int sample) {
1226 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
1227 base::Histogram::FactoryGet(
1228 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1229 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
1232 // Helper enum for reporting generateKeyRequest/addKey histograms.
1233 enum MediaKeyException {
1234 kUnknownResultId,
1235 kSuccess,
1236 kKeySystemNotSupported,
1237 kInvalidPlayerState,
1238 kMaxMediaKeyException
1241 static MediaKeyException MediaKeyExceptionForUMA(
1242 WebMediaPlayer::MediaKeyException e) {
1243 switch (e) {
1244 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
1245 return kKeySystemNotSupported;
1246 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
1247 return kInvalidPlayerState;
1248 case WebMediaPlayer::MediaKeyExceptionNoError:
1249 return kSuccess;
1250 default:
1251 return kUnknownResultId;
1255 // Helper for converting |key_system| name and exception |e| to a pair of enum
1256 // values from above, for reporting to UMA.
1257 static void ReportMediaKeyExceptionToUMA(const std::string& method,
1258 const std::string& key_system,
1259 WebMediaPlayer::MediaKeyException e) {
1260 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
1261 DCHECK_NE(result_id, kUnknownResultId) << e;
1262 EmeUMAHistogramEnumeration(
1263 key_system, method, result_id, kMaxMediaKeyException);
1266 bool WebMediaPlayerAndroid::IsKeySystemSupported(
1267 const std::string& key_system) {
1268 // On Android, EME only works with MSE.
1269 return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
1270 IsConcreteSupportedKeySystem(key_system);
1273 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
1274 const WebString& key_system,
1275 const unsigned char* init_data,
1276 unsigned init_data_length) {
1277 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
1278 << std::string(reinterpret_cast<const char*>(init_data),
1279 static_cast<size_t>(init_data_length));
1281 std::string ascii_key_system =
1282 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1284 WebMediaPlayer::MediaKeyException e =
1285 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
1286 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
1287 return e;
1290 // Guess the type of |init_data|. This is only used to handle some corner cases
1291 // so we keep it as simple as possible without breaking major use cases.
1292 static std::string GuessInitDataType(const unsigned char* init_data,
1293 unsigned init_data_length) {
1294 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
1295 if (init_data_length == 16)
1296 return "video/webm";
1298 return "video/mp4";
1301 // TODO(xhwang): Report an error when there is encrypted stream but EME is
1302 // not enabled. Currently the player just doesn't start and waits for
1303 // ever.
1304 WebMediaPlayer::MediaKeyException
1305 WebMediaPlayerAndroid::GenerateKeyRequestInternal(
1306 const std::string& key_system,
1307 const unsigned char* init_data,
1308 unsigned init_data_length) {
1309 if (!IsKeySystemSupported(key_system))
1310 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1312 // We do not support run-time switching between key systems for now.
1313 if (current_key_system_.empty()) {
1314 if (!proxy_decryptor_) {
1315 proxy_decryptor_.reset(new ProxyDecryptor(
1316 cdm_manager_,
1317 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
1318 weak_factory_.GetWeakPtr()),
1319 base::Bind(&WebMediaPlayerAndroid::OnKeyError,
1320 weak_factory_.GetWeakPtr()),
1321 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
1322 weak_factory_.GetWeakPtr())));
1325 GURL security_origin(frame_->document().securityOrigin().toString());
1326 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
1327 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1329 if (!decryptor_ready_cb_.is_null()) {
1330 base::ResetAndReturn(&decryptor_ready_cb_)
1331 .Run(proxy_decryptor_->GetDecryptor());
1334 // Only browser CDMs have CDM ID. Render side CDMs (e.g. ClearKey CDM) do
1335 // not have a CDM ID and there is no need to call player_manager_->SetCdm().
1336 if (proxy_decryptor_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1337 player_manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId());
1339 current_key_system_ = key_system;
1340 } else if (key_system != current_key_system_) {
1341 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1344 std::string init_data_type = init_data_type_;
1345 if (init_data_type.empty())
1346 init_data_type = GuessInitDataType(init_data, init_data_length);
1348 // TODO(xhwang): We assume all streams are from the same container (thus have
1349 // the same "type") for now. In the future, the "type" should be passed down
1350 // from the application.
1351 if (!proxy_decryptor_->GenerateKeyRequest(
1352 init_data_type, init_data, init_data_length)) {
1353 current_key_system_.clear();
1354 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1357 return WebMediaPlayer::MediaKeyExceptionNoError;
1360 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
1361 const WebString& key_system,
1362 const unsigned char* key,
1363 unsigned key_length,
1364 const unsigned char* init_data,
1365 unsigned init_data_length,
1366 const WebString& session_id) {
1367 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
1368 << std::string(reinterpret_cast<const char*>(key),
1369 static_cast<size_t>(key_length)) << ", "
1370 << std::string(reinterpret_cast<const char*>(init_data),
1371 static_cast<size_t>(init_data_length)) << " ["
1372 << base::string16(session_id) << "]";
1374 std::string ascii_key_system =
1375 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1376 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1378 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
1379 key,
1380 key_length,
1381 init_data,
1382 init_data_length,
1383 ascii_session_id);
1384 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
1385 return e;
1388 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
1389 const std::string& key_system,
1390 const unsigned char* key,
1391 unsigned key_length,
1392 const unsigned char* init_data,
1393 unsigned init_data_length,
1394 const std::string& session_id) {
1395 DCHECK(key);
1396 DCHECK_GT(key_length, 0u);
1398 if (!IsKeySystemSupported(key_system))
1399 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1401 if (current_key_system_.empty() || key_system != current_key_system_)
1402 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1404 proxy_decryptor_->AddKey(
1405 key, key_length, init_data, init_data_length, session_id);
1406 return WebMediaPlayer::MediaKeyExceptionNoError;
1409 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
1410 const WebString& key_system,
1411 const WebString& session_id) {
1412 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
1413 << " [" << base::string16(session_id) << "]";
1415 std::string ascii_key_system =
1416 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
1417 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
1419 WebMediaPlayer::MediaKeyException e =
1420 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
1421 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
1422 return e;
1425 WebMediaPlayer::MediaKeyException
1426 WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
1427 const std::string& session_id) {
1428 if (!IsKeySystemSupported(key_system))
1429 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
1431 if (current_key_system_.empty() || key_system != current_key_system_)
1432 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
1434 proxy_decryptor_->CancelKeyRequest(session_id);
1435 return WebMediaPlayer::MediaKeyExceptionNoError;
1438 void WebMediaPlayerAndroid::setContentDecryptionModule(
1439 blink::WebContentDecryptionModule* cdm) {
1440 DCHECK(main_thread_checker_.CalledOnValidThread());
1442 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
1443 if (!cdm)
1444 return;
1446 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
1447 if (!web_cdm_)
1448 return;
1450 if (!decryptor_ready_cb_.is_null())
1451 base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
1453 if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId)
1454 player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
1457 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
1458 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1460 client_->keyAdded(
1461 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1462 WebString::fromUTF8(session_id));
1465 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
1466 media::MediaKeys::KeyError error_code,
1467 uint32 system_code) {
1468 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1469 error_code, media::MediaKeys::kMaxKeyError);
1471 unsigned short short_system_code = 0;
1472 if (system_code > std::numeric_limits<unsigned short>::max()) {
1473 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1474 short_system_code = std::numeric_limits<unsigned short>::max();
1475 } else {
1476 short_system_code = static_cast<unsigned short>(system_code);
1479 client_->keyError(
1480 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1481 WebString::fromUTF8(session_id),
1482 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1483 short_system_code);
1486 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
1487 const std::vector<uint8>& message,
1488 const GURL& destination_url) {
1489 DCHECK(destination_url.is_empty() || destination_url.is_valid());
1491 client_->keyMessage(
1492 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1493 WebString::fromUTF8(session_id),
1494 message.empty() ? NULL : &message[0],
1495 message.size(),
1496 destination_url);
1499 void WebMediaPlayerAndroid::OnMediaSourceOpened(
1500 blink::WebMediaSource* web_media_source) {
1501 client_->mediaSourceOpened(web_media_source);
1504 void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
1505 const std::vector<uint8>& init_data) {
1506 DCHECK(main_thread_checker_.CalledOnValidThread());
1508 // Do not fire NeedKey event if encrypted media is not enabled.
1509 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1510 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1511 return;
1514 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1516 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1517 if (init_data_type_.empty())
1518 init_data_type_ = type;
1520 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1521 client_->keyNeeded(
1522 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1525 void WebMediaPlayerAndroid::SetDecryptorReadyCB(
1526 const media::DecryptorReadyCB& decryptor_ready_cb) {
1527 DCHECK(main_thread_checker_.CalledOnValidThread());
1529 // Cancels the previous decryptor request.
1530 if (decryptor_ready_cb.is_null()) {
1531 if (!decryptor_ready_cb_.is_null())
1532 base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
1533 return;
1536 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1537 // video and audio). The current implementation is okay for the current
1538 // media pipeline since we initialize audio and video decoders in sequence.
1539 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1540 // detail.
1541 DCHECK(decryptor_ready_cb_.is_null());
1543 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1544 DCHECK(!proxy_decryptor_ || !web_cdm_);
1546 if (proxy_decryptor_) {
1547 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
1548 return;
1551 if (web_cdm_) {
1552 decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
1553 return;
1556 decryptor_ready_cb_ = decryptor_ready_cb;
1559 void WebMediaPlayerAndroid::enterFullscreen() {
1560 if (player_manager_->CanEnterFullscreen(frame_)) {
1561 player_manager_->EnterFullscreen(player_id_, frame_);
1562 SetNeedsEstablishPeer(false);
1566 void WebMediaPlayerAndroid::exitFullscreen() {
1567 player_manager_->ExitFullscreen(player_id_);
1570 bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1571 return player_manager_->CanEnterFullscreen(frame_);
1574 } // namespace content