blob: 342ceaa79024e6d82c9685c4c79caed04d70e270 [file] [log] [blame]
// Copyright (c) 2012 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 "media/base/android/media_player_bridge.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "jni/MediaPlayerBridge_jni.h"
#include "media/base/android/media_player_manager.h"
#include "media/base/android/media_resource_getter.h"
#include "media/base/android/media_source_player.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
// Time update happens every 250ms.
static const int kTimeUpdateInterval = 250;
// Android MediaMetadataRetriever may fail to extract the metadata from the
// media under some circumstances. This makes the user unable to perform
// seek. To solve this problem, we use a temporary duration of 100 seconds when
// the duration is unknown. And we scale the seek position later when duration
// is available.
static const int kTemporaryDuration = 100;
namespace media {
#if !defined(GOOGLE_TV)
// static
MediaPlayerAndroid* MediaPlayerAndroid::Create(
int player_id,
const GURL& url,
SourceType source_type,
const GURL& first_party_for_cookies,
bool hide_url_log,
MediaPlayerManager* manager) {
if (source_type == SOURCE_TYPE_URL) {
MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge(
player_id,
url,
first_party_for_cookies,
hide_url_log,
manager);
media_player_bridge->Initialize();
return media_player_bridge;
} else {
return new MediaSourcePlayer(
player_id,
manager);
}
}
#endif
MediaPlayerBridge::MediaPlayerBridge(
int player_id,
const GURL& url,
const GURL& first_party_for_cookies,
bool hide_url_log,
MediaPlayerManager* manager)
: MediaPlayerAndroid(player_id,
manager),
prepared_(false),
pending_play_(false),
url_(url),
first_party_for_cookies_(first_party_for_cookies),
hide_url_log_(hide_url_log),
duration_(base::TimeDelta::FromSeconds(kTemporaryDuration)),
width_(0),
height_(0),
can_pause_(true),
can_seek_forward_(true),
can_seek_backward_(true),
weak_this_(this),
listener_(base::MessageLoopProxy::current(),
weak_this_.GetWeakPtr()) {
}
MediaPlayerBridge::~MediaPlayerBridge() {
Release();
}
void MediaPlayerBridge::Initialize() {
if (url_.SchemeIsFile()) {
cookies_.clear();
ExtractMediaMetadata(url_.spec());
return;
}
media::MediaResourceGetter* resource_getter =
manager()->GetMediaResourceGetter();
if (url_.SchemeIsFileSystem()) {
cookies_.clear();
resource_getter->GetPlatformPathFromFileSystemURL(url_, base::Bind(
&MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr()));
return;
}
resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind(
&MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr()));
}
void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(env));
SetMediaPlayerListener();
}
void MediaPlayerBridge::SetJavaMediaPlayerBridge(
jobject j_media_player_bridge) {
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
j_media_player_bridge_.Reset(env, j_media_player_bridge);
}
void MediaPlayerBridge::SetMediaPlayerListener() {
jobject j_context = base::android::GetApplicationContext();
DCHECK(j_context);
listener_.CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
}
void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
duration_ = duration;
}
void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
if (j_media_player_bridge_.is_null()) {
if (surface.IsEmpty())
return;
Prepare();
}
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
Java_MediaPlayerBridge_setSurface(
env, j_media_player_bridge_.obj(), surface.j_surface().obj());
}
void MediaPlayerBridge::Prepare() {
if (j_media_player_bridge_.is_null())
CreateJavaMediaPlayerBridge();
if (url_.SchemeIsFileSystem()) {
manager()->GetMediaResourceGetter()->GetPlatformPathFromFileSystemURL(
url_, base::Bind(&MediaPlayerBridge::SetDataSource,
weak_this_.GetWeakPtr()));
} else {
SetDataSource(url_.spec());
}
}
void MediaPlayerBridge::SetDataSource(const std::string& url) {
if (j_media_player_bridge_.is_null())
return;
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
// Create a Java String for the URL.
ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
env, cookies_);
jobject j_context = base::android::GetApplicationContext();
DCHECK(j_context);
if (Java_MediaPlayerBridge_setDataSource(
env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
j_cookies.obj(), hide_url_log_)) {
RequestMediaResourcesFromManager();
Java_MediaPlayerBridge_prepareAsync(
env, j_media_player_bridge_.obj());
} else {
OnMediaError(MEDIA_ERROR_FORMAT);
}
}
void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
cookies_ = cookies;
ExtractMediaMetadata(url_.spec());
}
void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
url, cookies_, base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
weak_this_.GetWeakPtr()));
}
void MediaPlayerBridge::OnMediaMetadataExtracted(
base::TimeDelta duration, int width, int height, bool success) {
if (success) {
duration_ = duration;
width_ = width;
height_ = height;
}
OnMediaMetadataChanged(duration_, width_, height_, success);
}
void MediaPlayerBridge::Start() {
if (j_media_player_bridge_.is_null()) {
pending_play_ = true;
Prepare();
} else {
if (prepared_)
StartInternal();
else
pending_play_ = true;
}
}
void MediaPlayerBridge::Pause() {
if (j_media_player_bridge_.is_null()) {
pending_play_ = false;
} else {
if (prepared_ && IsPlaying())
PauseInternal();
else
pending_play_ = false;
}
}
bool MediaPlayerBridge::IsPlaying() {
if (!prepared_)
return pending_play_;
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
jboolean result = Java_MediaPlayerBridge_isPlaying(
env, j_media_player_bridge_.obj());
return result;
}
int MediaPlayerBridge::GetVideoWidth() {
if (!prepared_)
return width_;
JNIEnv* env = base::android::AttachCurrentThread();
return Java_MediaPlayerBridge_getVideoWidth(
env, j_media_player_bridge_.obj());
}
int MediaPlayerBridge::GetVideoHeight() {
if (!prepared_)
return height_;
JNIEnv* env = base::android::AttachCurrentThread();
return Java_MediaPlayerBridge_getVideoHeight(
env, j_media_player_bridge_.obj());
}
void MediaPlayerBridge::SeekTo(base::TimeDelta time) {
// Record the time to seek when OnMediaPrepared() is called.
pending_seek_ = time;
if (j_media_player_bridge_.is_null())
Prepare();
else if (prepared_)
SeekInternal(time);
}
base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
if (!prepared_)
return pending_seek_;
JNIEnv* env = base::android::AttachCurrentThread();
return base::TimeDelta::FromMilliseconds(
Java_MediaPlayerBridge_getCurrentPosition(
env, j_media_player_bridge_.obj()));
}
base::TimeDelta MediaPlayerBridge::GetDuration() {
if (!prepared_)
return duration_;
JNIEnv* env = base::android::AttachCurrentThread();
return base::TimeDelta::FromMilliseconds(
Java_MediaPlayerBridge_getDuration(
env, j_media_player_bridge_.obj()));
}
void MediaPlayerBridge::Release() {
if (j_media_player_bridge_.is_null())
return;
time_update_timer_.Stop();
if (prepared_)
pending_seek_ = GetCurrentTime();
prepared_ = false;
pending_play_ = false;
SetVideoSurface(gfx::ScopedJavaSurface());
JNIEnv* env = base::android::AttachCurrentThread();
Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
j_media_player_bridge_.Reset();
ReleaseMediaResourcesFromManager();
listener_.ReleaseMediaPlayerListenerResources();
}
void MediaPlayerBridge::SetVolume(double volume) {
if (j_media_player_bridge_.is_null())
return;
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
Java_MediaPlayerBridge_setVolume(
env, j_media_player_bridge_.obj(), volume);
}
void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
width_ = width;
height_ = height;
MediaPlayerAndroid::OnVideoSizeChanged(width, height);
}
void MediaPlayerBridge::OnPlaybackComplete() {
time_update_timer_.Stop();
MediaPlayerAndroid::OnPlaybackComplete();
}
void MediaPlayerBridge::OnMediaInterrupted() {
time_update_timer_.Stop();
MediaPlayerAndroid::OnMediaInterrupted();
}
void MediaPlayerBridge::OnMediaPrepared() {
if (j_media_player_bridge_.is_null())
return;
prepared_ = true;
base::TimeDelta dur = duration_;
duration_ = GetDuration();
if (duration_ != dur && 0 != dur.InMilliseconds()) {
// Scale the |pending_seek_| according to the new duration.
pending_seek_ = base::TimeDelta::FromSeconds(
pending_seek_.InSecondsF() * duration_.InSecondsF() / dur.InSecondsF());
}
// If media player was recovered from a saved state, consume all the pending
// events.
PendingSeekInternal(pending_seek_);
if (pending_play_) {
StartInternal();
pending_play_ = false;
}
GetAllowedOperations();
OnMediaMetadataChanged(duration_, width_, height_, true);
}
void MediaPlayerBridge::GetAllowedOperations() {
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
ScopedJavaLocalRef<jobject> allowedOperations =
Java_MediaPlayerBridge_getAllowedOperations(
env, j_media_player_bridge_.obj());
can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
can_seek_forward_ = Java_AllowedOperations_canSeekForward(
env, allowedOperations.obj());
can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
env, allowedOperations.obj());
}
void MediaPlayerBridge::StartInternal() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
if (!time_update_timer_.IsRunning()) {
time_update_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
this, &MediaPlayerBridge::OnTimeUpdated);
}
}
void MediaPlayerBridge::PauseInternal() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
time_update_timer_.Stop();
}
void MediaPlayerBridge::PendingSeekInternal(base::TimeDelta time) {
SeekInternal(time);
}
void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
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 = base::android::AttachCurrentThread();
CHECK(env);
int time_msec = static_cast<int>(time.InMilliseconds());
Java_MediaPlayerBridge_seekTo(
env, j_media_player_bridge_.obj(), time_msec);
}
bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
bool ret = RegisterNativesImpl(env);
DCHECK(g_MediaPlayerBridge_clazz);
return ret;
}
bool MediaPlayerBridge::CanPause() {
return can_pause_;
}
bool MediaPlayerBridge::CanSeekForward() {
return can_seek_forward_;
}
bool MediaPlayerBridge::CanSeekBackward() {
return can_seek_backward_;
}
bool MediaPlayerBridge::IsPlayerReady() {
return prepared_;
}
GURL MediaPlayerBridge::GetUrl() {
return url_;
}
GURL MediaPlayerBridge::GetFirstPartyForCookies() {
return first_party_for_cookies_;
}
} // namespace media