OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "chrome/browser/media/android/remote/remote_media_player_bridge.h" | |
6 | |
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/browser/android/content_view_core_impl.h" | |
12 #include "jni/RemoteMediaPlayerBridge_jni.h" | |
13 #include "media/base/android/media_common_android.h" | |
14 #include "media/base/android/media_resource_getter.h" | |
15 #include "third_party/skia/include/core/SkBitmap.h" | |
16 #include "ui/gfx/android/java_bitmap.h" | |
17 | |
18 using base::android::ConvertUTF8ToJavaString; | |
19 using base::android::ScopedJavaLocalRef; | |
20 using base::android::AttachCurrentThread; | |
21 | |
22 namespace { | |
23 /* | |
24 * Dummy function for RequestMediaResources callback. The callback is never | |
25 * actually called by MediaPlayerAndroid or RemoteMediaPlayer but is needed | |
26 * to compile the constructor call. | |
27 */ | |
28 void DoNothing(int /*i*/) {} | |
29 } | |
30 | |
31 namespace remote_media { | |
32 | |
33 RemoteMediaPlayerBridge::RemoteMediaPlayerBridge( | |
34 MediaPlayerAndroid* local_player, const std::string& user_agent, | |
35 bool hide_url_log, RemoteMediaPlayerManager* manager) | |
36 : MediaPlayerAndroid(local_player->player_id(), manager, | |
37 base::Bind(&DoNothing), | |
38 local_player->frame_url()), | |
39 start_position_millis_(0), | |
40 local_player_(local_player), | |
41 in_use_(false), | |
42 prepared_(false), | |
43 pending_play_(false), | |
44 width_(0), | |
45 height_(0), | |
46 should_seek_on_prepare_(false), | |
47 hide_url_log_(hide_url_log), | |
48 volume_(-1.0), | |
49 url_(local_player->GetUrl()), | |
50 first_party_for_cookies_(local_player->GetFirstPartyForCookies()), | |
51 user_agent_(user_agent), | |
52 weak_factory_(this) { | |
53 if (local_player->GetCurrentTime().InMilliseconds() > 0) | |
54 start_position_millis_ = local_player->GetCurrentTime().InMilliseconds(); | |
55 JNIEnv* env = base::android::AttachCurrentThread(); | |
56 CHECK(env); | |
57 ScopedJavaLocalRef<jstring> j_url_string; | |
58 if (url_.is_valid()) { | |
59 // Create a Java String for the URL. | |
60 j_url_string = ConvertUTF8ToJavaString(env, url_.spec()); | |
61 } | |
62 ScopedJavaLocalRef<jstring> j_frame_url_string; | |
63 if (local_player->frame_url().is_valid()) { | |
64 // Create a Java String for the URL. | |
65 j_frame_url_string = ConvertUTF8ToJavaString( | |
66 env, local_player->frame_url().spec()); | |
67 } | |
68 java_bridge_.Reset( | |
69 Java_RemoteMediaPlayerBridge_create(env, reinterpret_cast<intptr_t>(this), | |
70 start_position_millis_, | |
71 j_url_string.obj(), | |
72 j_frame_url_string.obj())); | |
73 } | |
74 | |
75 RemoteMediaPlayerBridge::~RemoteMediaPlayerBridge() { | |
76 JNIEnv* env = base::android::AttachCurrentThread(); | |
77 CHECK(env); | |
78 Java_RemoteMediaPlayerBridge_destroy(env, java_bridge_.obj()); | |
79 Release(); | |
80 } | |
81 | |
82 int RemoteMediaPlayerBridge::GetVideoWidth() { | |
83 return local_player_->GetVideoWidth(); | |
84 } | |
85 | |
86 int RemoteMediaPlayerBridge::GetVideoHeight() { | |
87 return local_player_->GetVideoHeight(); | |
88 } | |
89 | |
90 void RemoteMediaPlayerBridge::OnVideoSizeChanged(int width, int height) { | |
91 width_ = width; | |
92 height_ = height; | |
93 MediaPlayerAndroid::OnVideoSizeChanged(width, height); | |
94 } | |
95 | |
96 void RemoteMediaPlayerBridge::OnPlaybackComplete() { | |
97 time_update_timer_.Stop(); | |
98 MediaPlayerAndroid::OnPlaybackComplete(); | |
99 } | |
100 | |
101 void RemoteMediaPlayerBridge::OnMediaInterrupted() {} | |
102 | |
103 void RemoteMediaPlayerBridge::OnMediaPrepared() { | |
104 if (!in_use_) | |
105 return; | |
106 | |
107 prepared_ = true; | |
108 duration_ = GetDuration(); | |
109 | |
110 // If media player was recovered from a saved state, consume all the pending | |
111 // events. | |
112 if (should_seek_on_prepare_) { | |
113 PendingSeekInternal(pending_seek_); | |
114 pending_seek_ = base::TimeDelta::FromMilliseconds(0); | |
115 should_seek_on_prepare_ = false; | |
116 } | |
117 | |
118 if (pending_play_) { | |
119 StartInternal(); | |
120 pending_play_ = false; | |
121 } | |
122 | |
123 manager()->OnMediaMetadataChanged( | |
124 player_id(), duration_, width_, height_, true); | |
125 } | |
126 | |
127 void RemoteMediaPlayerBridge::StartInternal() { | |
128 JNIEnv* env = AttachCurrentThread(); | |
129 Java_RemoteMediaPlayerBridge_start(env, java_bridge_.obj()); | |
130 if (!time_update_timer_.IsRunning()) { | |
131 time_update_timer_.Start( | |
132 FROM_HERE, | |
133 base::TimeDelta::FromMilliseconds(media::kTimeUpdateInterval), | |
134 this, &RemoteMediaPlayerBridge::OnTimeUpdateTimerFired); | |
135 } | |
136 } | |
137 | |
138 void RemoteMediaPlayerBridge::PauseInternal() { | |
139 JNIEnv* env = AttachCurrentThread(); | |
140 Java_RemoteMediaPlayerBridge_pause(env, java_bridge_.obj()); | |
141 time_update_timer_.Stop(); | |
142 } | |
143 | |
144 void RemoteMediaPlayerBridge::SeekInternal(base::TimeDelta time) { | |
145 if (time > duration_) | |
146 time = duration_; | |
147 | |
148 // Seeking to an invalid position may cause media player to stuck in an | |
149 // error state. | |
150 if (time < base::TimeDelta()) { | |
151 DCHECK_EQ(-1.0, time.InMillisecondsF()); | |
152 return; | |
153 } | |
154 | |
155 JNIEnv* env = AttachCurrentThread(); | |
156 CHECK(env); | |
157 int time_msec = static_cast<int>(time.InMilliseconds()); | |
158 Java_RemoteMediaPlayerBridge_seekTo( | |
159 env, java_bridge_.obj(), time_msec); | |
160 } | |
161 | |
162 void RemoteMediaPlayerBridge::OnTimeUpdateTimerFired() { | |
163 manager()->OnTimeUpdate( | |
164 player_id(), GetCurrentTime(), base::TimeTicks::Now()); | |
165 } | |
166 | |
167 void RemoteMediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) { | |
168 SeekInternal(time); | |
169 } | |
170 | |
171 void RemoteMediaPlayerBridge::Prepare() { | |
172 DCHECK(!in_use_); | |
173 DCHECK(IsMediaPlayableRemotely()); | |
174 in_use_ = true; | |
175 AttachListener(java_bridge_.obj()); | |
176 JNIEnv* env = AttachCurrentThread(); | |
177 CHECK(env); | |
178 | |
179 if (url_.is_valid()) { | |
180 // Create a Java String for the URL. | |
181 ScopedJavaLocalRef<jstring> j_url_string = | |
182 ConvertUTF8ToJavaString(env, url_.spec()); | |
183 | |
184 jobject j_context = base::android::GetApplicationContext(); | |
185 DCHECK(j_context); | |
186 | |
187 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString( | |
188 env, cookies_); | |
189 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString( | |
190 env, user_agent_); | |
191 | |
192 if (!Java_RemoteMediaPlayerBridge_setDataSource( | |
193 env, java_bridge_.obj(), j_context, j_url_string.obj(), | |
194 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) { | |
195 OnMediaError(MEDIA_ERROR_FORMAT); | |
196 return; | |
197 } | |
198 } | |
199 | |
200 if (!Java_RemoteMediaPlayerBridge_prepareAsync(env, java_bridge_.obj())) | |
201 OnMediaError(MEDIA_ERROR_FORMAT); | |
202 } | |
203 | |
204 void RemoteMediaPlayerBridge::Pause(bool is_media_related_action) { | |
205 // Ignore the pause if it's not from an event that is explicitly telling | |
206 // the video to pause. It's possible for Pause() to be called for other | |
207 // reasons, such as freeing resources, etc. and during those times, the | |
208 // remote video playback should not be paused. | |
209 if (is_media_related_action) { | |
210 if (!in_use_) { | |
211 pending_play_ = false; | |
212 } else { | |
213 if (prepared_ && IsPlaying()) | |
214 PauseInternal(); | |
215 else | |
216 pending_play_ = false; | |
217 } | |
218 } | |
219 } | |
220 | |
221 void RemoteMediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) { | |
222 // The surface is reset whenever the fullscreen view is destroyed or created. | |
223 // Since the remote player doesn't use it, we forward it to the local player | |
224 // for the time when user disconnects and resumes local playback | |
225 // (see crbug.com/420690). | |
226 local_player_->SetVideoSurface(surface.Pass()); | |
227 } | |
228 | |
229 base::android::ScopedJavaLocalRef<jstring> RemoteMediaPlayerBridge::GetFrameUrl( | |
230 JNIEnv* env, jobject obj) { | |
231 return ConvertUTF8ToJavaString(env, frame_url().spec()); | |
232 } | |
233 | |
234 void RemoteMediaPlayerBridge::OnPlaying(JNIEnv* env, jobject obj) { | |
235 static_cast<RemoteMediaPlayerManager *>(manager())->OnPlaying(player_id()); | |
236 } | |
237 | |
238 void RemoteMediaPlayerBridge::OnPaused(JNIEnv* env, jobject obj) { | |
239 static_cast<RemoteMediaPlayerManager *>(manager())->OnPaused(player_id()); | |
240 } | |
241 | |
242 void RemoteMediaPlayerBridge::OnRouteSelected(JNIEnv* env, jobject obj, | |
243 jstring castingMessage) { | |
244 casting_message_.reset( | |
245 new std::string( | |
246 base::android::ConvertJavaStringToUTF8(env, castingMessage))); | |
247 static_cast<RemoteMediaPlayerManager *>(manager())->OnRemoteDeviceSelected( | |
248 player_id()); | |
249 } | |
250 | |
251 void RemoteMediaPlayerBridge::OnRouteUnselected(JNIEnv* env, jobject obj) { | |
252 casting_message_.reset(); | |
253 static_cast<RemoteMediaPlayerManager *>(manager())->OnRemoteDeviceUnselected( | |
254 player_id()); | |
255 } | |
256 | |
257 void RemoteMediaPlayerBridge::OnPlaybackFinished(JNIEnv* env, jobject obj) { | |
258 static_cast<RemoteMediaPlayerManager *>(manager())->OnRemotePlaybackFinished( | |
259 player_id()); | |
260 } | |
261 | |
262 void RemoteMediaPlayerBridge::OnRouteAvailabilityChanged(JNIEnv* env, | |
263 jobject obj, | |
264 jboolean available) { | |
265 static_cast<RemoteMediaPlayerManager *>(manager())-> | |
266 OnRouteAvailabilityChanged(player_id(), available); | |
267 } | |
268 | |
269 // static | |
270 bool RemoteMediaPlayerBridge::RegisterRemoteMediaPlayerBridge(JNIEnv* env) { | |
271 bool ret = RegisterNativesImpl(env); | |
272 DCHECK(g_RemoteMediaPlayerBridge_clazz); | |
273 return ret; | |
274 } | |
275 | |
276 void RemoteMediaPlayerBridge::RequestRemotePlayback() { | |
277 JNIEnv* env = AttachCurrentThread(); | |
278 CHECK(env); | |
279 | |
280 Java_RemoteMediaPlayerBridge_requestRemotePlayback( | |
281 env, java_bridge_.obj()); | |
282 } | |
283 | |
284 void RemoteMediaPlayerBridge::RequestRemotePlaybackControl() { | |
285 JNIEnv* env = AttachCurrentThread(); | |
286 CHECK(env); | |
287 | |
288 Java_RemoteMediaPlayerBridge_requestRemotePlaybackControl( | |
289 env, java_bridge_.obj()); | |
290 } | |
291 | |
292 void RemoteMediaPlayerBridge::SetNativePlayer() { | |
293 JNIEnv* env = AttachCurrentThread(); | |
294 CHECK(env); | |
295 | |
296 Java_RemoteMediaPlayerBridge_setNativePlayer( | |
297 env, java_bridge_.obj()); | |
298 } | |
299 | |
300 void RemoteMediaPlayerBridge::OnPlayerCreated() { | |
301 JNIEnv* env = AttachCurrentThread(); | |
302 CHECK(env); | |
303 | |
304 Java_RemoteMediaPlayerBridge_onPlayerCreated( | |
305 env, java_bridge_.obj()); | |
306 } | |
307 | |
308 void RemoteMediaPlayerBridge::OnPlayerDestroyed() { | |
309 JNIEnv* env = AttachCurrentThread(); | |
310 CHECK(env); | |
311 | |
312 Java_RemoteMediaPlayerBridge_onPlayerDestroyed( | |
313 env, java_bridge_.obj()); | |
314 } | |
315 | |
316 bool RemoteMediaPlayerBridge::IsRemotePlaybackAvailable() const { | |
317 JNIEnv* env = AttachCurrentThread(); | |
318 CHECK(env); | |
319 | |
320 jboolean result = Java_RemoteMediaPlayerBridge_isRemotePlaybackAvailable( | |
321 env, java_bridge_.obj()); | |
322 | |
323 return result; | |
324 } | |
325 | |
326 bool RemoteMediaPlayerBridge::IsRemotePlaybackPreferredForFrame() const { | |
327 if (in_use_) { | |
328 // We have already decided to use remote playback | |
329 return true; | |
330 } | |
331 JNIEnv* env = AttachCurrentThread(); | |
332 CHECK(env); | |
333 | |
334 jboolean result = | |
335 Java_RemoteMediaPlayerBridge_isRemotePlaybackPreferredForFrame( | |
336 env, java_bridge_.obj()); | |
337 return result; | |
338 } | |
339 | |
340 std::string RemoteMediaPlayerBridge::GetCastingMessage() { | |
341 return casting_message_ ? | |
342 *casting_message_ : std::string(); | |
343 } | |
344 | |
345 void RemoteMediaPlayerBridge::SetPosterBitmap( | |
346 const std::vector<SkBitmap>& bitmaps) { | |
347 JNIEnv* env = AttachCurrentThread(); | |
348 CHECK(env); | |
349 | |
350 if (bitmaps.empty()) { | |
351 Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_.obj(), NULL); | |
352 } else { | |
353 ScopedJavaLocalRef<jobject> j_poster_bitmap; | |
354 j_poster_bitmap = gfx::ConvertToJavaBitmap(&(bitmaps[0])); | |
355 | |
356 Java_RemoteMediaPlayerBridge_setPosterBitmap(env, java_bridge_.obj(), | |
357 j_poster_bitmap.obj()); | |
358 } | |
359 } | |
360 | |
361 void RemoteMediaPlayerBridge::Start() { | |
362 if (!in_use_) { | |
363 pending_play_ = true; | |
364 Prepare(); | |
365 } else { | |
366 if (prepared_) | |
367 StartInternal(); | |
368 else | |
369 pending_play_ = true; | |
370 } | |
371 } | |
372 | |
373 void RemoteMediaPlayerBridge::SeekTo(base::TimeDelta timestamp) { | |
374 // Record the time to seek when OnMediaPrepared() is called. | |
375 pending_seek_ = timestamp; | |
376 should_seek_on_prepare_ = true; | |
377 | |
378 if (!in_use_) | |
379 Prepare(); | |
380 else if (prepared_) | |
381 SeekInternal(timestamp); | |
382 } | |
383 | |
384 void RemoteMediaPlayerBridge::Release() { | |
385 if (!in_use_) | |
386 return; | |
387 time_update_timer_.Stop(); | |
388 if (prepared_) { | |
389 pending_seek_ = GetCurrentTime(); | |
390 should_seek_on_prepare_ = true; | |
391 } | |
392 | |
393 prepared_ = false; | |
394 pending_play_ = false; | |
395 JNIEnv* env = AttachCurrentThread(); | |
396 Java_RemoteMediaPlayerBridge_release(env, java_bridge_.obj()); | |
397 DetachListener(); | |
398 in_use_ = false; | |
399 } | |
400 | |
401 void RemoteMediaPlayerBridge::SetVolume(double volume) { | |
402 if (!in_use_) { | |
403 volume_ = volume; | |
404 return; | |
405 } | |
406 | |
407 JNIEnv* env = AttachCurrentThread(); | |
408 CHECK(env); | |
409 Java_RemoteMediaPlayerBridge_setVolume( | |
410 env, java_bridge_.obj(), volume); | |
411 } | |
412 | |
413 base::TimeDelta RemoteMediaPlayerBridge::GetCurrentTime() { | |
414 if (!prepared_) | |
415 return pending_seek_; | |
416 JNIEnv* env = AttachCurrentThread(); | |
417 return base::TimeDelta::FromMilliseconds( | |
418 Java_RemoteMediaPlayerBridge_getCurrentPosition( | |
419 env, java_bridge_.obj())); | |
420 } | |
421 | |
422 base::TimeDelta RemoteMediaPlayerBridge::GetDuration() { | |
423 if (!prepared_) | |
424 return duration_; | |
425 JNIEnv* env = AttachCurrentThread(); | |
426 const int duration_ms = | |
427 Java_RemoteMediaPlayerBridge_getDuration(env, java_bridge_.obj()); | |
428 // Sometimes we can't get the duration remotely, but the local media player | |
429 // knows it. | |
430 // TODO (aberent) This is for YouTube. Remove it when the YouTube receiver is | |
431 // fixed. | |
432 if (duration_ms == 0) { | |
433 return local_player_->GetDuration(); | |
434 } | |
435 return duration_ms < 0 ? media::kInfiniteDuration() | |
436 : base::TimeDelta::FromMilliseconds(duration_ms); | |
437 } | |
438 | |
439 bool RemoteMediaPlayerBridge::IsPlaying() { | |
440 if (!prepared_) | |
441 return pending_play_; | |
442 | |
443 JNIEnv* env = AttachCurrentThread(); | |
444 CHECK(env); | |
445 jboolean result = Java_RemoteMediaPlayerBridge_isPlaying( | |
446 env, java_bridge_.obj()); | |
447 return result; | |
448 } | |
449 | |
450 bool RemoteMediaPlayerBridge::CanPause() { | |
451 return true; | |
452 } | |
453 | |
454 bool RemoteMediaPlayerBridge::CanSeekForward() { | |
455 return true; | |
456 } | |
457 | |
458 bool RemoteMediaPlayerBridge::CanSeekBackward() { | |
459 return true; | |
460 } | |
461 | |
462 bool RemoteMediaPlayerBridge::IsPlayerReady() { | |
463 return prepared_; | |
464 } | |
465 | |
466 GURL RemoteMediaPlayerBridge::GetUrl() { | |
467 return url_; | |
468 } | |
469 | |
470 GURL RemoteMediaPlayerBridge::GetFirstPartyForCookies() { | |
471 return first_party_for_cookies_; | |
472 } | |
473 | |
474 void RemoteMediaPlayerBridge::Initialize() { | |
475 cookies_.clear(); | |
476 media::MediaResourceGetter* resource_getter = | |
477 manager()->GetMediaResourceGetter(); | |
478 resource_getter->GetCookies( | |
479 url_, first_party_for_cookies_, | |
480 base::Bind(&RemoteMediaPlayerBridge::OnCookiesRetrieved, | |
481 weak_factory_.GetWeakPtr())); | |
482 } | |
483 | |
484 bool RemoteMediaPlayerBridge::IsMediaPlayableRemotely() const { | |
485 JNIEnv* env = AttachCurrentThread(); | |
486 CHECK(env); | |
487 | |
488 return Java_RemoteMediaPlayerBridge_isMediaPlayableRemotely( | |
489 env, java_bridge_.obj()); | |
490 } | |
491 | |
492 base::android::ScopedJavaLocalRef<jstring> RemoteMediaPlayerBridge::GetTitle( | |
493 JNIEnv* env, jobject obj) { | |
494 base::string16 title; | |
495 content::ContentViewCoreImpl* core = | |
496 static_cast<RemoteMediaPlayerManager*>(manager())->GetContentViewCore(); | |
497 if (core) { | |
498 content::WebContents* contents = core->GetWebContents(); | |
499 if (contents) { | |
500 title = contents->GetTitle(); | |
501 } | |
502 } | |
503 return base::android::ConvertUTF16ToJavaString(env, title); | |
504 } | |
505 | |
506 void RemoteMediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) { | |
507 cookies_ = cookies; | |
qinmin
2015/03/20 19:35:46
Do we need to retrieve auth credentials for basic
aberent
2015/03/23 17:35:31
Adding a TODO to investigate.
| |
508 } | |
509 | |
510 } // namespace remote_media | |
OLD | NEW |