This source file includes following definitions.
- OnReleaseTexture
- weak_factory_
- load
- DidLoadMediaInfo
- play
- pause
- seek
- supportsSave
- setRate
- setVolume
- hasVideo
- hasAudio
- paused
- seeking
- duration
- currentTime
- naturalSize
- networkState
- readyState
- buffered
- maxTimeSeekable
- didLoadingProgress
- paint
- copyVideoTextureToPlatformTexture
- hasSingleSecurityOrigin
- didPassCORSAccessCheck
- mediaTimeForTimeValue
- decodedFrameCount
- droppedFrameCount
- audioDecodedByteCount
- videoDecodedByteCount
- OnMediaMetadataChanged
- OnPlaybackComplete
- OnBufferingUpdate
- OnSeekRequest
- OnSeekComplete
- OnMediaError
- OnVideoSizeChanged
- OnTimeUpdate
- OnConnectedToRemoteDevice
- OnDisconnectedFromRemoteDevice
- OnDidEnterFullscreen
- OnDidExitFullscreen
- OnMediaPlayerPlay
- OnMediaPlayerPause
- OnRequestFullscreen
- OnDurationChanged
- UpdateNetworkState
- UpdateReadyState
- OnPlayerReleased
- ReleaseMediaResources
- OnDestruct
- Detach
- Pause
- DrawRemotePlaybackText
- ReallocateVideoFrame
- SetVideoFrameProviderClient
- SetCurrentFrameInternal
- GetCurrentFrame
- PutCurrentFrame
- TryCreateStreamTextureProxyIfNeeded
- EstablishSurfaceTexturePeer
- DoCreateStreamTexture
- SetNeedsEstablishPeer
- setPoster
- UpdatePlayingState
- UpdateBoundaryRectangle
- GetBoundaryRectangle
- ToASCIIOrEmpty
- EmeUMAHistogramEnumeration
- EmeUMAHistogramCounts
- MediaKeyExceptionForUMA
- ReportMediaKeyExceptionToUMA
- IsKeySystemSupported
- generateKeyRequest
- GuessInitDataType
- GenerateKeyRequestInternal
- addKey
- AddKeyInternal
- cancelKeyRequest
- CancelKeyRequestInternal
- setContentDecryptionModule
- OnKeyAdded
- OnKeyError
- OnKeyMessage
- OnMediaSourceOpened
- OnNeedKey
- SetDecryptorReadyCB
- enterFullscreen
- exitFullscreen
- canEnterFullscreen
#include "content/renderer/media/android/webmediaplayer_android.h"
#include <limits>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/layers/video_layer.h"
#include "content/public/common/content_client.h"
#include "content/public/renderer/render_frame.h"
#include "content/renderer/media/android/renderer_demuxer_android.h"
#include "content/renderer/media/android/renderer_media_player_manager.h"
#include "content/renderer/media/crypto/key_systems.h"
#include "content/renderer/media/webcontentdecryptionmodule_impl.h"
#include "content/renderer/media/webmediaplayer_delegate.h"
#include "content/renderer/media/webmediaplayer_util.h"
#include "content/renderer/render_frame_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/android/media_player_android.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/media_keys.h"
#include "media/base/media_switches.h"
#include "media/base/video_frame.h"
#include "net/base/mime_util.h"
#include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/image/image.h"
#include "webkit/renderer/compositor_bindings/web_layer_impl.h"
static const uint32 kGLTextureExternalOES = 0x8D65;
using blink::WebMediaPlayer;
using blink::WebSize;
using blink::WebString;
using blink::WebTimeRanges;
using blink::WebURL;
using gpu::gles2::GLES2Interface;
using media::MediaPlayerAndroid;
using media::VideoFrame;
namespace {
const char* kMediaEme = "Media.EME.";
void OnReleaseTexture(
const scoped_refptr<content::StreamTextureFactory>& factories,
uint32 texture_id,
scoped_ptr<gpu::MailboxHolder> mailbox_holder) {
GLES2Interface* gl = factories->ContextGL();
if (mailbox_holder->sync_point)
gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point);
gl->DeleteTextures(1, &texture_id);
}
}
namespace content {
WebMediaPlayerAndroid::WebMediaPlayerAndroid(
blink::WebFrame* frame,
blink::WebMediaPlayerClient* client,
base::WeakPtr<WebMediaPlayerDelegate> delegate,
RendererMediaPlayerManager* manager,
scoped_refptr<StreamTextureFactory> factory,
const scoped_refptr<base::MessageLoopProxy>& media_loop,
media::MediaLog* media_log)
: RenderFrameObserver(RenderFrame::FromWebFrame(frame)),
frame_(frame),
client_(client),
delegate_(delegate),
buffered_(static_cast<size_t>(1)),
media_loop_(media_loop),
ignore_metadata_duration_change_(false),
pending_seek_(false),
seeking_(false),
did_loading_progress_(false),
manager_(manager),
network_state_(WebMediaPlayer::NetworkStateEmpty),
ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
texture_id_(0),
stream_id_(0),
is_playing_(false),
playing_started_(false),
needs_establish_peer_(true),
stream_texture_proxy_initialized_(false),
has_size_info_(false),
has_media_metadata_(false),
has_media_info_(false),
stream_texture_factory_(factory),
needs_external_surface_(false),
video_frame_provider_client_(NULL),
pending_playback_(false),
player_type_(MEDIA_PLAYER_TYPE_URL),
current_time_(0),
is_remote_(false),
media_log_(media_log),
web_cdm_(NULL),
weak_factory_(this) {
DCHECK(manager_);
DCHECK(main_thread_checker_.CalledOnValidThread());
player_id_ = manager_->RegisterMediaPlayer(this);
#if defined(VIDEO_HOLE)
needs_establish_peer_ = false;
current_frame_ = VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
#endif
TryCreateStreamTextureProxyIfNeeded();
}
WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
SetVideoFrameProviderClient(NULL);
client_->setWebLayer(NULL);
if (manager_) {
manager_->DestroyPlayer(player_id_);
manager_->UnregisterMediaPlayer(player_id_);
}
if (stream_id_) {
GLES2Interface* gl = stream_texture_factory_->ContextGL();
gl->DeleteTextures(1, &texture_id_);
texture_id_ = 0;
texture_mailbox_ = gpu::Mailbox();
stream_id_ = 0;
}
{
base::AutoLock auto_lock(current_frame_lock_);
current_frame_ = NULL;
}
if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE && delegate_)
delegate_->PlayerGone(this);
}
void WebMediaPlayerAndroid::load(LoadType load_type,
const blink::WebURL& url,
CORSMode cors_mode) {
switch (load_type) {
case LoadTypeURL:
player_type_ = MEDIA_PLAYER_TYPE_URL;
break;
case LoadTypeMediaSource:
player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
break;
case LoadTypeMediaStream:
CHECK(false) << "WebMediaPlayerAndroid doesn't support MediaStream on "
"this platform";
return;
}
has_media_metadata_ = false;
has_media_info_ = false;
int demuxer_client_id = 0;
if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
has_media_info_ = true;
RendererDemuxerAndroid* demuxer =
RenderThreadImpl::current()->renderer_demuxer();
demuxer_client_id = demuxer->GetNextDemuxerClientID();
media_source_delegate_.reset(new MediaSourceDelegate(
demuxer, demuxer_client_id, media_loop_, media_log_));
if (player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
media::SetDecryptorReadyCB set_decryptor_ready_cb =
media::BindToCurrentLoop(
base::Bind(&WebMediaPlayerAndroid::SetDecryptorReadyCB,
weak_factory_.GetWeakPtr()));
media_source_delegate_->InitializeMediaSource(
base::Bind(&WebMediaPlayerAndroid::OnMediaSourceOpened,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnNeedKey,
weak_factory_.GetWeakPtr()),
set_decryptor_ready_cb,
base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
weak_factory_.GetWeakPtr()));
}
} else {
info_loader_.reset(
new MediaInfoLoader(
url,
cors_mode,
base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
weak_factory_.GetWeakPtr())));
info_loader_->Start(frame_);
}
url_ = url;
GURL first_party_url = frame_->document().firstPartyForCookies();
manager_->Initialize(
player_type_, player_id_, url, first_party_url, demuxer_client_id);
if (manager_->ShouldEnterFullscreen(frame_))
manager_->EnterFullscreen(player_id_, frame_);
UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
}
void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
DCHECK(!media_source_delegate_);
if (status == MediaInfoLoader::kFailed) {
info_loader_.reset();
UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError);
return;
}
has_media_info_ = true;
if (has_media_metadata_ &&
ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
}
if (!playing_started_)
UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
}
void WebMediaPlayerAndroid::play() {
#if defined(VIDEO_HOLE)
if (hasVideo() && needs_external_surface_ &&
!manager_->IsInFullscreen(frame_)) {
DCHECK(!needs_establish_peer_);
manager_->RequestExternalSurface(player_id_, last_computed_rect_);
}
#endif
TryCreateStreamTextureProxyIfNeeded();
if (hasVideo() && needs_establish_peer_)
EstablishSurfaceTexturePeer();
if (paused())
manager_->Start(player_id_);
UpdatePlayingState(true);
UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
playing_started_ = true;
}
void WebMediaPlayerAndroid::pause() {
Pause(true);
}
void WebMediaPlayerAndroid::seek(double seconds) {
DCHECK(main_thread_checker_.CalledOnValidThread());
DVLOG(1) << __FUNCTION__ << "(" << seconds << ")";
base::TimeDelta new_seek_time = ConvertSecondsToTimestamp(seconds);
if (seeking_) {
if (new_seek_time == seek_time_) {
if (media_source_delegate_) {
if (!pending_seek_) {
return;
}
} else {
pending_seek_ = false;
return;
}
}
pending_seek_ = true;
pending_seek_time_ = new_seek_time;
if (media_source_delegate_)
media_source_delegate_->CancelPendingSeek(pending_seek_time_);
return;
}
seeking_ = true;
seek_time_ = new_seek_time;
if (media_source_delegate_)
media_source_delegate_->StartWaitingForSeek(seek_time_);
manager_->Seek(player_id_, seek_time_);
}
bool WebMediaPlayerAndroid::supportsSave() const {
return false;
}
void WebMediaPlayerAndroid::setRate(double rate) {
NOTIMPLEMENTED();
}
void WebMediaPlayerAndroid::setVolume(double volume) {
manager_->SetVolume(player_id_, volume);
}
bool WebMediaPlayerAndroid::hasVideo() const {
if (has_size_info_)
return !natural_size_.isEmpty();
if (!url_.has_path())
return false;
std::string mime;
if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
return true;
return mime.find("audio/") == std::string::npos;
}
bool WebMediaPlayerAndroid::hasAudio() const {
if (!url_.has_path())
return false;
std::string mime;
if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime))
return true;
if (mime.find("audio/") != std::string::npos ||
mime.find("video/") != std::string::npos ||
mime.find("application/ogg") != std::string::npos) {
return true;
}
return false;
}
bool WebMediaPlayerAndroid::paused() const {
return !is_playing_;
}
bool WebMediaPlayerAndroid::seeking() const {
return seeking_;
}
double WebMediaPlayerAndroid::duration() const {
if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing)
return std::numeric_limits<double>::quiet_NaN();
if (duration_ == media::kInfiniteDuration())
return std::numeric_limits<double>::infinity();
return duration_.InSecondsF();
}
double WebMediaPlayerAndroid::currentTime() const {
if (seeking()) {
return pending_seek_ ?
pending_seek_time_.InSecondsF() : seek_time_.InSecondsF();
}
return current_time_;
}
WebSize WebMediaPlayerAndroid::naturalSize() const {
return natural_size_;
}
WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
return network_state_;
}
WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
return ready_state_;
}
const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
if (media_source_delegate_)
return media_source_delegate_->Buffered();
return buffered_;
}
double WebMediaPlayerAndroid::maxTimeSeekable() const {
if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata)
return 0.0;
if (duration() == std::numeric_limits<double>::infinity())
return 0.0;
return std::min(std::numeric_limits<int32>::max() / 1000.0, duration());
}
bool WebMediaPlayerAndroid::didLoadingProgress() const {
bool ret = did_loading_progress_;
did_loading_progress_ = false;
return ret;
}
void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
const blink::WebRect& rect,
unsigned char alpha) {
NOTIMPLEMENTED();
}
bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture(
blink::WebGraphicsContext3D* web_graphics_context,
unsigned int texture,
unsigned int level,
unsigned int internal_format,
unsigned int type,
bool premultiply_alpha,
bool flip_y) {
if (needs_external_surface_)
return false;
scoped_refptr<VideoFrame> video_frame;
{
base::AutoLock auto_lock(current_frame_lock_);
video_frame = current_frame_;
}
if (!video_frame ||
video_frame->format() != media::VideoFrame::NATIVE_TEXTURE)
return false;
gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
DCHECK((!is_remote_ &&
mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) ||
(is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D));
if (!is_remote_ &&
(cached_stream_texture_size_.width != natural_size_.width ||
cached_stream_texture_size_.height != natural_size_.height)) {
stream_texture_factory_->SetStreamTextureSize(
stream_id_, gfx::Size(natural_size_.width, natural_size_.height));
cached_stream_texture_size_ = natural_size_;
}
uint32 source_texture = web_graphics_context->createTexture();
web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
web_graphics_context->bindTexture(mailbox_holder->texture_target,
source_texture);
web_graphics_context->consumeTextureCHROMIUM(mailbox_holder->texture_target,
mailbox_holder->mailbox.name);
web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
premultiply_alpha);
web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, source_texture,
texture, level, internal_format,
type);
web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
false);
if (mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES)
web_graphics_context->bindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
else
web_graphics_context->bindTexture(GL_TEXTURE_2D, texture);
web_graphics_context->deleteTexture(source_texture);
web_graphics_context->flush();
return true;
}
bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
if (info_loader_)
return info_loader_->HasSingleOrigin();
if (player_type_ == MEDIA_PLAYER_TYPE_URL)
return false;
return true;
}
bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
if (info_loader_)
return info_loader_->DidPassCORSAccessCheck();
return false;
}
double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const {
return ConvertSecondsToTimestamp(timeValue).InSecondsF();
}
unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
if (media_source_delegate_)
return media_source_delegate_->DecodedFrameCount();
NOTIMPLEMENTED();
return 0;
}
unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
if (media_source_delegate_)
return media_source_delegate_->DroppedFrameCount();
NOTIMPLEMENTED();
return 0;
}
unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
if (media_source_delegate_)
return media_source_delegate_->AudioDecodedByteCount();
NOTIMPLEMENTED();
return 0;
}
unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
if (media_source_delegate_)
return media_source_delegate_->VideoDecodedByteCount();
NOTIMPLEMENTED();
return 0;
}
void WebMediaPlayerAndroid::OnMediaMetadataChanged(
const base::TimeDelta& duration, int width, int height, bool success) {
bool need_to_signal_duration_changed = false;
if (url_.SchemeIs("file"))
UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded);
if (!ignore_metadata_duration_change_ && duration_ != duration) {
duration_ = duration;
if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing &&
player_type_ != MEDIA_PLAYER_TYPE_MEDIA_SOURCE) {
need_to_signal_duration_changed = true;
}
}
has_media_metadata_ = true;
if (has_media_info_ &&
ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
}
if (success)
OnVideoSizeChanged(width, height);
if (need_to_signal_duration_changed)
client_->durationChanged();
}
void WebMediaPlayerAndroid::OnPlaybackComplete() {
OnTimeUpdate(duration_);
client_->timeChanged();
is_playing_ = false;
if (seeking_ && seek_time_ == base::TimeDelta())
pending_playback_ = true;
}
void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
buffered_[0].end = duration() * percentage / 100;
did_loading_progress_ = true;
}
void WebMediaPlayerAndroid::OnSeekRequest(const base::TimeDelta& time_to_seek) {
DCHECK(main_thread_checker_.CalledOnValidThread());
client_->requestSeek(time_to_seek.InSecondsF());
}
void WebMediaPlayerAndroid::OnSeekComplete(
const base::TimeDelta& current_time) {
DCHECK(main_thread_checker_.CalledOnValidThread());
seeking_ = false;
if (pending_seek_) {
pending_seek_ = false;
seek(pending_seek_time_.InSecondsF());
return;
}
OnTimeUpdate(current_time);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
client_->timeChanged();
if (pending_playback_) {
play();
pending_playback_ = false;
}
}
void WebMediaPlayerAndroid::OnMediaError(int error_type) {
switch (error_type) {
case MediaPlayerAndroid::MEDIA_ERROR_FORMAT:
UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
break;
case MediaPlayerAndroid::MEDIA_ERROR_DECODE:
UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError);
break;
case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError);
break;
case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE:
break;
}
client_->repaint();
}
void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
has_size_info_ = true;
if (natural_size_.width == width && natural_size_.height == height)
return;
#if defined(VIDEO_HOLE)
if (media_source_delegate_ && media_source_delegate_->IsVideoEncrypted()) {
needs_external_surface_ = true;
if (!paused() && !manager_->IsInFullscreen(frame_))
manager_->RequestExternalSurface(player_id_, last_computed_rect_);
} else if (stream_texture_factory_ && !stream_id_) {
DoCreateStreamTexture();
if (paused()) {
SetNeedsEstablishPeer(true);
} else {
EstablishSurfaceTexturePeer();
}
}
#else
if (!paused() && needs_establish_peer_)
EstablishSurfaceTexturePeer();
#endif
natural_size_.width = width;
natural_size_.height = height;
ReallocateVideoFrame();
if (!video_weblayer_) {
video_weblayer_.reset(
new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
client_->setWebLayer(video_weblayer_.get());
}
client_->timeChanged();
}
void WebMediaPlayerAndroid::OnTimeUpdate(const base::TimeDelta& current_time) {
DCHECK(main_thread_checker_.CalledOnValidThread());
current_time_ = current_time.InSecondsF();
}
void WebMediaPlayerAndroid::OnConnectedToRemoteDevice(
const std::string& remote_playback_message) {
DCHECK(main_thread_checker_.CalledOnValidThread());
DCHECK(!media_source_delegate_);
DrawRemotePlaybackText(remote_playback_message);
is_remote_ = true;
SetNeedsEstablishPeer(false);
}
void WebMediaPlayerAndroid::OnDisconnectedFromRemoteDevice() {
DCHECK(main_thread_checker_.CalledOnValidThread());
DCHECK(!media_source_delegate_);
SetNeedsEstablishPeer(true);
if (!paused())
EstablishSurfaceTexturePeer();
is_remote_ = false;
ReallocateVideoFrame();
}
void WebMediaPlayerAndroid::OnDidEnterFullscreen() {
if (!manager_->IsInFullscreen(frame_)) {
frame_->view()->willEnterFullScreen();
frame_->view()->didEnterFullScreen();
manager_->DidEnterFullscreen(frame_);
}
}
void WebMediaPlayerAndroid::OnDidExitFullscreen() {
if (!needs_external_surface_)
SetNeedsEstablishPeer(true);
if (!paused() && needs_establish_peer_)
EstablishSurfaceTexturePeer();
#if defined(VIDEO_HOLE)
if (!paused() && needs_external_surface_)
manager_->RequestExternalSurface(player_id_, last_computed_rect_);
#endif
frame_->view()->willExitFullScreen();
frame_->view()->didExitFullScreen();
manager_->DidExitFullscreen();
client_->repaint();
}
void WebMediaPlayerAndroid::OnMediaPlayerPlay() {
UpdatePlayingState(true);
client_->playbackStateChanged();
}
void WebMediaPlayerAndroid::OnMediaPlayerPause() {
UpdatePlayingState(false);
client_->playbackStateChanged();
}
void WebMediaPlayerAndroid::OnRequestFullscreen() {
client_->requestFullscreen();
}
void WebMediaPlayerAndroid::OnDurationChanged(const base::TimeDelta& duration) {
DCHECK(main_thread_checker_.CalledOnValidThread());
DCHECK_EQ(player_type_, MEDIA_PLAYER_TYPE_MEDIA_SOURCE);
duration_ = duration;
ignore_metadata_duration_change_ = true;
if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing)
client_->durationChanged();
}
void WebMediaPlayerAndroid::UpdateNetworkState(
WebMediaPlayer::NetworkState state) {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing &&
(state == WebMediaPlayer::NetworkStateNetworkError ||
state == WebMediaPlayer::NetworkStateDecodeError)) {
network_state_ = WebMediaPlayer::NetworkStateFormatError;
} else {
network_state_ = state;
}
client_->networkStateChanged();
}
void WebMediaPlayerAndroid::UpdateReadyState(
WebMediaPlayer::ReadyState state) {
ready_state_ = state;
client_->readyStateChanged();
}
void WebMediaPlayerAndroid::OnPlayerReleased() {
if (!needs_external_surface_)
needs_establish_peer_ = true;
if (is_playing_)
OnMediaPlayerPause();
#if defined(VIDEO_HOLE)
last_computed_rect_ = gfx::RectF();
#endif
}
void WebMediaPlayerAndroid::ReleaseMediaResources() {
switch (network_state_) {
case WebMediaPlayer::NetworkStateIdle:
case WebMediaPlayer::NetworkStateLoading:
case WebMediaPlayer::NetworkStateLoaded:
Pause(false);
client_->playbackStateChanged();
break;
case WebMediaPlayer::NetworkStateEmpty:
case WebMediaPlayer::NetworkStateFormatError:
case WebMediaPlayer::NetworkStateNetworkError:
case WebMediaPlayer::NetworkStateDecodeError:
break;
}
manager_->ReleaseResources(player_id_);
OnPlayerReleased();
}
void WebMediaPlayerAndroid::OnDestruct() {
if (manager_)
manager_->UnregisterMediaPlayer(player_id_);
Detach();
}
void WebMediaPlayerAndroid::Detach() {
if (stream_id_) {
GLES2Interface* gl = stream_texture_factory_->ContextGL();
gl->DeleteTextures(1, &texture_id_);
texture_id_ = 0;
texture_mailbox_ = gpu::Mailbox();
stream_id_ = 0;
}
media_source_delegate_.reset();
{
base::AutoLock auto_lock(current_frame_lock_);
current_frame_ = NULL;
}
is_remote_ = false;
manager_ = NULL;
}
void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
manager_->Pause(player_id_, is_media_related_action);
UpdatePlayingState(false);
}
void WebMediaPlayerAndroid::DrawRemotePlaybackText(
const std::string& remote_playback_message) {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (!video_weblayer_)
return;
gfx::Size video_size_css_px = video_weblayer_->bounds();
float device_scale_factor = frame_->view()->deviceScaleFactor();
gfx::Size canvas_size(
static_cast<int>(video_size_css_px.width() * device_scale_factor),
static_cast<int>(video_size_css_px.height() * device_scale_factor));
SkBitmap bitmap;
bitmap.setConfig(
SkBitmap::kARGB_8888_Config, canvas_size.width(), canvas_size.height());
bitmap.allocPixels();
SkCanvas canvas(bitmap);
canvas.drawColor(SK_ColorBLACK);
const SkScalar kTextSize(40);
const SkScalar kMinPadding(40);
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
paint.setColor(SK_ColorWHITE);
paint.setTypeface(SkTypeface::CreateFromName("sans", SkTypeface::kBold));
paint.setTextSize(kTextSize);
SkPaint::FontMetrics font_metrics;
paint.getFontMetrics(&font_metrics);
SkScalar sk_vertical_margin = kMinPadding - font_metrics.fAscent;
size_t display_text_width = paint.measureText(
remote_playback_message.c_str(), remote_playback_message.size());
std::string display_text(remote_playback_message);
if (display_text_width + (kMinPadding * 2) > canvas_size.width()) {
const std::string kTruncationEllipsis("\xE2\x80\xA6");
SkScalar sk_ellipse_width = paint.measureText(
kTruncationEllipsis.c_str(), kTruncationEllipsis.size());
SkScalar sk_max_original_text_width(
canvas_size.width() - (kMinPadding * 2) - sk_ellipse_width);
size_t sk_max_original_text_length = paint.breakText(
remote_playback_message.c_str(),
remote_playback_message.size(),
sk_max_original_text_width);
display_text.erase(sk_max_original_text_length,
remote_playback_message.size() - sk_max_original_text_length);
display_text.append(kTruncationEllipsis);
display_text_width = paint.measureText(
display_text.c_str(), display_text.size());
}
SkScalar sk_horizontal_margin =
(canvas_size.width() - display_text_width) / 2.0;
canvas.drawText(display_text.c_str(),
display_text.size(),
sk_horizontal_margin,
sk_vertical_margin,
paint);
GLES2Interface* gl = stream_texture_factory_->ContextGL();
GLuint remote_playback_texture_id = 0;
gl->GenTextures(1, &remote_playback_texture_id);
GLuint texture_target = GL_TEXTURE_2D;
gl->BindTexture(texture_target, remote_playback_texture_id);
gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
{
SkAutoLockPixels lock(bitmap);
gl->TexImage2D(texture_target,
0 ,
GL_RGBA ,
bitmap.width(),
bitmap.height(),
0 ,
GL_RGBA ,
GL_UNSIGNED_BYTE ,
bitmap.getPixels());
}
gpu::Mailbox texture_mailbox;
gl->GenMailboxCHROMIUM(texture_mailbox.name);
gl->ProduceTextureCHROMIUM(texture_target, texture_mailbox.name);
gl->Flush();
GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
make_scoped_ptr(new gpu::MailboxHolder(
texture_mailbox, texture_target, texture_mailbox_sync_point)),
media::BindToCurrentLoop(base::Bind(&OnReleaseTexture,
stream_texture_factory_,
remote_playback_texture_id)),
canvas_size ,
gfx::Rect(canvas_size) ,
canvas_size ,
base::TimeDelta() ,
VideoFrame::ReadPixelsCB());
SetCurrentFrameInternal(new_frame);
}
void WebMediaPlayerAndroid::ReallocateVideoFrame() {
if (needs_external_surface_) {
#if defined(VIDEO_HOLE)
if (!natural_size_.isEmpty()) {
scoped_refptr<VideoFrame> new_frame =
VideoFrame::CreateHoleFrame(natural_size_);
SetCurrentFrameInternal(new_frame);
client_->repaint();
}
#else
NOTIMPLEMENTED() << "Hole punching not supported without VIDEO_HOLE flag";
#endif
} else if (!is_remote_ && texture_id_) {
GLES2Interface* gl = stream_texture_factory_->ContextGL();
GLuint texture_id_ref = 0;
gl->GenTextures(1, &texture_id_ref);
GLuint texture_target = kGLTextureExternalOES;
gl->BindTexture(texture_target, texture_id_ref);
gl->ConsumeTextureCHROMIUM(texture_target, texture_mailbox_.name);
gl->Flush();
GLuint texture_mailbox_sync_point = gl->InsertSyncPointCHROMIUM();
scoped_refptr<VideoFrame> new_frame = VideoFrame::WrapNativeTexture(
make_scoped_ptr(new gpu::MailboxHolder(
texture_mailbox_, texture_target, texture_mailbox_sync_point)),
media::BindToCurrentLoop(base::Bind(
&OnReleaseTexture, stream_texture_factory_, texture_id_ref)),
natural_size_,
gfx::Rect(natural_size_),
natural_size_,
base::TimeDelta(),
VideoFrame::ReadPixelsCB());
SetCurrentFrameInternal(new_frame);
}
}
void WebMediaPlayerAndroid::SetVideoFrameProviderClient(
cc::VideoFrameProvider::Client* client) {
if (video_frame_provider_client_)
video_frame_provider_client_->StopUsingProvider();
video_frame_provider_client_ = client;
if (stream_texture_proxy_)
stream_texture_proxy_->SetClient(client);
}
void WebMediaPlayerAndroid::SetCurrentFrameInternal(
scoped_refptr<media::VideoFrame>& video_frame) {
base::AutoLock auto_lock(current_frame_lock_);
current_frame_ = video_frame;
}
scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() {
scoped_refptr<VideoFrame> video_frame;
{
base::AutoLock auto_lock(current_frame_lock_);
video_frame = current_frame_;
}
if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ &&
stream_id_ && !needs_external_surface_ && !is_remote_) {
gfx::Size natural_size = video_frame->natural_size();
stream_texture_proxy_->BindToCurrentThread(stream_id_);
stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size);
stream_texture_proxy_initialized_ = true;
cached_stream_texture_size_ = natural_size;
}
return video_frame;
}
void WebMediaPlayerAndroid::PutCurrentFrame(
const scoped_refptr<media::VideoFrame>& frame) {
}
void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() {
if (stream_texture_proxy_)
return;
if (!stream_texture_factory_)
return;
stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy());
if (needs_establish_peer_ && stream_texture_proxy_) {
DoCreateStreamTexture();
ReallocateVideoFrame();
}
if (stream_texture_proxy_ && video_frame_provider_client_)
stream_texture_proxy_->SetClient(video_frame_provider_client_);
}
void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() {
if (!stream_texture_proxy_)
return;
if (stream_texture_factory_.get() && stream_id_)
stream_texture_factory_->EstablishPeer(stream_id_, player_id_);
needs_establish_peer_ = false;
}
void WebMediaPlayerAndroid::DoCreateStreamTexture() {
DCHECK(!stream_id_);
DCHECK(!texture_id_);
stream_id_ = stream_texture_factory_->CreateStreamTexture(
kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
}
void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) {
needs_establish_peer_ = needs_establish_peer;
}
void WebMediaPlayerAndroid::setPoster(const blink::WebURL& poster) {
manager_->SetPoster(player_id_, poster);
}
void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) {
is_playing_ = is_playing;
if (!delegate_)
return;
if (is_playing)
delegate_->DidPlay(this);
else
delegate_->DidPause(this);
}
#if defined(VIDEO_HOLE)
bool WebMediaPlayerAndroid::UpdateBoundaryRectangle() {
if (!video_weblayer_)
return false;
cc::Layer* layer = video_weblayer_->layer();
gfx::RectF rect(layer->bounds());
while (layer) {
rect.Offset(layer->position().OffsetFromOrigin());
layer = layer->parent();
}
if (last_computed_rect_ == rect)
return false;
last_computed_rect_ = rect;
return true;
}
const gfx::RectF WebMediaPlayerAndroid::GetBoundaryRectangle() {
return last_computed_rect_;
}
#endif
static std::string ToASCIIOrEmpty(const blink::WebString& string) {
return IsStringASCII(string) ? base::UTF16ToASCII(string) : std::string();
}
static void EmeUMAHistogramEnumeration(const std::string& key_system,
const std::string& method,
int sample,
int boundary_value) {
base::LinearHistogram::FactoryGet(
kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1, boundary_value, boundary_value + 1,
base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
}
static void EmeUMAHistogramCounts(const std::string& key_system,
const std::string& method,
int sample) {
base::Histogram::FactoryGet(
kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
}
enum MediaKeyException {
kUnknownResultId,
kSuccess,
kKeySystemNotSupported,
kInvalidPlayerState,
kMaxMediaKeyException
};
static MediaKeyException MediaKeyExceptionForUMA(
WebMediaPlayer::MediaKeyException e) {
switch (e) {
case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
return kKeySystemNotSupported;
case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
return kInvalidPlayerState;
case WebMediaPlayer::MediaKeyExceptionNoError:
return kSuccess;
default:
return kUnknownResultId;
}
}
static void ReportMediaKeyExceptionToUMA(const std::string& method,
const std::string& key_system,
WebMediaPlayer::MediaKeyException e) {
MediaKeyException result_id = MediaKeyExceptionForUMA(e);
DCHECK_NE(result_id, kUnknownResultId) << e;
EmeUMAHistogramEnumeration(
key_system, method, result_id, kMaxMediaKeyException);
}
bool WebMediaPlayerAndroid::IsKeySystemSupported(
const std::string& key_system) {
return player_type_ == MEDIA_PLAYER_TYPE_MEDIA_SOURCE &&
IsConcreteSupportedKeySystem(key_system);
}
WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest(
const WebString& key_system,
const unsigned char* init_data,
unsigned init_data_length) {
DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
<< std::string(reinterpret_cast<const char*>(init_data),
static_cast<size_t>(init_data_length));
std::string ascii_key_system =
GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
WebMediaPlayer::MediaKeyException e =
GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
return e;
}
static std::string GuessInitDataType(const unsigned char* init_data,
unsigned init_data_length) {
if (init_data_length == 16)
return "video/webm";
return "video/mp4";
}
WebMediaPlayer::MediaKeyException
WebMediaPlayerAndroid::GenerateKeyRequestInternal(
const std::string& key_system,
const unsigned char* init_data,
unsigned init_data_length) {
if (!IsKeySystemSupported(key_system))
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
if (current_key_system_.empty()) {
if (!proxy_decryptor_) {
proxy_decryptor_.reset(new ProxyDecryptor(
manager_,
base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyError,
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
weak_factory_.GetWeakPtr())));
}
GURL security_origin(frame_->document().securityOrigin().toString());
if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
if (!decryptor_ready_cb_.is_null()) {
base::ResetAndReturn(&decryptor_ready_cb_)
.Run(proxy_decryptor_->GetDecryptor());
}
manager_->SetCdm(player_id_, proxy_decryptor_->GetCdmId());
current_key_system_ = key_system;
} else if (key_system != current_key_system_) {
return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
}
std::string init_data_type = init_data_type_;
if (init_data_type.empty())
init_data_type = GuessInitDataType(init_data, init_data_length);
if (!proxy_decryptor_->GenerateKeyRequest(
init_data_type, init_data, init_data_length)) {
current_key_system_.clear();
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
}
return WebMediaPlayer::MediaKeyExceptionNoError;
}
WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey(
const WebString& key_system,
const unsigned char* key,
unsigned key_length,
const unsigned char* init_data,
unsigned init_data_length,
const WebString& session_id) {
DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
<< std::string(reinterpret_cast<const char*>(key),
static_cast<size_t>(key_length)) << ", "
<< std::string(reinterpret_cast<const char*>(init_data),
static_cast<size_t>(init_data_length)) << " ["
<< base::string16(session_id) << "]";
std::string ascii_key_system =
GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
std::string ascii_session_id = ToASCIIOrEmpty(session_id);
WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
key,
key_length,
init_data,
init_data_length,
ascii_session_id);
ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
return e;
}
WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal(
const std::string& key_system,
const unsigned char* key,
unsigned key_length,
const unsigned char* init_data,
unsigned init_data_length,
const std::string& session_id) {
DCHECK(key);
DCHECK_GT(key_length, 0u);
if (!IsKeySystemSupported(key_system))
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
if (current_key_system_.empty() || key_system != current_key_system_)
return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
proxy_decryptor_->AddKey(
key, key_length, init_data, init_data_length, session_id);
return WebMediaPlayer::MediaKeyExceptionNoError;
}
WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest(
const WebString& key_system,
const WebString& session_id) {
DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
<< " [" << base::string16(session_id) << "]";
std::string ascii_key_system =
GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
std::string ascii_session_id = ToASCIIOrEmpty(session_id);
WebMediaPlayer::MediaKeyException e =
CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
return e;
}
WebMediaPlayer::MediaKeyException
WebMediaPlayerAndroid::CancelKeyRequestInternal(const std::string& key_system,
const std::string& session_id) {
if (!IsKeySystemSupported(key_system))
return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
if (current_key_system_.empty() || key_system != current_key_system_)
return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
proxy_decryptor_->CancelKeyRequest(session_id);
return WebMediaPlayer::MediaKeyExceptionNoError;
}
void WebMediaPlayerAndroid::setContentDecryptionModule(
blink::WebContentDecryptionModule* cdm) {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (!cdm)
return;
web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
if (!web_cdm_)
return;
if (!decryptor_ready_cb_.is_null())
base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor());
manager_->SetCdm(player_id_, web_cdm_->GetCdmId());
}
void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) {
EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
client_->keyAdded(
WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
WebString::fromUTF8(session_id));
}
void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id,
media::MediaKeys::KeyError error_code,
uint32 system_code) {
EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
error_code, media::MediaKeys::kMaxKeyError);
unsigned short short_system_code = 0;
if (system_code > std::numeric_limits<unsigned short>::max()) {
LOG(WARNING) << "system_code exceeds unsigned short limit.";
short_system_code = std::numeric_limits<unsigned short>::max();
} else {
short_system_code = static_cast<unsigned short>(system_code);
}
client_->keyError(
WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
WebString::fromUTF8(session_id),
static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
short_system_code);
}
void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id,
const std::vector<uint8>& message,
const std::string& destination_url) {
const GURL destination_url_gurl(destination_url);
DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid())
<< "Invalid URL in destination_url: " << destination_url;
client_->keyMessage(
WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
WebString::fromUTF8(session_id),
message.empty() ? NULL : &message[0],
message.size(),
destination_url_gurl);
}
void WebMediaPlayerAndroid::OnMediaSourceOpened(
blink::WebMediaSource* web_media_source) {
client_->mediaSourceOpened(web_media_source);
}
void WebMediaPlayerAndroid::OnNeedKey(const std::string& type,
const std::vector<uint8>& init_data) {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
!blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
return;
}
UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
if (init_data_type_.empty())
init_data_type_ = type;
const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
client_->keyNeeded(
WebString::fromUTF8(type), init_data_ptr, init_data.size());
}
void WebMediaPlayerAndroid::SetDecryptorReadyCB(
const media::DecryptorReadyCB& decryptor_ready_cb) {
DCHECK(main_thread_checker_.CalledOnValidThread());
if (decryptor_ready_cb.is_null()) {
if (!decryptor_ready_cb_.is_null())
base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
return;
}
DCHECK(decryptor_ready_cb_.is_null());
DCHECK(!proxy_decryptor_ || !web_cdm_);
if (proxy_decryptor_) {
decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor());
return;
}
if (web_cdm_) {
decryptor_ready_cb.Run(web_cdm_->GetDecryptor());
return;
}
decryptor_ready_cb_ = decryptor_ready_cb;
}
void WebMediaPlayerAndroid::enterFullscreen() {
if (manager_->CanEnterFullscreen(frame_)) {
manager_->EnterFullscreen(player_id_, frame_);
SetNeedsEstablishPeer(false);
}
}
void WebMediaPlayerAndroid::exitFullscreen() {
manager_->ExitFullscreen(player_id_);
}
bool WebMediaPlayerAndroid::canEnterFullscreen() const {
return manager_->CanEnterFullscreen(frame_);
}
}