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
57static const uint32 kGLTextureExternalOES = 0x8D65;
58static const int kSDKVersionToSupportSecurityOriginCheck = 20;
59
60using blink::WebMediaPlayer;
61using blink::WebSize;
62using blink::WebString;
63using blink::WebTimeRanges;
64using blink::WebURL;
65using gpu::gles2::GLES2Interface;
66using media::MediaPlayerAndroid;
67using media::VideoFrame;
68
69namespace {
70// Prefix for histograms related to Encrypted Media Extensions.
71const char* kMediaEme = "Media.EME.";
72
73// File-static function is to allow it to run even after WMPA is deleted.
74void 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
85namespace content {
86
87WebMediaPlayerAndroid::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
149WebMediaPlayerAndroid::~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
177void 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
240void 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
258void 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
281void WebMediaPlayerAndroid::pause() {
282  Pause(true);
283}
284
285void 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
329bool WebMediaPlayerAndroid::supportsSave() const {
330  return false;
331}
332
333void WebMediaPlayerAndroid::setRate(double rate) {
334  NOTIMPLEMENTED();
335}
336
337void WebMediaPlayerAndroid::setVolume(double volume) {
338  player_manager_->SetVolume(player_id_, volume);
339}
340
341bool 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
362bool 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
377bool WebMediaPlayerAndroid::paused() const {
378  return !is_playing_;
379}
380
381bool WebMediaPlayerAndroid::seeking() const {
382  return seeking_;
383}
384
385double 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
396double 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
407double 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
418WebSize WebMediaPlayerAndroid::naturalSize() const {
419  return natural_size_;
420}
421
422WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
423  return network_state_;
424}
425
426WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
427  return ready_state_;
428}
429
430WebTimeRanges WebMediaPlayerAndroid::buffered() const {
431  if (media_source_delegate_)
432    return media_source_delegate_->Buffered();
433  return buffered_;
434}
435
436double 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
445bool WebMediaPlayerAndroid::didLoadingProgress() {
446  bool ret = did_loading_progress_;
447  did_loading_progress_ = false;
448  return ret;
449}
450
451void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
452                                  const blink::WebRect& rect,
453                                  unsigned char alpha) {
454  NOTIMPLEMENTED();
455}
456
457bool 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
530bool 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
550bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
551  if (info_loader_)
552    return info_loader_->DidPassCORSAccessCheck();
553  return false;
554}
555
556double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
557  return ConvertSecondsToTimestamp(timeValue).InSecondsF();
558}
559
560unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
561  if (media_source_delegate_)
562    return media_source_delegate_->DecodedFrameCount();
563  NOTIMPLEMENTED();
564  return 0;
565}
566
567unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
568  if (media_source_delegate_)
569    return media_source_delegate_->DroppedFrameCount();
570  NOTIMPLEMENTED();
571  return 0;
572}
573
574unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
575  if (media_source_delegate_)
576    return media_source_delegate_->AudioDecodedByteCount();
577  NOTIMPLEMENTED();
578  return 0;
579}
580
581unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
582  if (media_source_delegate_)
583    return media_source_delegate_->VideoDecodedByteCount();
584  NOTIMPLEMENTED();
585  return 0;
586}
587
588void 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
625void 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
643void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
644  buffered_[0].end = duration() * percentage / 100;
645  did_loading_progress_ = true;
646}
647
648void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
649  DCHECK(main_thread_checker_.CalledOnValidThread());
650  client_->requestSeek(time_to_seek.InSecondsF());
651}
652
653void 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
675void 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
692void 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
736void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
737  DCHECK(main_thread_checker_.CalledOnValidThread());
738  current_time_ = current_time.InSecondsF();
739}
740
741void 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
750void 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
760void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
761  if (!player_manager_->IsInFullscreen(frame_))
762    player_manager_->DidEnterFullscreen(frame_);
763}
764
765void 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
783void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
784  UpdatePlayingState(true);
785  client_->playbackStateChanged();
786}
787
788void WebMediaPlayerAndroid::OnMediaPlayerPause() {
789  UpdatePlayingState(false);
790  client_->playbackStateChanged();
791}
792
793void WebMediaPlayerAndroid::OnRequestFullscreen() {
794  client_->requestFullscreen();
795}
796
797void 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
812void 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
827void WebMediaPlayerAndroid::UpdateReadyState(
828    WebMediaPlayer::ReadyState state) {
829  ready_state_ = state;
830  client_->readyStateChanged();
831}
832
833void 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
846void 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
869void WebMediaPlayerAndroid::OnDestruct() {
870  NOTREACHED() << "WebMediaPlayer should be destroyed before any "
871                  "RenderFrameObserver::OnDestruct() gets called when "
872                  "the RenderFrame goes away.";
873}
874
875void 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
888void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
889  player_manager_->Pause(player_id_, is_media_related_action);
890  UpdatePlayingState(false);
891}
892
893void 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
1018void 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
1056void 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
1071void WebMediaPlayerAndroid::SetCurrentFrameInternal(
1072    scoped_refptr<media::VideoFrame>& video_frame) {
1073  base::AutoLock auto_lock(current_frame_lock_);
1074  current_frame_ = video_frame;
1075}
1076
1077scoped_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
1095void WebMediaPlayerAndroid::PutCurrentFrame(
1096    const scoped_refptr<media::VideoFrame>& frame) {
1097}
1098
1099void 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
1119void 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
1143void 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
1152void 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
1159void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
1160  needs_establish_peer_ = needs_establish_peer;
1161}
1162
1163void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
1164  player_manager_->SetPoster(player_id_, poster);
1165}
1166
1167void 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)
1178bool 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
1199const 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.
1210static 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
1221static 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
1231static 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.
1241enum MediaKeyException {
1242  kUnknownResultId,
1243  kSuccess,
1244  kKeySystemNotSupported,
1245  kInvalidPlayerState,
1246  kMaxMediaKeyException
1247};
1248
1249static 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.
1265static 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
1274bool 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
1281WebMediaPlayer::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.
1300static 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.
1312WebMediaPlayer::MediaKeyException
1313WebMediaPlayerAndroid::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
1368WebMediaPlayer::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
1396WebMediaPlayer::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
1417WebMediaPlayer::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
1433WebMediaPlayer::MediaKeyException
1434WebMediaPlayerAndroid::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
1446void 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
1465void 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
1473void 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
1494void 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
1507void WebMediaPlayerAndroid::OnMediaSourceOpened(
1508    blink::WebMediaSource* web_media_source) {
1509  client_->mediaSourceOpened(web_media_source);
1510}
1511
1512void 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
1533void 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
1567void WebMediaPlayerAndroid::enterFullscreen() {
1568  if (player_manager_->CanEnterFullscreen(frame_)) {
1569    player_manager_->EnterFullscreen(player_id_, frame_);
1570    SetNeedsEstablishPeer(false);
1571  }
1572}
1573
1574void WebMediaPlayerAndroid::exitFullscreen() {
1575  player_manager_->ExitFullscreen(player_id_);
1576}
1577
1578bool WebMediaPlayerAndroid::canEnterFullscreen() const {
1579  return player_manager_->CanEnterFullscreen(frame_);
1580}
1581
1582}  // namespace content
1583