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