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