summaryrefslogtreecommitdiffstats
path: root/chrome/browser/media/android/remote/remote_media_player_bridge.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/media/android/remote/remote_media_player_bridge.cc')
-rw-r--r--chrome/browser/media/android/remote/remote_media_player_bridge.cc512
1 files changed, 512 insertions, 0 deletions
diff --git a/chrome/browser/media/android/remote/remote_media_player_bridge.cc b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
new file mode 100644
index 0000000..dfad5fb
--- /dev/null
+++ b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
@@ -0,0 +1,512 @@
+// 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 "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "chrome/browser/media/android/remote/record_cast_action.h"
+#include "chrome/browser/media/android/remote/remote_media_player_manager.h"
+#include "content/browser/android/content_view_core_impl.h"
+#include "jni/RemoteMediaPlayerBridge_jni.h"
+#include "media/base/android/media_common_android.h"
+#include "media/base/android/media_resource_getter.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/android/java_bitmap.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+using base::android::AttachCurrentThread;
+
+namespace {
+/*
+ * Dummy function for RequestMediaResources callback. The callback is never
+ * actually called by MediaPlayerAndroid or RemoteMediaPlayer but is needed
+ * to compile the constructor call.
+ */
+void DoNothing(int /*i*/) {}
+}
+
+namespace remote_media {
+
+RemoteMediaPlayerBridge::RemoteMediaPlayerBridge(
+ MediaPlayerAndroid* local_player, const std::string& user_agent,
+ bool hide_url_log, RemoteMediaPlayerManager* manager)
+ : MediaPlayerAndroid(local_player->player_id(), manager,
+ base::Bind(&DoNothing),
+ local_player->frame_url()),
+ start_position_millis_(0),
+ local_player_(local_player),
+ in_use_(false),
+ prepared_(false),
+ pending_play_(false),
+ width_(0),
+ height_(0),
+ should_seek_on_prepare_(false),
+ hide_url_log_(hide_url_log),
+ volume_(-1.0),
+ url_(local_player->GetUrl()),
+ first_party_for_cookies_(local_player->GetFirstPartyForCookies()),
+ user_agent_(user_agent),
+ weak_factory_(this) {
+ if (local_player->GetCurrentTime().InMilliseconds() > 0)
+ start_position_millis_ = local_player->GetCurrentTime().InMilliseconds();
+ JNIEnv* env = base::android::AttachCurrentThread();
+ CHECK(env);
+ ScopedJavaLocalRef<jstring> j_url_string;
+ if (url_.is_valid()) {
+ // Create a Java String for the URL.
+ j_url_string = ConvertUTF8ToJavaString(env, url_.spec());
+ }
+ ScopedJavaLocalRef<jstring> j_frame_url_string;
+ if (local_player->frame_url().is_valid()) {
+ // Create a Java String for the URL.
+ j_frame_url_string = ConvertUTF8ToJavaString(
+ env, local_player->frame_url().spec());
+ }
+ java_bridge_.Reset(
+ Java_RemoteMediaPlayerBridge_create(env, reinterpret_cast<intptr_t>(this),
+ start_position_millis_,
+ j_url_string.obj(),
+ j_frame_url_string.obj()));
+}
+
+RemoteMediaPlayerBridge::~RemoteMediaPlayerBridge() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ CHECK(env);
+ Java_RemoteMediaPlayerBridge_destroy(env, java_bridge_.obj());
+ Release();
+}
+
+int RemoteMediaPlayerBridge::GetVideoWidth() {
+ return local_player_->GetVideoWidth();
+}
+
+int RemoteMediaPlayerBridge::GetVideoHeight() {
+ 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::OnMediaPrepared() {
+ if (!in_use_)
+ return;
+
+ prepared_ = true;
+ duration_ = GetDuration();
+
+ // If media player was recovered from a saved state, consume all the pending
+ // events.
+ if (should_seek_on_prepare_) {
+ PendingSeekInternal(pending_seek_);
+ pending_seek_ = base::TimeDelta::FromMilliseconds(0);
+ should_seek_on_prepare_ = false;
+ }
+
+ if (pending_play_) {
+ StartInternal();
+ pending_play_ = false;
+ }
+
+ manager()->OnMediaMetadataChanged(
+ player_id(), duration_, width_, height_, true);
+}
+
+void RemoteMediaPlayerBridge::StartInternal() {
+ JNIEnv* env = AttachCurrentThread();
+ Java_RemoteMediaPlayerBridge_start(env, java_bridge_.obj());
+ if (!time_update_timer_.IsRunning()) {
+ time_update_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(media::kTimeUpdateInterval),
+ this, &RemoteMediaPlayerBridge::OnTimeUpdateTimerFired);
+ }
+}
+
+void RemoteMediaPlayerBridge::PauseInternal() {
+ JNIEnv* env = AttachCurrentThread();
+ Java_RemoteMediaPlayerBridge_pause(env, java_bridge_.obj());
+ time_update_timer_.Stop();
+}
+
+void RemoteMediaPlayerBridge::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 = AttachCurrentThread();
+ CHECK(env);
+ int time_msec = static_cast<int>(time.InMilliseconds());
+ Java_RemoteMediaPlayerBridge_seekTo(
+ env, java_bridge_.obj(), time_msec);
+}
+
+void RemoteMediaPlayerBridge::OnTimeUpdateTimerFired() {
+ manager()->OnTimeUpdate(
+ player_id(), GetCurrentTime(), base::TimeTicks::Now());
+}
+
+void RemoteMediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
+ SeekInternal(time);
+}
+
+void RemoteMediaPlayerBridge::Prepare() {
+ DCHECK(!in_use_);
+ DCHECK(IsMediaPlayableRemotely());
+ in_use_ = true;
+ AttachListener(java_bridge_.obj());
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ if (url_.is_valid()) {
+ // Create a Java String for the URL.
+ ScopedJavaLocalRef<jstring> j_url_string =
+ ConvertUTF8ToJavaString(env, url_.spec());
+
+ jobject j_context = base::android::GetApplicationContext();
+ DCHECK(j_context);
+
+ ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
+ env, cookies_);
+ ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
+ env, user_agent_);
+
+ if (!Java_RemoteMediaPlayerBridge_setDataSource(
+ env, java_bridge_.obj(), j_context, j_url_string.obj(),
+ j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
+ OnMediaError(MEDIA_ERROR_FORMAT);
+ return;
+ }
+ }
+
+ if (!Java_RemoteMediaPlayerBridge_prepareAsync(env, java_bridge_.obj()))
+ OnMediaError(MEDIA_ERROR_FORMAT);
+}
+
+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) {
+ if (!in_use_) {
+ pending_play_ = false;
+ } else {
+ if (prepared_ && IsPlaying())
+ PauseInternal();
+ else
+ pending_play_ = false;
+ }
+ }
+}
+
+void RemoteMediaPlayerBridge::SetVideoSurface(gfx::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).
+ local_player_->SetVideoSurface(surface.Pass());
+}
+
+base::android::ScopedJavaLocalRef<jstring> RemoteMediaPlayerBridge::GetFrameUrl(
+ JNIEnv* env, jobject obj) {
+ return ConvertUTF8ToJavaString(env, frame_url().spec());
+}
+
+void RemoteMediaPlayerBridge::OnPlaying(JNIEnv* env, jobject obj) {
+ static_cast<RemoteMediaPlayerManager *>(manager())->OnPlaying(player_id());
+}
+
+void RemoteMediaPlayerBridge::OnPaused(JNIEnv* env, jobject obj) {
+ static_cast<RemoteMediaPlayerManager *>(manager())->OnPaused(player_id());
+}
+
+void RemoteMediaPlayerBridge::OnRouteSelected(JNIEnv* env, jobject obj,
+ jstring castingMessage) {
+ casting_message_.reset(
+ new std::string(
+ base::android::ConvertJavaStringToUTF8(env, castingMessage)));
+ static_cast<RemoteMediaPlayerManager *>(manager())->OnRemoteDeviceSelected(
+ player_id());
+}
+
+void RemoteMediaPlayerBridge::OnRouteUnselected(JNIEnv* env, jobject obj) {
+ casting_message_.reset();
+ static_cast<RemoteMediaPlayerManager *>(manager())->OnRemoteDeviceUnselected(
+ player_id());
+}
+
+void RemoteMediaPlayerBridge::OnPlaybackFinished(JNIEnv* env, jobject obj) {
+ static_cast<RemoteMediaPlayerManager *>(manager())->OnRemotePlaybackFinished(
+ player_id());
+}
+
+void RemoteMediaPlayerBridge::OnRouteAvailabilityChanged(JNIEnv* env,
+ jobject obj,
+ jboolean available) {
+ static_cast<RemoteMediaPlayerManager *>(manager())->
+ OnRouteAvailabilityChanged(player_id(), available);
+}
+
+// static
+bool RemoteMediaPlayerBridge::RegisterRemoteMediaPlayerBridge(JNIEnv* env) {
+ bool ret = RegisterNativesImpl(env);
+ DCHECK(g_RemoteMediaPlayerBridge_clazz);
+ return ret;
+}
+
+void RemoteMediaPlayerBridge::RequestRemotePlayback() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_RemoteMediaPlayerBridge_requestRemotePlayback(
+ env, java_bridge_.obj());
+}
+
+void RemoteMediaPlayerBridge::RequestRemotePlaybackControl() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_RemoteMediaPlayerBridge_requestRemotePlaybackControl(
+ env, java_bridge_.obj());
+}
+
+void RemoteMediaPlayerBridge::SetNativePlayer() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_RemoteMediaPlayerBridge_setNativePlayer(
+ env, java_bridge_.obj());
+}
+
+void RemoteMediaPlayerBridge::OnPlayerCreated() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_RemoteMediaPlayerBridge_onPlayerCreated(
+ env, java_bridge_.obj());
+}
+
+void RemoteMediaPlayerBridge::OnPlayerDestroyed() {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ Java_RemoteMediaPlayerBridge_onPlayerDestroyed(
+ env, java_bridge_.obj());
+}
+
+bool RemoteMediaPlayerBridge::IsRemotePlaybackAvailable() const {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ jboolean result = Java_RemoteMediaPlayerBridge_isRemotePlaybackAvailable(
+ env, java_bridge_.obj());
+
+ return result;
+}
+
+bool RemoteMediaPlayerBridge::IsRemotePlaybackPreferredForFrame() const {
+ if (in_use_) {
+ // We have already decided to use remote playback
+ return true;
+ }
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ jboolean result =
+ Java_RemoteMediaPlayerBridge_isRemotePlaybackPreferredForFrame(
+ env, java_bridge_.obj());
+ return result;
+}
+
+std::string RemoteMediaPlayerBridge::GetCastingMessage() {
+ return casting_message_ ?
+ *casting_message_ : std::string();
+}
+
+void RemoteMediaPlayerBridge::SetPosterBitmap(
+ const std::vector<SkBitmap>& bitmaps) {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ if (bitmaps.empty()) {
+ Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_.obj(), NULL);
+ } else {
+ ScopedJavaLocalRef<jobject> j_poster_bitmap;
+ j_poster_bitmap = gfx::ConvertToJavaBitmap(&(bitmaps[0]));
+
+ Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_.obj(),
+ j_poster_bitmap.obj());
+ }
+}
+
+void RemoteMediaPlayerBridge::Start() {
+ if (!in_use_) {
+ pending_play_ = true;
+ Prepare();
+ } else {
+ if (prepared_)
+ StartInternal();
+ else
+ pending_play_ = true;
+ }
+}
+
+void RemoteMediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
+ // Record the time to seek when OnMediaPrepared() is called.
+ pending_seek_ = timestamp;
+ should_seek_on_prepare_ = true;
+
+ if (!in_use_)
+ Prepare();
+ else if (prepared_)
+ SeekInternal(timestamp);
+}
+
+void RemoteMediaPlayerBridge::Release() {
+ if (!in_use_)
+ return;
+ time_update_timer_.Stop();
+ if (prepared_) {
+ pending_seek_ = GetCurrentTime();
+ should_seek_on_prepare_ = true;
+ }
+
+ prepared_ = false;
+ pending_play_ = false;
+ JNIEnv* env = AttachCurrentThread();
+ Java_RemoteMediaPlayerBridge_release(env, java_bridge_.obj());
+ DetachListener();
+ in_use_ = false;
+}
+
+void RemoteMediaPlayerBridge::SetVolume(double volume) {
+ if (!in_use_) {
+ volume_ = volume;
+ return;
+ }
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+ Java_RemoteMediaPlayerBridge_setVolume(
+ env, java_bridge_.obj(), volume);
+}
+
+base::TimeDelta RemoteMediaPlayerBridge::GetCurrentTime() {
+ if (!prepared_)
+ return pending_seek_;
+ JNIEnv* env = AttachCurrentThread();
+ return base::TimeDelta::FromMilliseconds(
+ Java_RemoteMediaPlayerBridge_getCurrentPosition(
+ env, java_bridge_.obj()));
+}
+
+base::TimeDelta RemoteMediaPlayerBridge::GetDuration() {
+ if (!prepared_)
+ return duration_;
+ JNIEnv* env = AttachCurrentThread();
+ const int duration_ms =
+ Java_RemoteMediaPlayerBridge_getDuration(env, java_bridge_.obj());
+ // 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) {
+ return local_player_->GetDuration();
+ }
+ return duration_ms < 0 ? media::kInfiniteDuration()
+ : base::TimeDelta::FromMilliseconds(duration_ms);
+}
+
+bool RemoteMediaPlayerBridge::IsPlaying() {
+ if (!prepared_)
+ return pending_play_;
+
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+ jboolean result = Java_RemoteMediaPlayerBridge_isPlaying(
+ env, java_bridge_.obj());
+ return result;
+}
+
+bool RemoteMediaPlayerBridge::CanPause() {
+ return true;
+}
+
+bool RemoteMediaPlayerBridge::CanSeekForward() {
+ return true;
+}
+
+bool RemoteMediaPlayerBridge::CanSeekBackward() {
+ return true;
+}
+
+bool RemoteMediaPlayerBridge::IsPlayerReady() {
+ return prepared_;
+}
+
+GURL RemoteMediaPlayerBridge::GetUrl() {
+ return url_;
+}
+
+GURL RemoteMediaPlayerBridge::GetFirstPartyForCookies() {
+ return first_party_for_cookies_;
+}
+
+void RemoteMediaPlayerBridge::Initialize() {
+ cookies_.clear();
+ media::MediaResourceGetter* resource_getter =
+ manager()->GetMediaResourceGetter();
+ resource_getter->GetCookies(
+ url_, first_party_for_cookies_,
+ base::Bind(&RemoteMediaPlayerBridge::OnCookiesRetrieved,
+ weak_factory_.GetWeakPtr()));
+}
+
+bool RemoteMediaPlayerBridge::IsMediaPlayableRemotely() const {
+ JNIEnv* env = AttachCurrentThread();
+ CHECK(env);
+
+ return Java_RemoteMediaPlayerBridge_isMediaPlayableRemotely(
+ env, java_bridge_.obj());
+}
+
+base::android::ScopedJavaLocalRef<jstring> RemoteMediaPlayerBridge::GetTitle(
+ JNIEnv* env, jobject obj) {
+ base::string16 title;
+ content::ContentViewCoreImpl* core =
+ static_cast<RemoteMediaPlayerManager*>(manager())->GetContentViewCore();
+ if (core) {
+ content::WebContents* contents = core->GetWebContents();
+ if (contents) {
+ title = contents->GetTitle();
+ }
+ }
+ return base::android::ConvertUTF16ToJavaString(env, title);
+}
+
+void RemoteMediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
+ // TODO(aberent) Do we need to retrieve auth credentials for basic
+ // authentication? MediaPlayerBridge does.
+ cookies_ = cookies;
+}
+
+} // namespace remote_media