blob: 29c53b1d9abea1efbc7ac7d8e7f83cd98ec67df5 [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 "chrome/browser/media/android/remote/remote_media_player_bridge.h"
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "chrome/browser/media/android/remote/remote_media_player_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "jni/RemoteMediaPlayerBridge_jni.h"
#include "media/base/android/media_common_android.h"
#include "media/base/android/media_resource_getter.h"
#include "media/base/timestamp_constants.h"
#include "net/base/escape.h"
#include "third_party/blink/public/platform/modules/remoteplayback/web_remote_playback_availability.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/android/java_bitmap.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
using base::android::AttachCurrentThread;
using content::BrowserThread;
using media::MediaPlayerAndroid;
namespace remote_media {
RemoteMediaPlayerBridge::RemoteMediaPlayerBridge(
int player_id,
const std::string& user_agent,
RemoteMediaPlayerManager* manager)
: MediaPlayerAndroid(player_id,
manager,
base::DoNothing(),
manager->GetLocalPlayer(player_id)->frame_url()),
width_(0),
height_(0),
url_(manager->GetLocalPlayer(player_id)->GetUrl()),
site_for_cookies_(
manager->GetLocalPlayer(player_id)->GetSiteForCookies()),
user_agent_(user_agent),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
ScopedJavaLocalRef<jstring> j_url_string;
if (url_.is_valid()) {
// Escape the URL to make it safe to use. Don't escape existing escape
// sequences though.
std::string escaped_url = net::EscapeExternalHandlerValue(url_.spec());
// Create a Java String for the URL.
j_url_string = ConvertUTF8ToJavaString(env, escaped_url);
}
ScopedJavaLocalRef<jstring> j_frame_url_string;
GURL frameUrl = GetLocalPlayer()->frame_url();
if (frameUrl.is_valid()) {
// Create a Java String for the URL.
j_frame_url_string = ConvertUTF8ToJavaString(env, frameUrl.spec());
}
java_bridge_.Reset(Java_RemoteMediaPlayerBridge_create(
env, reinterpret_cast<intptr_t>(this), j_url_string, j_frame_url_string,
ConvertUTF8ToJavaString(env, user_agent)));
}
RemoteMediaPlayerBridge::~RemoteMediaPlayerBridge() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_destroy(env, java_bridge_);
Release();
}
bool RemoteMediaPlayerBridge::HasVideo() const {
NOTIMPLEMENTED();
return true;
}
bool RemoteMediaPlayerBridge::HasAudio() const {
NOTIMPLEMENTED();
return true;
}
int RemoteMediaPlayerBridge::GetVideoWidth() {
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return 0;
return local_player->GetVideoWidth();
}
int RemoteMediaPlayerBridge::GetVideoHeight() {
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return 0;
return local_player->GetVideoHeight();
}
void RemoteMediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
width_ = width;
height_ = height;
MediaPlayerAndroid::OnVideoSizeChanged(width, height);
}
void RemoteMediaPlayerBridge::OnPlaybackComplete() {
time_update_timer_.Stop();
MediaPlayerAndroid::OnPlaybackComplete();
}
void RemoteMediaPlayerBridge::OnMediaInterrupted() {}
void RemoteMediaPlayerBridge::StartInternal() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
Java_RemoteMediaPlayerBridge_start(env, java_bridge_);
if (!time_update_timer_.IsRunning()) {
time_update_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(media::kTimeUpdateInterval),
this, &RemoteMediaPlayerBridge::OnTimeUpdateTimerFired);
}
}
void RemoteMediaPlayerBridge::PauseInternal() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
Java_RemoteMediaPlayerBridge_pause(env, java_bridge_);
time_update_timer_.Stop();
}
void RemoteMediaPlayerBridge::OnTimeUpdateTimerFired() {
manager()->OnTimeUpdate(
player_id(), GetCurrentTime(), base::TimeTicks::Now());
}
void RemoteMediaPlayerBridge::PauseLocal(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return;
local_player->Pause(true);
static_cast<RemoteMediaPlayerManager*>(manager())->OnPaused(player_id());
}
jint RemoteMediaPlayerBridge::GetLocalPosition(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return 0;
base::TimeDelta time = local_player->GetCurrentTime();
return static_cast<jint>(time.InMilliseconds());
}
void RemoteMediaPlayerBridge::OnCastStarting(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& casting_message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
static_cast<RemoteMediaPlayerManager*>(manager())->SwitchToRemotePlayer(
player_id(), ConvertJavaStringToUTF8(env, casting_message));
if (!time_update_timer_.IsRunning()) {
time_update_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(media::kTimeUpdateInterval), this,
&RemoteMediaPlayerBridge::OnTimeUpdateTimerFired);
}
}
void RemoteMediaPlayerBridge::OnCastStarted(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
static_cast<RemoteMediaPlayerManager*>(manager())->OnRemotePlaybackStarted(
player_id());
}
void RemoteMediaPlayerBridge::OnCastStopping(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
static_cast<RemoteMediaPlayerManager*>(manager())
->SwitchToLocalPlayer(player_id());
}
void RemoteMediaPlayerBridge::OnSeekCompleted(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
OnSeekComplete();
}
void RemoteMediaPlayerBridge::Pause(bool is_media_related_action) {
// Ignore the pause if it's not from an event that is explicitly telling
// the video to pause. It's possible for Pause() to be called for other
// reasons, such as freeing resources, etc. and during those times, the
// remote video playback should not be paused.
if (is_media_related_action) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
Java_RemoteMediaPlayerBridge_pause(env, java_bridge_);
time_update_timer_.Stop();
}
}
void RemoteMediaPlayerBridge::SetVideoSurface(gl::ScopedJavaSurface surface) {
// The surface is reset whenever the fullscreen view is destroyed or created.
// Since the remote player doesn't use it, we forward it to the local player
// for the time when user disconnects and resumes local playback
// (see crbug.com/420690).
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return;
local_player->SetVideoSurface(std::move(surface));
}
void RemoteMediaPlayerBridge::OnPlaying(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
static_cast<RemoteMediaPlayerManager *>(manager())->OnPlaying(player_id());
}
void RemoteMediaPlayerBridge::OnPaused(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
static_cast<RemoteMediaPlayerManager *>(manager())->OnPaused(player_id());
}
void RemoteMediaPlayerBridge::OnRouteUnselected(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
casting_message_.reset();
static_cast<RemoteMediaPlayerManager *>(manager())->OnRemoteDeviceUnselected(
player_id());
}
void RemoteMediaPlayerBridge::OnPlaybackFinished(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
static_cast<RemoteMediaPlayerManager *>(manager())->OnRemotePlaybackFinished(
player_id());
}
void RemoteMediaPlayerBridge::OnRouteAvailabilityChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
int availability) {
static_cast<RemoteMediaPlayerManager *>(manager())->
OnRouteAvailabilityChanged(
player_id(),
static_cast<blink::WebRemotePlaybackAvailability>(availability));
}
void RemoteMediaPlayerBridge::RequestRemotePlayback() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return;
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_requestRemotePlayback(
env, java_bridge_, local_player->GetCurrentTime().InMilliseconds());
}
void RemoteMediaPlayerBridge::RequestRemotePlaybackControl() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_requestRemotePlaybackControl(env, java_bridge_);
}
void RemoteMediaPlayerBridge::RequestRemotePlaybackStop() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_requestRemotePlaybackStop(env, java_bridge_);
}
void RemoteMediaPlayerBridge::SetNativePlayer() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_setNativePlayer(env, java_bridge_);
}
void RemoteMediaPlayerBridge::OnPlayerCreated() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_onPlayerCreated(env, java_bridge_);
}
void RemoteMediaPlayerBridge::OnPlayerDestroyed() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_onPlayerDestroyed(env, java_bridge_);
}
std::string RemoteMediaPlayerBridge::GetCastingMessage() {
return casting_message_ ?
*casting_message_ : std::string();
}
void RemoteMediaPlayerBridge::SetPosterBitmap(
const std::vector<SkBitmap>& bitmaps) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
if (bitmaps.empty()) {
Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_, nullptr);
} else {
ScopedJavaLocalRef<jobject> j_poster_bitmap;
j_poster_bitmap = gfx::ConvertToJavaBitmap(&(bitmaps[0]));
Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_,
j_poster_bitmap);
}
}
void RemoteMediaPlayerBridge::Start() {
StartInternal();
}
void RemoteMediaPlayerBridge::SeekTo(base::TimeDelta time) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(aberent) Move the checks to the Java side.
base::TimeDelta duration = GetDuration();
if (time > duration)
time = duration;
// Seeking to an invalid position may cause media player to stuck in an
// error state.
if (time < base::TimeDelta()) {
DCHECK_EQ(-1.0, time.InMillisecondsF());
return;
}
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
int time_msec = static_cast<int>(time.InMilliseconds());
Java_RemoteMediaPlayerBridge_seekTo(env, java_bridge_, time_msec);
}
void RemoteMediaPlayerBridge::Release() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
time_update_timer_.Stop();
JNIEnv* env = AttachCurrentThread();
Java_RemoteMediaPlayerBridge_release(env, java_bridge_);
DetachListener();
}
void RemoteMediaPlayerBridge::UpdateEffectiveVolumeInternal(
double effective_volume) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_setVolume(env, java_bridge_,
GetEffectiveVolume());
}
base::TimeDelta RemoteMediaPlayerBridge::GetCurrentTime() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
return base::TimeDelta::FromMilliseconds(
Java_RemoteMediaPlayerBridge_getCurrentPosition(env, java_bridge_));
}
base::TimeDelta RemoteMediaPlayerBridge::GetDuration() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
const int duration_ms =
Java_RemoteMediaPlayerBridge_getDuration(env, java_bridge_);
// Sometimes we can't get the duration remotely, but the local media player
// knows it.
// TODO (aberent) This is for YouTube. Remove it when the YouTube receiver is
// fixed.
if (duration_ms == 0) {
MediaPlayerAndroid* local_player = GetLocalPlayer();
if (!local_player)
return media::kInfiniteDuration;
return local_player->GetDuration();
}
return duration_ms < 0 ? media::kInfiniteDuration
: base::TimeDelta::FromMilliseconds(duration_ms);
}
bool RemoteMediaPlayerBridge::IsPlaying() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
jboolean result = Java_RemoteMediaPlayerBridge_isPlaying(env, java_bridge_);
return result;
}
bool RemoteMediaPlayerBridge::CanPause() {
return true;
}
bool RemoteMediaPlayerBridge::CanSeekForward() {
return true;
}
bool RemoteMediaPlayerBridge::CanSeekBackward() {
return true;
}
bool RemoteMediaPlayerBridge::IsPlayerReady() {
return true;
}
GURL RemoteMediaPlayerBridge::GetUrl() {
return url_;
}
GURL RemoteMediaPlayerBridge::GetSiteForCookies() {
return site_for_cookies_;
}
void RemoteMediaPlayerBridge::Initialize() {
cookies_.clear();
media::MediaResourceGetter* resource_getter =
manager()->GetMediaResourceGetter();
resource_getter->GetCookies(
url_, site_for_cookies_,
base::Bind(&RemoteMediaPlayerBridge::OnCookiesRetrieved,
weak_factory_.GetWeakPtr()));
}
base::android::ScopedJavaLocalRef<jstring> RemoteMediaPlayerBridge::GetTitle(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
base::string16 title;
auto* contents =
static_cast<RemoteMediaPlayerManager*>(manager())->web_contents();
if (contents)
title = contents->GetTitle();
return base::android::ConvertUTF16ToJavaString(env, title);
}
void RemoteMediaPlayerBridge::OnError(
JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
// TODO(https://crbug.com/585379) implement some useful codes for remote
// playback. None of the existing MediaPlayerAndroid codes are
// relevant for remote playback.
manager()->OnError(player_id(), MEDIA_ERROR_INVALID_CODE);
}
void RemoteMediaPlayerBridge::OnCancelledRemotePlaybackRequest(
JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) {
static_cast<RemoteMediaPlayerManager*>(manager())
->OnCancelledRemotePlaybackRequest(player_id());
}
void RemoteMediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(aberent) Do we need to retrieve auth credentials for basic
// authentication? MediaPlayerBridge does.
cookies_ = cookies;
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_RemoteMediaPlayerBridge_setCookies(
env, java_bridge_, ConvertUTF8ToJavaString(env, cookies));
}
MediaPlayerAndroid* RemoteMediaPlayerBridge::GetLocalPlayer() {
return static_cast<RemoteMediaPlayerManager*>(manager())->GetLocalPlayer(
player_id());
}
} // namespace remote_media