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