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