blob: 0537e7b83402a875dcfee0f2238b87da64b7af79 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/android/media_player_renderer.h"
#include <memory>
#include "base/callback_helpers.h"
#include "base/task/post_task.h"
#include "content/browser/android/scoped_surface_request_manager.h"
#include "content/browser/media/android/media_player_renderer_web_contents_observer.h"
#include "content/browser/media/android/media_resource_getter_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "media/base/android/media_service_throttler.h"
#include "media/base/timestamp_constants.h"
// TODO(tguilbert): Remove this ID once MediaPlayerManager has been deleted
// and MediaPlayerBridge updated. See comment in header file.
constexpr int kUnusedAndIrrelevantPlayerId = 0;
namespace content {
namespace {
media::MediaUrlInterceptor* g_media_url_interceptor = nullptr;
const float kDefaultVolume = 1.0;
} // namespace
MediaPlayerRenderer::MediaPlayerRenderer(int process_id,
int routing_id,
WebContents* web_contents)
: render_process_id_(process_id),
routing_id_(routing_id),
has_error_(false),
volume_(kDefaultVolume),
weak_factory_(this) {
DCHECK_EQ(static_cast<RenderFrameHostImpl*>(
RenderFrameHost::FromID(process_id, routing_id))
->delegate()
->GetAsWebContents(),
web_contents);
WebContentsImpl* web_contents_impl =
static_cast<WebContentsImpl*>(web_contents);
web_contents_muted_ = web_contents_impl && web_contents_impl->IsAudioMuted();
if (web_contents) {
MediaPlayerRendererWebContentsObserver::CreateForWebContents(web_contents);
web_contents_observer_ =
MediaPlayerRendererWebContentsObserver::FromWebContents(web_contents);
if (web_contents_observer_)
web_contents_observer_->AddMediaPlayerRenderer(this);
}
}
MediaPlayerRenderer::~MediaPlayerRenderer() {
CancelScopedSurfaceRequest();
if (web_contents_observer_)
web_contents_observer_->RemoveMediaPlayerRenderer(this);
}
void MediaPlayerRenderer::Initialize(media::MediaResource* media_resource,
media::RendererClient* client,
const media::PipelineStatusCB& init_cb) {
DVLOG(1) << __func__;
DCHECK_CURRENTLY_ON(BrowserThread::UI);
renderer_client_ = client;
if (media_resource->GetType() != media::MediaResource::Type::URL) {
DLOG(ERROR) << "MediaResource is not of Type URL";
init_cb.Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
base::TimeDelta creation_delay =
media::MediaServiceThrottler::GetInstance()->GetDelayForClientCreation();
if (creation_delay.is_zero()) {
CreateMediaPlayer(media_resource->GetMediaUrlParams(), init_cb);
return;
}
base::PostDelayedTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::Bind(&MediaPlayerRenderer::CreateMediaPlayer,
weak_factory_.GetWeakPtr(),
media_resource->GetMediaUrlParams(), init_cb),
creation_delay);
}
void MediaPlayerRenderer::CreateMediaPlayer(
const media::MediaUrlParams& url_params,
const media::PipelineStatusCB& init_cb) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Force the initialization of |media_resource_getter_| first. If it fails,
// the RenderFrameHost may have been destroyed already.
if (!GetMediaResourceGetter()) {
DLOG(ERROR) << "Unable to retrieve MediaResourceGetter";
init_cb.Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
const std::string user_agent = GetContentClient()->browser()->GetUserAgent();
media_player_.reset(new media::MediaPlayerBridge(
kUnusedAndIrrelevantPlayerId, url_params.media_url,
url_params.site_for_cookies, user_agent,
false, // hide_url_log
this,
base::Bind(&MediaPlayerRenderer::OnDecoderResourcesReleased,
weak_factory_.GetWeakPtr()),
GURL(), // frame_url
true)); // allow_crendentials
media_player_->Initialize();
UpdateVolume();
init_cb.Run(media::PIPELINE_OK);
}
void MediaPlayerRenderer::SetCdm(media::CdmContext* cdm_context,
const media::CdmAttachedCB& cdm_attached_cb) {
NOTREACHED();
}
void MediaPlayerRenderer::Flush(const base::Closure& flush_cb) {
DVLOG(3) << __func__;
flush_cb.Run();
}
void MediaPlayerRenderer::StartPlayingFrom(base::TimeDelta time) {
// MediaPlayerBridge's Start() is idempotent, except when it has encountered
// an error (in which case, calling Start() again is logged as a new error).
if (has_error_)
return;
media_player_->SeekTo(time);
media_player_->Start();
// WMPI needs to receive a BUFFERING_HAVE_ENOUGH data before sending a
// playback_rate > 0. The MediaPlayer manages its own buffering and will pause
// internally if ever it runs out of data. Sending BUFFERING_HAVE_ENOUGH here
// is always safe.
//
// NOTE: OnBufferingUpdate is triggered whenever the media has buffered or
// played up to a % value between 1-100, and it's not a reliable indicator of
// the buffering state.
//
// TODO(tguilbert): Investigate the effect of this call on UMAs.
renderer_client_->OnBufferingStateChange(media::BUFFERING_HAVE_ENOUGH);
}
void MediaPlayerRenderer::SetPlaybackRate(double playback_rate) {
if (has_error_)
return;
if (playback_rate == 0) {
media_player_->Pause(true);
} else {
// MediaPlayerBridge's Start() is idempotent.
media_player_->Start();
// TODO(tguilbert): MediaPlayer's interface allows variable playback rate,
// but is not currently exposed in the MediaPlayerBridge interface.
// Investigate wether or not we want to add variable playback speed.
}
}
void MediaPlayerRenderer::OnScopedSurfaceRequestCompleted(
gl::ScopedJavaSurface surface) {
DCHECK(surface_request_token_);
surface_request_token_ = base::UnguessableToken();
media_player_->SetVideoSurface(std::move(surface));
}
base::UnguessableToken MediaPlayerRenderer::InitiateScopedSurfaceRequest() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CancelScopedSurfaceRequest();
surface_request_token_ =
ScopedSurfaceRequestManager::GetInstance()->RegisterScopedSurfaceRequest(
base::Bind(&MediaPlayerRenderer::OnScopedSurfaceRequestCompleted,
weak_factory_.GetWeakPtr()));
return surface_request_token_;
}
void MediaPlayerRenderer::SetVolume(float volume) {
volume_ = volume;
UpdateVolume();
}
void MediaPlayerRenderer::UpdateVolume() {
float volume = web_contents_muted_ ? 0 : volume_;
if (media_player_)
media_player_->SetVolume(volume);
}
base::TimeDelta MediaPlayerRenderer::GetMediaTime() {
return media_player_->GetCurrentTime();
}
media::MediaResourceGetter* MediaPlayerRenderer::GetMediaResourceGetter() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!media_resource_getter_.get()) {
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
// The RenderFrameHost/RenderProcessHost may have been destroyed already,
// as there might be a delay between the frame closing and
// MojoRendererService receiving a connection closing error.
if (!host)
return nullptr;
BrowserContext* context = host->GetBrowserContext();
StoragePartition* partition = host->GetStoragePartition();
storage::FileSystemContext* file_system_context =
partition ? partition->GetFileSystemContext() : nullptr;
media_resource_getter_.reset(new MediaResourceGetterImpl(
context, file_system_context, render_process_id_, routing_id_));
}
return media_resource_getter_.get();
}
media::MediaUrlInterceptor* MediaPlayerRenderer::GetMediaUrlInterceptor() {
return g_media_url_interceptor;
}
void MediaPlayerRenderer::OnTimeUpdate(int player_id,
base::TimeDelta current_timestamp,
base::TimeTicks current_time_ticks) {}
void MediaPlayerRenderer::OnMediaMetadataChanged(int player_id,
base::TimeDelta duration,
int width,
int height,
bool success) {
// Always try to propage the video size.
// This call will no-op if |video_size_| is already current.
OnVideoSizeChanged(kUnusedAndIrrelevantPlayerId, width, height);
// For HLS streams, the reported duration may be zero for infinite streams.
// See http://crbug.com/501213.
if (duration.is_zero())
duration = media::kInfiniteDuration;
if (duration_ != duration) {
duration_ = duration;
renderer_client_->OnDurationChange(duration);
}
}
void MediaPlayerRenderer::OnPlaybackComplete(int player_id) {
renderer_client_->OnEnded();
}
void MediaPlayerRenderer::OnMediaInterrupted(int player_id) {}
void MediaPlayerRenderer::OnBufferingUpdate(int player_id, int percentage) {}
void MediaPlayerRenderer::OnSeekComplete(int player_id,
const base::TimeDelta& current_time) {}
void MediaPlayerRenderer::OnError(int player_id, int error) {
// Some errors are forwarded to the MediaPlayerListener, but are of no
// importance to us. Ignore these errors, which are reported as
// MEDIA_ERROR_INVALID_CODE by MediaPlayerListener.
if (error ==
media::MediaPlayerAndroid::MediaErrorType::MEDIA_ERROR_INVALID_CODE) {
return;
}
LOG(ERROR) << __func__ << " Error: " << error;
has_error_ = true;
renderer_client_->OnError(media::PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED);
}
void MediaPlayerRenderer::OnVideoSizeChanged(int player_id,
int width,
int height) {
// This method is called when we find a video size from metadata or when
// |media_player|'s size actually changes.
// We therefore may already have the latest video size.
gfx::Size new_size = gfx::Size(width, height);
if (video_size_ != new_size) {
video_size_ = new_size;
renderer_client_->OnVideoNaturalSizeChange(video_size_);
}
}
media::MediaPlayerAndroid* MediaPlayerRenderer::GetPlayer(int player_id) {
NOTREACHED();
return nullptr;
}
bool MediaPlayerRenderer::RequestPlay(int player_id,
base::TimeDelta duration,
bool has_audio) {
return true;
}
void MediaPlayerRenderer::OnUpdateAudioMutingState(bool muted) {
web_contents_muted_ = muted;
UpdateVolume();
}
void MediaPlayerRenderer::OnWebContentsDestroyed() {
web_contents_observer_ = nullptr;
}
void MediaPlayerRenderer::OnDecoderResourcesReleased(int player_id) {
// Since we are not using a pool of MediaPlayerAndroid instances, this
// function is not relevant.
}
// static
void MediaPlayerRenderer::RegisterMediaUrlInterceptor(
media::MediaUrlInterceptor* media_url_interceptor) {
g_media_url_interceptor = media_url_interceptor;
}
void MediaPlayerRenderer::CancelScopedSurfaceRequest() {
if (!surface_request_token_)
return;
ScopedSurfaceRequestManager::GetInstance()->UnregisterScopedSurfaceRequest(
surface_request_token_);
surface_request_token_ = base::UnguessableToken();
}
} // namespace content