1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media/android/remote/remote_media_player_bridge.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "chrome/browser/media/android/remote/record_cast_action.h"
10 #include "chrome/browser/media/android/remote/remote_media_player_manager.h"
11 #include "content/public/browser/android/content_view_core.h"
12 #include "content/public/browser/web_contents.h"
13 #include "jni/RemoteMediaPlayerBridge_jni.h"
14 #include "media/base/android/media_common_android.h"
15 #include "media/base/android/media_resource_getter.h"
16 #include "media/base/timestamp_constants.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "ui/gfx/android/java_bitmap.h"
20 using base::android::ConvertUTF8ToJavaString
;
21 using base::android::ScopedJavaLocalRef
;
22 using base::android::AttachCurrentThread
;
26 * Dummy function for RequestMediaResources callback. The callback is never
27 * actually called by MediaPlayerAndroid or RemoteMediaPlayer but is needed
28 * to compile the constructor call.
30 void DoNothing(int /*i*/) {}
33 namespace remote_media
{
35 RemoteMediaPlayerBridge::RemoteMediaPlayerBridge(
36 MediaPlayerAndroid
* local_player
, const std::string
& user_agent
,
37 bool hide_url_log
, RemoteMediaPlayerManager
* manager
)
38 : MediaPlayerAndroid(local_player
->player_id(), manager
,
39 base::Bind(&DoNothing
),
40 local_player
->frame_url()),
41 start_position_millis_(0),
42 local_player_(local_player
),
48 should_seek_on_prepare_(false),
49 hide_url_log_(hide_url_log
),
51 url_(local_player
->GetUrl()),
52 first_party_for_cookies_(local_player
->GetFirstPartyForCookies()),
53 user_agent_(user_agent
),
55 if (local_player
->GetCurrentTime().InMilliseconds() > 0)
56 start_position_millis_
= local_player
->GetCurrentTime().InMilliseconds();
57 JNIEnv
* env
= base::android::AttachCurrentThread();
59 ScopedJavaLocalRef
<jstring
> j_url_string
;
60 if (url_
.is_valid()) {
61 // Create a Java String for the URL.
62 j_url_string
= ConvertUTF8ToJavaString(env
, url_
.spec());
64 ScopedJavaLocalRef
<jstring
> j_frame_url_string
;
65 if (local_player
->frame_url().is_valid()) {
66 // Create a Java String for the URL.
67 j_frame_url_string
= ConvertUTF8ToJavaString(
68 env
, local_player
->frame_url().spec());
71 Java_RemoteMediaPlayerBridge_create(env
, reinterpret_cast<intptr_t>(this),
72 start_position_millis_
,
74 j_frame_url_string
.obj()));
77 RemoteMediaPlayerBridge::~RemoteMediaPlayerBridge() {
78 JNIEnv
* env
= base::android::AttachCurrentThread();
80 Java_RemoteMediaPlayerBridge_destroy(env
, java_bridge_
.obj());
84 int RemoteMediaPlayerBridge::GetVideoWidth() {
85 return local_player_
->GetVideoWidth();
88 int RemoteMediaPlayerBridge::GetVideoHeight() {
89 return local_player_
->GetVideoHeight();
92 void RemoteMediaPlayerBridge::OnVideoSizeChanged(int width
, int height
) {
95 MediaPlayerAndroid::OnVideoSizeChanged(width
, height
);
98 void RemoteMediaPlayerBridge::OnPlaybackComplete() {
99 time_update_timer_
.Stop();
100 MediaPlayerAndroid::OnPlaybackComplete();
103 void RemoteMediaPlayerBridge::OnMediaInterrupted() {}
105 void RemoteMediaPlayerBridge::OnMediaPrepared() {
110 duration_
= GetDuration();
112 // If media player was recovered from a saved state, consume all the pending
114 if (should_seek_on_prepare_
) {
115 PendingSeekInternal(pending_seek_
);
116 pending_seek_
= base::TimeDelta::FromMilliseconds(0);
117 should_seek_on_prepare_
= false;
122 pending_play_
= false;
125 manager()->OnMediaMetadataChanged(
126 player_id(), duration_
, width_
, height_
, true);
129 void RemoteMediaPlayerBridge::StartInternal() {
130 JNIEnv
* env
= AttachCurrentThread();
131 Java_RemoteMediaPlayerBridge_start(env
, java_bridge_
.obj());
132 if (!time_update_timer_
.IsRunning()) {
133 time_update_timer_
.Start(
135 base::TimeDelta::FromMilliseconds(media::kTimeUpdateInterval
),
136 this, &RemoteMediaPlayerBridge::OnTimeUpdateTimerFired
);
140 void RemoteMediaPlayerBridge::PauseInternal() {
141 JNIEnv
* env
= AttachCurrentThread();
142 Java_RemoteMediaPlayerBridge_pause(env
, java_bridge_
.obj());
143 time_update_timer_
.Stop();
146 void RemoteMediaPlayerBridge::SeekInternal(base::TimeDelta time
) {
147 if (time
> duration_
)
150 // Seeking to an invalid position may cause media player to stuck in an
152 if (time
< base::TimeDelta()) {
153 DCHECK_EQ(-1.0, time
.InMillisecondsF());
157 JNIEnv
* env
= AttachCurrentThread();
159 int time_msec
= static_cast<int>(time
.InMilliseconds());
160 Java_RemoteMediaPlayerBridge_seekTo(
161 env
, java_bridge_
.obj(), time_msec
);
164 void RemoteMediaPlayerBridge::OnTimeUpdateTimerFired() {
165 manager()->OnTimeUpdate(
166 player_id(), GetCurrentTime(), base::TimeTicks::Now());
169 void RemoteMediaPlayerBridge::PendingSeekInternal(const base::TimeDelta
& time
) {
173 void RemoteMediaPlayerBridge::Prepare() {
175 DCHECK(IsMediaPlayableRemotely());
177 AttachListener(java_bridge_
.obj());
178 JNIEnv
* env
= AttachCurrentThread();
181 if (url_
.is_valid()) {
182 // Create a Java String for the URL.
183 ScopedJavaLocalRef
<jstring
> j_url_string
=
184 ConvertUTF8ToJavaString(env
, url_
.spec());
186 jobject j_context
= base::android::GetApplicationContext();
189 ScopedJavaLocalRef
<jstring
> j_cookies
= ConvertUTF8ToJavaString(
191 ScopedJavaLocalRef
<jstring
> j_user_agent
= ConvertUTF8ToJavaString(
194 if (!Java_RemoteMediaPlayerBridge_setDataSource(
195 env
, java_bridge_
.obj(), j_context
, j_url_string
.obj(),
196 j_cookies
.obj(), j_user_agent
.obj(), hide_url_log_
)) {
197 OnMediaError(MEDIA_ERROR_FORMAT
);
202 if (!Java_RemoteMediaPlayerBridge_prepareAsync(env
, java_bridge_
.obj()))
203 OnMediaError(MEDIA_ERROR_FORMAT
);
206 void RemoteMediaPlayerBridge::Pause(bool is_media_related_action
) {
207 // Ignore the pause if it's not from an event that is explicitly telling
208 // the video to pause. It's possible for Pause() to be called for other
209 // reasons, such as freeing resources, etc. and during those times, the
210 // remote video playback should not be paused.
211 if (is_media_related_action
) {
213 pending_play_
= false;
215 if (prepared_
&& IsPlaying())
218 pending_play_
= false;
223 void RemoteMediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface
) {
224 // The surface is reset whenever the fullscreen view is destroyed or created.
225 // Since the remote player doesn't use it, we forward it to the local player
226 // for the time when user disconnects and resumes local playback
227 // (see crbug.com/420690).
228 local_player_
->SetVideoSurface(surface
.Pass());
231 base::android::ScopedJavaLocalRef
<jstring
> RemoteMediaPlayerBridge::GetFrameUrl(
232 JNIEnv
* env
, jobject obj
) {
233 return ConvertUTF8ToJavaString(env
, frame_url().spec());
236 void RemoteMediaPlayerBridge::OnPlaying(JNIEnv
* env
, jobject obj
) {
237 static_cast<RemoteMediaPlayerManager
*>(manager())->OnPlaying(player_id());
240 void RemoteMediaPlayerBridge::OnPaused(JNIEnv
* env
, jobject obj
) {
241 static_cast<RemoteMediaPlayerManager
*>(manager())->OnPaused(player_id());
244 void RemoteMediaPlayerBridge::OnRouteSelected(JNIEnv
* env
, jobject obj
,
245 jstring castingMessage
) {
246 casting_message_
.reset(
248 base::android::ConvertJavaStringToUTF8(env
, castingMessage
)));
249 static_cast<RemoteMediaPlayerManager
*>(manager())->OnRemoteDeviceSelected(
253 void RemoteMediaPlayerBridge::OnRouteUnselected(JNIEnv
* env
, jobject obj
) {
254 casting_message_
.reset();
255 static_cast<RemoteMediaPlayerManager
*>(manager())->OnRemoteDeviceUnselected(
259 void RemoteMediaPlayerBridge::OnPlaybackFinished(JNIEnv
* env
, jobject obj
) {
260 static_cast<RemoteMediaPlayerManager
*>(manager())->OnRemotePlaybackFinished(
264 void RemoteMediaPlayerBridge::OnRouteAvailabilityChanged(JNIEnv
* env
,
266 jboolean available
) {
267 static_cast<RemoteMediaPlayerManager
*>(manager())->
268 OnRouteAvailabilityChanged(player_id(), available
);
272 bool RemoteMediaPlayerBridge::RegisterRemoteMediaPlayerBridge(JNIEnv
* env
) {
273 bool ret
= RegisterNativesImpl(env
);
274 DCHECK(g_RemoteMediaPlayerBridge_clazz
);
278 void RemoteMediaPlayerBridge::RequestRemotePlayback() {
279 JNIEnv
* env
= AttachCurrentThread();
282 Java_RemoteMediaPlayerBridge_requestRemotePlayback(
283 env
, java_bridge_
.obj());
286 void RemoteMediaPlayerBridge::RequestRemotePlaybackControl() {
287 JNIEnv
* env
= AttachCurrentThread();
290 Java_RemoteMediaPlayerBridge_requestRemotePlaybackControl(
291 env
, java_bridge_
.obj());
294 void RemoteMediaPlayerBridge::SetNativePlayer() {
295 JNIEnv
* env
= AttachCurrentThread();
298 Java_RemoteMediaPlayerBridge_setNativePlayer(
299 env
, java_bridge_
.obj());
302 void RemoteMediaPlayerBridge::OnPlayerCreated() {
303 JNIEnv
* env
= AttachCurrentThread();
306 Java_RemoteMediaPlayerBridge_onPlayerCreated(
307 env
, java_bridge_
.obj());
310 void RemoteMediaPlayerBridge::OnPlayerDestroyed() {
311 JNIEnv
* env
= AttachCurrentThread();
314 Java_RemoteMediaPlayerBridge_onPlayerDestroyed(
315 env
, java_bridge_
.obj());
318 bool RemoteMediaPlayerBridge::IsRemotePlaybackAvailable() const {
319 JNIEnv
* env
= AttachCurrentThread();
322 jboolean result
= Java_RemoteMediaPlayerBridge_isRemotePlaybackAvailable(
323 env
, java_bridge_
.obj());
328 bool RemoteMediaPlayerBridge::IsRemotePlaybackPreferredForFrame() const {
330 // We have already decided to use remote playback
333 JNIEnv
* env
= AttachCurrentThread();
337 Java_RemoteMediaPlayerBridge_isRemotePlaybackPreferredForFrame(
338 env
, java_bridge_
.obj());
342 std::string
RemoteMediaPlayerBridge::GetCastingMessage() {
343 return casting_message_
?
344 *casting_message_
: std::string();
347 void RemoteMediaPlayerBridge::SetPosterBitmap(
348 const std::vector
<SkBitmap
>& bitmaps
) {
349 JNIEnv
* env
= AttachCurrentThread();
352 if (bitmaps
.empty()) {
353 Java_RemoteMediaPlayerBridge_setPosterBitmap(env
, java_bridge_
.obj(), NULL
);
355 ScopedJavaLocalRef
<jobject
> j_poster_bitmap
;
356 j_poster_bitmap
= gfx::ConvertToJavaBitmap(&(bitmaps
[0]));
358 Java_RemoteMediaPlayerBridge_setPosterBitmap(env
, java_bridge_
.obj(),
359 j_poster_bitmap
.obj());
363 void RemoteMediaPlayerBridge::Start() {
365 pending_play_
= true;
371 pending_play_
= true;
375 void RemoteMediaPlayerBridge::SeekTo(base::TimeDelta timestamp
) {
376 // Record the time to seek when OnMediaPrepared() is called.
377 pending_seek_
= timestamp
;
378 should_seek_on_prepare_
= true;
383 SeekInternal(timestamp
);
386 void RemoteMediaPlayerBridge::Release() {
389 time_update_timer_
.Stop();
391 pending_seek_
= GetCurrentTime();
392 should_seek_on_prepare_
= true;
396 pending_play_
= false;
397 JNIEnv
* env
= AttachCurrentThread();
398 Java_RemoteMediaPlayerBridge_release(env
, java_bridge_
.obj());
403 void RemoteMediaPlayerBridge::SetVolume(double volume
) {
409 JNIEnv
* env
= AttachCurrentThread();
411 Java_RemoteMediaPlayerBridge_setVolume(
412 env
, java_bridge_
.obj(), volume
);
415 base::TimeDelta
RemoteMediaPlayerBridge::GetCurrentTime() {
417 return pending_seek_
;
418 JNIEnv
* env
= AttachCurrentThread();
419 return base::TimeDelta::FromMilliseconds(
420 Java_RemoteMediaPlayerBridge_getCurrentPosition(
421 env
, java_bridge_
.obj()));
424 base::TimeDelta
RemoteMediaPlayerBridge::GetDuration() {
427 JNIEnv
* env
= AttachCurrentThread();
428 const int duration_ms
=
429 Java_RemoteMediaPlayerBridge_getDuration(env
, java_bridge_
.obj());
430 // Sometimes we can't get the duration remotely, but the local media player
432 // TODO (aberent) This is for YouTube. Remove it when the YouTube receiver is
434 if (duration_ms
== 0) {
435 return local_player_
->GetDuration();
437 return duration_ms
< 0 ? media::kInfiniteDuration()
438 : base::TimeDelta::FromMilliseconds(duration_ms
);
441 bool RemoteMediaPlayerBridge::IsPlaying() {
443 return pending_play_
;
445 JNIEnv
* env
= AttachCurrentThread();
447 jboolean result
= Java_RemoteMediaPlayerBridge_isPlaying(
448 env
, java_bridge_
.obj());
452 bool RemoteMediaPlayerBridge::CanPause() {
456 bool RemoteMediaPlayerBridge::CanSeekForward() {
460 bool RemoteMediaPlayerBridge::CanSeekBackward() {
464 bool RemoteMediaPlayerBridge::IsPlayerReady() {
468 GURL
RemoteMediaPlayerBridge::GetUrl() {
472 GURL
RemoteMediaPlayerBridge::GetFirstPartyForCookies() {
473 return first_party_for_cookies_
;
476 void RemoteMediaPlayerBridge::Initialize() {
478 media::MediaResourceGetter
* resource_getter
=
479 manager()->GetMediaResourceGetter();
480 resource_getter
->GetCookies(
481 url_
, first_party_for_cookies_
,
482 base::Bind(&RemoteMediaPlayerBridge::OnCookiesRetrieved
,
483 weak_factory_
.GetWeakPtr()));
486 bool RemoteMediaPlayerBridge::IsMediaPlayableRemotely() const {
487 JNIEnv
* env
= AttachCurrentThread();
490 return Java_RemoteMediaPlayerBridge_isMediaPlayableRemotely(
491 env
, java_bridge_
.obj());
494 base::android::ScopedJavaLocalRef
<jstring
> RemoteMediaPlayerBridge::GetTitle(
495 JNIEnv
* env
, jobject obj
) {
496 base::string16 title
;
497 content::ContentViewCore
* core
=
498 static_cast<RemoteMediaPlayerManager
*>(manager())->GetContentViewCore();
500 content::WebContents
* contents
= core
->GetWebContents();
502 title
= contents
->GetTitle();
505 return base::android::ConvertUTF16ToJavaString(env
, title
);
508 void RemoteMediaPlayerBridge::OnCookiesRetrieved(const std::string
& cookies
) {
509 // TODO(aberent) Do we need to retrieve auth credentials for basic
510 // authentication? MediaPlayerBridge does.
514 } // namespace remote_media