blob: 19d5d95e75a7bd9fcd56b2d73e6c1845e29c4375 [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 "base/android/scoped_java_ref.h"
#include "base/command_line.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/media/android/browser_demuxer_android.h"
#include "content/browser/media/android/media_resource_getter_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_view_android.h"
#include "content/common/media/media_player_messages_android.h"
#include "content/public/browser/android/content_view_core.h"
#include "content/public/browser/android/external_video_surface_container.h"
#include "content/public/browser/browser_context.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_source_player.h"
#include "media/base/android/media_url_interceptor.h"
#include "media/base/media_switches.h"
using media::MediaPlayerAndroid;
using media::MediaPlayerBridge;
using media::MediaPlayerManager;
using media::MediaSourcePlayer;
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_factory = NULL;
static media::MediaUrlInterceptor* media_url_interceptor_ = NULL;
// static
void BrowserMediaPlayerManager::RegisterFactory(Factory factory) {
g_factory = factory;
}
// static
void BrowserMediaPlayerManager::RegisterMediaUrlInterceptor(
media::MediaUrlInterceptor* media_url_interceptor) {
media_url_interceptor_ = media_url_interceptor;
}
// static
BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create(
RenderFrameHost* rfh) {
if (g_factory)
return g_factory(rfh);
return new BrowserMediaPlayerManager(rfh);
}
ContentViewCoreImpl* BrowserMediaPlayerManager::GetContentViewCore() const {
return ContentViewCoreImpl::FromWebContents(web_contents());
}
MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer(
const MediaPlayerHostMsg_Initialize_Params& media_player_params,
bool hide_url_log,
MediaPlayerManager* manager,
BrowserDemuxerAndroid* demuxer) {
switch (media_player_params.type) {
case MEDIA_PLAYER_TYPE_URL: {
const std::string user_agent = GetContentClient()->GetUserAgent();
MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge(
media_player_params.player_id,
media_player_params.url,
media_player_params.first_party_for_cookies,
user_agent,
hide_url_log,
manager,
base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased,
weak_ptr_factory_.GetWeakPtr()),
media_player_params.frame_url,
media_player_params.allow_credentials);
BrowserMediaPlayerManager* browser_media_player_manager =
static_cast<BrowserMediaPlayerManager*>(manager);
ContentViewCoreImpl* content_view_core_impl =
static_cast<ContentViewCoreImpl*>(ContentViewCore::FromWebContents(
browser_media_player_manager->web_contents_));
if (!content_view_core_impl) {
// May reach here due to prerendering. Don't extract the metadata
// since it is expensive.
// TODO(qinmin): extract the metadata once the user decided to load
// the page.
browser_media_player_manager->OnMediaMetadataChanged(
media_player_params.player_id, base::TimeDelta(), 0, 0, false);
} else if (!content_view_core_impl->ShouldBlockMediaRequest(
media_player_params.url)) {
media_player_bridge->Initialize();
}
return media_player_bridge;
}
case MEDIA_PLAYER_TYPE_MEDIA_SOURCE: {
return new MediaSourcePlayer(
media_player_params.player_id,
manager,
base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesRequested,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased,
weak_ptr_factory_.GetWeakPtr()),
demuxer->CreateDemuxer(media_player_params.demuxer_client_id),
media_player_params.frame_url);
}
}
NOTREACHED();
return NULL;
}
BrowserMediaPlayerManager::BrowserMediaPlayerManager(
RenderFrameHost* render_frame_host)
: render_frame_host_(render_frame_host),
fullscreen_player_id_(-1),
fullscreen_player_is_released_(false),
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 ScopedVector.
}
void BrowserMediaPlayerManager::FullscreenPlayerPlay() {
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (player) {
if (fullscreen_player_is_released_) {
video_view_->OpenVideo();
fullscreen_player_is_released_ = false;
}
player->Start();
Send(new MediaPlayerMsg_DidMediaPlayerPlay(RoutingID(),
fullscreen_player_id_));
}
}
void BrowserMediaPlayerManager::FullscreenPlayerPause() {
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (player) {
player->Pause(true);
Send(new MediaPlayerMsg_DidMediaPlayerPause(RoutingID(),
fullscreen_player_id_));
}
}
void BrowserMediaPlayerManager::FullscreenPlayerSeek(int msec) {
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (player) {
// TODO(kbalazs): if |fullscreen_player_is_released_| is true
// at this point, player->GetCurrentTime() will be wrong until
// FullscreenPlayerPlay (http://crbug.com/322798).
OnSeekRequest(fullscreen_player_id_,
base::TimeDelta::FromMilliseconds(msec));
}
}
void BrowserMediaPlayerManager::ExitFullscreen(bool release_media_player) {
if (WebContentsDelegate* delegate = web_contents_->GetDelegate())
delegate->ToggleFullscreenModeForTab(web_contents_, false);
if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableOverlayFullscreenVideoSubtitle)) {
if (RenderWidgetHostViewAndroid* view_android =
static_cast<RenderWidgetHostViewAndroid*>(
web_contents_->GetRenderWidgetHostView())) {
view_android->SetOverlayVideoMode(false);
}
}
Send(
new MediaPlayerMsg_DidExitFullscreen(RoutingID(), fullscreen_player_id_));
video_view_.reset();
MediaPlayerAndroid* player = GetFullscreenPlayer();
fullscreen_player_id_ = -1;
if (!player)
return;
if (release_media_player)
ReleaseFullscreenPlayer(player);
else
player->SetVideoSurface(gfx::ScopedJavaSurface());
}
void BrowserMediaPlayerManager::OnTimeUpdate(int player_id,
base::TimeDelta current_time) {
Send(
new MediaPlayerMsg_MediaTimeUpdate(RoutingID(), player_id, current_time));
}
void BrowserMediaPlayerManager::SetVideoSurface(
gfx::ScopedJavaSurface surface) {
MediaPlayerAndroid* player = GetFullscreenPlayer();
if (!player)
return;
bool empty_surface = surface.IsEmpty();
player->SetVideoSurface(surface.Pass());
if (empty_surface)
return;
Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player->player_id()));
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableOverlayFullscreenVideoSubtitle)) {
return;
}
if (RenderWidgetHostViewAndroid* view_android =
static_cast<RenderWidgetHostViewAndroid*>(
web_contents_->GetRenderWidgetHostView())) {
view_android->SetOverlayVideoMode(true);
}
}
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));
if (fullscreen_player_id_ == player_id)
video_view_->UpdateMediaMetadata();
}
void BrowserMediaPlayerManager::OnPlaybackComplete(int player_id) {
Send(new MediaPlayerMsg_MediaPlaybackCompleted(RoutingID(), player_id));
if (fullscreen_player_id_ == player_id)
video_view_->OnPlaybackComplete();
}
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));
OnReleaseResources(player_id);
}
void BrowserMediaPlayerManager::OnBufferingUpdate(
int player_id, int percentage) {
Send(new MediaPlayerMsg_MediaBufferingUpdate(
RoutingID(), player_id, percentage));
if (fullscreen_player_id_ == player_id)
video_view_->OnBufferingUpdate(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::PauseVideo() {
Send(new MediaPlayerMsg_PauseVideo(RoutingID()));
}
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));
if (fullscreen_player_id_ == player_id)
video_view_->OnMediaPlayerError(error);
}
void BrowserMediaPlayerManager::OnVideoSizeChanged(
int player_id, int width, int height) {
Send(new MediaPlayerMsg_MediaVideoSizeChanged(RoutingID(), player_id,
width, height));
if (fullscreen_player_id_ == player_id)
video_view_->OnVideoSizeChanged(width, height);
}
media::MediaResourceGetter*
BrowserMediaPlayerManager::GetMediaResourceGetter() {
if (!media_resource_getter_.get()) {
RenderProcessHost* host = web_contents()->GetRenderProcessHost();
BrowserContext* context = host->GetBrowserContext();
StoragePartition* partition = host->GetStoragePartition();
fileapi::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::GetFullscreenPlayer() {
return GetPlayer(fullscreen_player_id_);
}
MediaPlayerAndroid* BrowserMediaPlayerManager::GetPlayer(int player_id) {
for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin();
it != players_.end(); ++it) {
if ((*it)->player_id() == player_id)
return *it;
}
return NULL;
}
void BrowserMediaPlayerManager::RequestFullScreen(int player_id) {
if (fullscreen_player_id_ == player_id)
return;
if (fullscreen_player_id_ != -1) {
// TODO(qinmin): Determine the correct error code we should report to WMPA.
OnError(player_id, MediaPlayerAndroid::MEDIA_ERROR_DECODE);
return;
}
Send(new MediaPlayerMsg_RequestFullscreen(RoutingID(), player_id));
}
#if defined(VIDEO_HOLE)
bool
BrowserMediaPlayerManager::ShouldUseVideoOverlayForEmbeddedEncryptedVideo() {
RendererPreferences* prefs = web_contents_->GetMutableRendererPrefs();
return prefs->use_video_overlay_for_embedded_encrypted_video;
}
void BrowserMediaPlayerManager::AttachExternalVideoSurface(int player_id,
jobject surface) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player) {
player->SetVideoSurface(
gfx::ScopedJavaSurface::AcquireExternalSurface(surface));
}
}
void BrowserMediaPlayerManager::DetachExternalVideoSurface(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->SetVideoSurface(gfx::ScopedJavaSurface());
}
void BrowserMediaPlayerManager::OnFrameInfoUpdated() {
if (external_video_surface_container_)
external_video_surface_container_->OnFrameInfoUpdated();
}
void BrowserMediaPlayerManager::OnNotifyExternalSurface(
int player_id, bool is_request, const gfx::RectF& rect) {
if (!web_contents_)
return;
if (is_request) {
OnRequestExternalSurface(player_id, rect);
}
if (external_video_surface_container_) {
external_video_surface_container_->OnExternalVideoSurfacePositionChanged(
player_id, rect);
}
}
void BrowserMediaPlayerManager::OnRequestExternalSurface(
int player_id, const gfx::RectF& rect) {
if (!external_video_surface_container_) {
ContentBrowserClient* client = GetContentClient()->browser();
external_video_surface_container_.reset(
client->OverrideCreateExternalVideoSurfaceContainer(web_contents_));
}
// It's safe to use base::Unretained(this), because the callbacks will not
// be called after running ReleaseExternalVideoSurface().
if (external_video_surface_container_) {
external_video_surface_container_->RequestExternalVideoSurface(
player_id,
base::Bind(&BrowserMediaPlayerManager::AttachExternalVideoSurface,
base::Unretained(this)),
base::Bind(&BrowserMediaPlayerManager::DetachExternalVideoSurface,
base::Unretained(this)));
}
}
#endif // defined(VIDEO_HOLE)
void BrowserMediaPlayerManager::OnEnterFullscreen(int player_id) {
DCHECK_EQ(fullscreen_player_id_, -1);
#if defined(VIDEO_HOLE)
if (external_video_surface_container_)
external_video_surface_container_->ReleaseExternalVideoSurface(player_id);
#endif // defined(VIDEO_HOLE)
if (video_view_.get()) {
fullscreen_player_id_ = player_id;
video_view_->OpenVideo();
return;
} else if (!ContentVideoView::GetInstance()) {
if (!GetPlayer(player_id)) {
// If a player doesn't exist, it must be waiting for CORS check.
// As a result, just request the tab to enter fullscreen mode without
// creating the surface view. This is only needed for M37.
Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id));
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableOverlayFullscreenVideoSubtitle)) {
return;
}
if (RenderWidgetHostViewAndroid* view_android =
static_cast<RenderWidgetHostViewAndroid*>(
web_contents_->GetRenderWidgetHostView())) {
view_android->SetOverlayVideoMode(true);
}
if (WebContentsDelegate* delegate = web_contents_->GetDelegate())
delegate->ToggleFullscreenModeForTab(web_contents_, true);
}
// In Android WebView, two ContentViewCores could both try to enter
// fullscreen video, we just ignore the second one.
video_view_.reset(new ContentVideoView(this));
base::android::ScopedJavaLocalRef<jobject> j_content_video_view =
video_view_->GetJavaObject(base::android::AttachCurrentThread());
if (!j_content_video_view.is_null()) {
fullscreen_player_id_ = player_id;
return;
}
}
// Force the second video to exit fullscreen.
// TODO(qinmin): There is no need to send DidEnterFullscreen message.
// However, if we don't send the message, page layers will not be
// correctly restored. http:crbug.com/367346.
Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id));
Send(new MediaPlayerMsg_DidExitFullscreen(RoutingID(), player_id));
video_view_.reset();
}
void BrowserMediaPlayerManager::OnExitFullscreen(int player_id) {
if (fullscreen_player_id_ == player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->SetVideoSurface(gfx::ScopedJavaSurface());
video_view_->OnExitFullscreen();
}
}
void BrowserMediaPlayerManager::OnInitialize(
const MediaPlayerHostMsg_Initialize_Params& media_player_params) {
DCHECK(media_player_params.type != MEDIA_PLAYER_TYPE_MEDIA_SOURCE ||
media_player_params.demuxer_client_id > 0)
<< "Media source players must have positive demuxer client IDs: "
<< media_player_params.demuxer_client_id;
RemovePlayer(media_player_params.player_id);
RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>(
web_contents()->GetRenderProcessHost());
MediaPlayerAndroid* player = CreateMediaPlayer(
media_player_params,
host->GetBrowserContext()->IsOffTheRecord(), this,
host->browser_demuxer_android());
if (!player)
return;
AddPlayer(player);
}
void BrowserMediaPlayerManager::OnStart(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (!player)
return;
player->Start();
if (fullscreen_player_id_ == player_id && fullscreen_player_is_released_) {
video_view_->OpenVideo();
fullscreen_player_is_released_ = false;
}
}
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::OnReleaseResources(int player_id) {
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player)
player->Release();
if (player_id == fullscreen_player_id_)
fullscreen_player_is_released_ = true;
}
void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) {
RemovePlayer(player_id);
if (fullscreen_player_id_ == player_id)
fullscreen_player_id_ = -1;
}
void BrowserMediaPlayerManager::AddPlayer(MediaPlayerAndroid* player) {
DCHECK(!GetPlayer(player->player_id()));
players_.push_back(player);
}
void BrowserMediaPlayerManager::RemovePlayer(int player_id) {
for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin();
it != players_.end(); ++it) {
MediaPlayerAndroid* player = *it;
if (player->player_id() == player_id) {
players_.erase(it);
break;
}
}
}
scoped_ptr<media::MediaPlayerAndroid> BrowserMediaPlayerManager::SwapPlayer(
int player_id, media::MediaPlayerAndroid* player) {
media::MediaPlayerAndroid* previous_player = NULL;
for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin();
it != players_.end(); ++it) {
if ((*it)->player_id() == player_id) {
previous_player = *it;
players_.weak_erase(it);
players_.push_back(player);
break;
}
}
return scoped_ptr<media::MediaPlayerAndroid>(previous_player);
}
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) {
player->Release();
}
void BrowserMediaPlayerManager::OnMediaResourcesRequested(int player_id) {
int num_active_player = 0;
ScopedVector<MediaPlayerAndroid>::iterator it;
for (it = players_.begin(); it != players_.end(); ++it) {
if (!(*it)->IsPlayerReady())
continue;
// The player is already active, ignore it.
if ((*it)->player_id() == player_id)
return;
else
num_active_player++;
}
// Number of active players are less than the threshold, do nothing.
if (num_active_player < kMediaPlayerThreshold)
return;
for (it = players_.begin(); it != players_.end(); ++it) {
if ((*it)->IsPlayerReady() && !(*it)->IsPlaying() &&
fullscreen_player_id_ != (*it)->player_id()) {
(*it)->Release();
Send(new MediaPlayerMsg_MediaPlayerReleased(RoutingID(),
(*it)->player_id()));
}
}
}
void BrowserMediaPlayerManager::OnMediaResourcesReleased(int player_id) {
#if defined(VIDEO_HOLE)
MediaPlayerAndroid* player = GetPlayer(player_id);
if (player && player->IsSurfaceInUse())
return;
if (external_video_surface_container_)
external_video_surface_container_->ReleaseExternalVideoSurface(player_id);
#endif // defined(VIDEO_HOLE)
}
} // namespace content