Index: chrome/browser/media/android/remote/remote_media_player_bridge.cc |
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 0000000000000000000000000000000000000000..b5449afb4ec4846614602dc1dfe16de843b366a5 |
--- /dev/null |
+++ b/chrome/browser/media/android/remote/remote_media_player_bridge.cc |
@@ -0,0 +1,504 @@ |
+// 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 "chrome/browser/media/android/remote/remote_media_player_manager.h" |
whywhat
2015/02/25 16:31:31
blank line after the class header include
aberent
2015/03/11 18:29:58
Done.
|
+#include "base/android/scoped_java_ref.h" |
+#include "base/android/jni_android.h" |
+#include "base/android/jni_string.h" |
+#include "content/browser/android/content_view_core_impl.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" |
+ |
+#include "jni/RemoteMediaPlayerBridge_jni.h" |
+ |
+using base::android::ConvertUTF8ToJavaString; |
+using base::android::ScopedJavaLocalRef; |
+using base::android::AttachCurrentThread; |
+ |
+namespace { |
whywhat
2015/02/25 16:31:31
why didn't we use the anonymous namespace downstre
aberent
2015/03/10 18:46:46
Stupidity? (I wrote this originally). I added this
|
+/* |
+ * Dummy function to fix callbacks; 0 argument Callback constructor creates |
+ * a callback to 0x0, which crashes when called. |
+ */ |
+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, |
+ RequestMediaResourcesCB(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(); |
+; |
whywhat
2015/02/25 16:31:31
remove the line
aberent
2015/03/11 18:29:58
Done.
|
+ 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; |
+ } |
+ } |
+ |
whywhat
2015/02/25 16:31:31
nit: remove the empty line
aberent
2015/03/11 18:29:58
Done.
|
+} |
+ |
+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 { |
+ JNIEnv* env = AttachCurrentThread(); |
+ CHECK(env); |
+ |
+ jboolean result = |
+ Java_RemoteMediaPlayerBridge_isRemotePlaybackPreferredForFrame( |
+ env, java_bridge_.obj()); |
+ return result; |
+} |
+ |
+std::string RemoteMediaPlayerBridge::GetCastingMessage() { |
+ return casting_message_.get() ? |
+ *casting_message_.get() : 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) { |
+ cookies_ = cookies; |
+} |
+ |
+} // namespace remote_media |