blob: dc57bd8db2a49d9de510d84a07c7817ff2f9626c [file] [log] [blame]
// Copyright 2013 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/browser_media_player_manager.h"
#include <utility>
#include "base/android/scoped_java_ref.h"
#include "base/memory/singleton.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/media/android/media_resource_getter_impl.h"
#include "content/browser/media/android/media_web_contents_observer_android.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/media/media_player_messages_android.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "media/base/android/media_player_bridge.h"
#include "media/base/android/media_url_interceptor.h"
#include "media/base/media_content_type.h"
#if !defined(USE_AURA)
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#endif
using media::MediaPlayerAndroid;
using media::MediaPlayerBridge;
using media::MediaPlayerManager;
namespace content {
// Threshold on the number of media players per renderer before we start
// attempting to release inactive media players.
const int kMediaPlayerThreshold = 1;
static BrowserMediaPlayerManager::Factory
g_browser_media_player_manager_factory = NULL;
static media::MediaUrlInterceptor* media_url_interceptor_ = NULL;
// static
void BrowserMediaPlayerManager::RegisterFactory(Factory factory) {
// TODO(aberent) nullptr test is a temporary fix to simplify upstreaming Cast.
// Until Cast is fully upstreamed we want the downstream factory to take
// priority over the upstream factory. The downstream call happens first,
// so this will ensure that it does.
if (g_browser_media_player_manager_factory == nullptr)
g_browser_media_player_manager_factory = factory;
}
// static
void BrowserMediaPlayerManager::RegisterMediaUrlInterceptor(
media::MediaUrlInterceptor* media_url_interceptor) {
media_url_interceptor_ = media_url_interceptor;
}
// static
BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create(
RenderFrameHost* rfh) {
// In chrome, |g_browser_media_player_manager_factory| should be set
// to create a RemoteMediaPlayerManager, since RegisterFactory()
// should be called from
// ChromeMainDelegateAndroid::BasicStartupComplete.
//
// In webview, no factory should be set, and returning a nullptr should be
// handled by the caller.
return g_browser_media_player_manager_factory != nullptr
? g_browser_media_player_manager_factory(rfh)
: nullptr;
}
std::unique_ptr<MediaPlayerAndroid>
BrowserMediaPlayerManager::CreateMediaPlayer(
const MediaPlayerHostMsg_Initialize_Params& media_player_params,
bool hide_url_log) {
switch (media_player_params.type) {
case MEDIA_PLAYER_TYPE_REMOTE_ONLY:
case MEDIA_PLAYER_TYPE_URL: {
const std::string user_agent =
GetContentClient()->browser()->GetUserAgent();
auto media_player_bridge = std::make_unique<MediaPlayerBridge>(
media_player_params.player_id, media_player_params.url,
media_player_params.site_for_cookies, user_agent, hide_url_log, this,
base::Bind(&BrowserMediaPlayerManager::OnDecoderResourcesReleased,
weak_ptr_factory_.GetWeakPtr()),
media_player_params.frame_url, media_player_params.allow_credentials);
if (media_player_params.type == MEDIA_PLAYER_TYPE_REMOTE_ONLY)
return std::move(media_player_bridge);
bool should_block = false;
bool extract_metadata =
// Initialize the player will cause MediaMetadataExtractor to decode
// small chunks of data.
RequestDecoderResources(media_player_params.player_id, true);
#if !defined(USE_AURA)
if (WebContentsDelegate* delegate = web_contents_->GetDelegate()) {
should_block =
delegate->ShouldBlockMediaRequest(media_player_params.url);
} else {
extract_metadata = false;
}
#endif
if (!extract_metadata) {
// May reach here due to prerendering or throttling. Don't extract the
// metadata since it is expensive.
// TODO(qinmin): extract the metadata once the user decided to load
// the page.
OnMediaMetadataChanged(media_player_params.player_id, base::TimeDelta(),
0, 0, false);
} else if (!should_block) {
media_player_bridge->Initialize();
}
return std::move(media_player_bridge);
}
}
NOTREACHED();
return nullptr;
}
BrowserMediaPlayerManager::BrowserMediaPlayerManager(
RenderFrameHost* render_frame_host)
: render_frame_host_(render_frame_host),
web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
weak_ptr_factory_(this) {
}
BrowserMediaPlayerManager::~BrowserMediaPlayerManager() {
// During the tear down process, OnDestroyPlayer() may or may not be called
// (e.g. the WebContents may be destroyed before the render process). So
// we cannot DCHECK(players_.empty()) here. Instead, all media players in
// |players_| will be destroyed here because |player_| is a
// std::vector<std::unique_ptr<>>.
for (auto& player : players_)
player.release()->DeleteOnCorrectThread();
players_.clear();
}
void BrowserMediaPlayerManager::OnTimeUpdate(
int player_id,
base::TimeDelta current_timestamp,
base::TimeTicks current_time_ticks) {
Send(new MediaPlayerMsg_MediaTimeUpdate(
RoutingID(), player_id, current_timestamp, current_time_ticks));
}
void BrowserMediaPlayerManager::OnMediaMetadataChanged(
int player_id, base::TimeDelta duration, int width, int height,
bool success) {
Send(new MediaPlayerMsg_MediaMetadataChanged(
RoutingID(), player_id, duration, width, height, success));
}
void BrowserMediaPlayerManager::OnPlaybackComplete(int player_id) {
Send(new MediaPlayerMsg_MediaPlaybackCompleted(RoutingID(), player_id));
}
void BrowserMediaPlayerManager::OnMediaInterrupted(int player_id) {
// Tell WebKit that the audio should be paused, then release all resources
Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(), player_id));
ReleaseResources(player_id);
}
void BrowserMediaPlayerManager::OnBufferingUpdate(int player_id,
int percentage) {
Send(new MediaPlayerMsg_MediaBufferingUpdate(RoutingID(), player_id,
percentage));
}
void BrowserMediaPlayerManager::OnSeekRequest(
int player_id,
const base::TimeDelta& time_to_seek) {
Send(new MediaPlayerMsg_SeekRequest(RoutingID(), player_id, time_to_seek));
}
void BrowserMediaPlayerManager::OnSeekComplete(
int player_id,
const base::TimeDelta& current_time) {
Send(new MediaPlayerMsg_SeekCompleted(RoutingID(), player_id, current_time));
}
void BrowserMediaPlayerManager::OnError(int player_id, int error) {
Send(new MediaPlayerMsg_MediaError(RoutingID(), player_id, error));
}
void BrowserMediaPlayerManager::OnVideoSizeChanged(
int player_id, int width, int height) {
Send(new MediaPlayerMsg_MediaVideoSizeChanged(RoutingID(), player_id,
width, height));
}
media::MediaResourceGetter*
BrowserMediaPlayerManager::GetMediaResourceGetter() {
if (!media_resource_getter_.get()) {
RenderProcessHost* host = web_contents()->GetMainFrame()->GetProcess();
BrowserContext* context = host->GetBrowserContext();
StoragePartition* partition = host->GetStoragePartition();
storage::FileSystemContext* file_system_context =
partition ? partition->GetFileSystemContext() : NULL;
// Eventually this needs to be fixed to pass the correct frame rather
// than just using the main frame.
media_resource_getter_.reset(new MediaResourceGetterImpl(
context,
file_system_context,
host->GetID(),
web_contents()->GetMainFrame()->GetRoutingID()));
}
return media_resource_getter_.get();
}
media::MediaUrlInterceptor*
BrowserMediaPlayerManager::GetMediaUrlInterceptor() {
return media_url_interceptor_;
}
MediaPlayerAndroid* BrowserMediaPlayerManager::GetPlayer(int player_id) {
for (const auto& player : players_) {
if (player->player_id() == player_id)
return player.get();
}
return nullptr;
}
bool BrowserMediaPlayerManager::RequestPlay(int player_id,
base::TimeDelta duration,
bool has_audio) {
DCHECK(player_id_to_delegate_id_map_.find(player_id) !=
player_id_to_delegate_id_map_.end());
return MediaWebContentsObserverAndroid::FromWebContents(web_contents_)
->RequestPlay(render_frame_host_,
player_id_to_delegate_id_map_[player_id], has_audio,
IsPlayingRemotely(player_id),
media::DurationToMediaContentType(duration));
}
void BrowserMediaPlayerManager::OnInitialize(
const MediaPlayerHostMsg_Initialize_Params& media_player_params) {
DestroyPlayer(media_player_params.player_id);
bool is_off_the_record =
web_contents()->GetBrowserContext()->IsOffTheRecord();
auto player = CreateMediaPlayer(media_player_params, is_off_the_record);
if (!player)
return;
AddPlayer(std::move(player), media_player_params.delegate_id);
}
void BrowserMediaPlayerManager::OnStart(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (!player)
return;
RequestDecoderResources(player_id, false);
player->Start();
}
void BrowserMediaPlayerManager::OnSeek(
int player_id,
const base::TimeDelta& time) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->SeekTo(time);
}
void BrowserMediaPlayerManager::OnPause(
int player_id,
bool is_media_related_action) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->Pause(is_media_related_action);
}
void BrowserMediaPlayerManager::OnSetVolume(int player_id, double volume) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->SetVolume(volume);
}
void BrowserMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) {
// To be overridden by subclasses.
}
void BrowserMediaPlayerManager::OnSuspendAndReleaseResources(int player_id) {
ReleaseResources(player_id);
}
void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) {
DestroyPlayer(player_id);
}
void BrowserMediaPlayerManager::OnRequestRemotePlayback(int /* player_id */) {
// Does nothing if we don't have a remote player
}
void BrowserMediaPlayerManager::OnRequestRemotePlaybackControl(
int /* player_id */) {
// Does nothing if we don't have a remote player
}
void BrowserMediaPlayerManager::OnRequestRemotePlaybackStop(
int /* player_id */) {
// Does nothing if we don't have a remote player
}
bool BrowserMediaPlayerManager::IsPlayingRemotely(int player_id) {
return false;
}
void BrowserMediaPlayerManager::AddPlayer(
std::unique_ptr<MediaPlayerAndroid> player,
int delegate_id) {
DCHECK(!GetPlayer(player->player_id()));
players_.push_back(std::move(player));
player_id_to_delegate_id_map_[players_.back()->player_id()] = delegate_id;
}
void BrowserMediaPlayerManager::DestroyPlayer(int player_id) {
for (auto it = players_.begin(); it != players_.end(); ++it) {
if ((*it)->player_id() == player_id) {
it->release()->DeleteOnCorrectThread();
players_.erase(it);
break;
}
}
active_players_.erase(player_id);
player_id_to_delegate_id_map_.erase(player_id);
}
void BrowserMediaPlayerManager::ReleaseResources(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
ReleasePlayer(player);
}
std::unique_ptr<MediaPlayerAndroid> BrowserMediaPlayerManager::SwapPlayer(
int player_id,
std::unique_ptr<MediaPlayerAndroid> player) {
std::unique_ptr<MediaPlayerAndroid> previous_player;
for (auto it = players_.begin(); it != players_.end(); ++it) {
if ((*it)->player_id() == player_id) {
previous_player = std::move(*it);
MediaWebContentsObserverAndroid::FromWebContents(web_contents_)
->DisconnectMediaSession(render_frame_host_,
player_id_to_delegate_id_map_[player_id]);
players_.erase(it);
players_.push_back(std::move(player));
break;
}
}
return previous_player;
}
bool BrowserMediaPlayerManager::RequestDecoderResources(
int player_id, bool temporary) {
ActivePlayerMap::iterator it;
// The player is already active, ignore it. A long running player should not
// request temporary permissions.
if ((it = active_players_.find(player_id)) != active_players_.end()) {
DCHECK(!temporary || it->second);
return true;
}
if (!temporary) {
int long_running_player = 0;
for (it = active_players_.begin(); it != active_players_.end(); ++it) {
if (!it->second)
long_running_player++;
}
// Number of active players are less than the threshold, do nothing.
if (long_running_player < kMediaPlayerThreshold)
return true;
for (it = active_players_.begin(); it != active_players_.end(); ++it) {
if (!it->second && !GetPlayer(it->first)->IsPlaying()) {
ReleasePlayer(GetPlayer(it->first));
Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(),
(it->first)));
}
}
}
active_players_[player_id] = temporary;
return true;
}
void BrowserMediaPlayerManager::OnDecoderResourcesReleased(int player_id) {
if (active_players_.find(player_id) == active_players_.end())
return;
active_players_.erase(player_id);
}
int BrowserMediaPlayerManager::RoutingID() {
return render_frame_host_->GetRoutingID();
}
bool BrowserMediaPlayerManager::Send(IPC::Message* msg) {
return render_frame_host_->Send(msg);
}
void BrowserMediaPlayerManager::ReleaseFullscreenPlayer(
MediaPlayerAndroid* player) {
ReleasePlayer(player);
}
void BrowserMediaPlayerManager::ReleasePlayer(MediaPlayerAndroid* player) {
player->Release();
}
} // namespace content