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