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