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