1 // Copyright (c) 2012 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 "media/base/android/media_player_bridge.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/string_util.h"
13 #include "jni/MediaPlayerBridge_jni.h"
14 #include "media/base/android/media_common_android.h"
15 #include "media/base/android/media_player_manager.h"
16 #include "media/base/android/media_resource_getter.h"
17 #include "media/base/android/media_url_interceptor.h"
18
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::ScopedJavaLocalRef;
21
22 namespace media {
23
MediaPlayerBridge(int player_id,const GURL & url,const GURL & first_party_for_cookies,const std::string & user_agent,bool hide_url_log,MediaPlayerManager * manager,const RequestMediaResourcesCB & request_media_resources_cb,const GURL & frame_url,bool allow_credentials)24 MediaPlayerBridge::MediaPlayerBridge(
25 int player_id,
26 const GURL& url,
27 const GURL& first_party_for_cookies,
28 const std::string& user_agent,
29 bool hide_url_log,
30 MediaPlayerManager* manager,
31 const RequestMediaResourcesCB& request_media_resources_cb,
32 const GURL& frame_url,
33 bool allow_credentials)
34 : MediaPlayerAndroid(player_id,
35 manager,
36 request_media_resources_cb,
37 frame_url),
38 prepared_(false),
39 pending_play_(false),
40 should_seek_on_prepare_(false),
41 url_(url),
42 first_party_for_cookies_(first_party_for_cookies),
43 user_agent_(user_agent),
44 hide_url_log_(hide_url_log),
45 width_(0),
46 height_(0),
47 can_pause_(true),
48 can_seek_forward_(true),
49 can_seek_backward_(true),
50 volume_(-1.0),
51 allow_credentials_(allow_credentials),
52 weak_factory_(this) {
53 }
54
~MediaPlayerBridge()55 MediaPlayerBridge::~MediaPlayerBridge() {
56 if (!j_media_player_bridge_.is_null()) {
57 JNIEnv* env = base::android::AttachCurrentThread();
58 CHECK(env);
59 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
60 }
61 Release();
62 }
63
Initialize()64 void MediaPlayerBridge::Initialize() {
65 cookies_.clear();
66 if (url_.SchemeIsFile()) {
67 ExtractMediaMetadata(url_.spec());
68 return;
69 }
70
71 media::MediaResourceGetter* resource_getter =
72 manager()->GetMediaResourceGetter();
73 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
74 resource_getter->GetPlatformPathFromURL(
75 url_,
76 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
77 weak_factory_.GetWeakPtr()));
78 return;
79 }
80
81 // Start extracting the metadata immediately if the request is anonymous.
82 // Otherwise, wait for user credentials to be retrieved first.
83 if (!allow_credentials_) {
84 ExtractMediaMetadata(url_.spec());
85 return;
86 }
87
88 resource_getter->GetCookies(url_,
89 first_party_for_cookies_,
90 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
91 weak_factory_.GetWeakPtr()));
92 }
93
CreateJavaMediaPlayerBridge()94 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
95 JNIEnv* env = base::android::AttachCurrentThread();
96 CHECK(env);
97
98 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
99 env, reinterpret_cast<intptr_t>(this)));
100
101 if (volume_ >= 0)
102 SetVolume(volume_);
103
104 AttachListener(j_media_player_bridge_.obj());
105 }
106
SetJavaMediaPlayerBridge(jobject j_media_player_bridge)107 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
108 jobject j_media_player_bridge) {
109 JNIEnv* env = base::android::AttachCurrentThread();
110 CHECK(env);
111
112 j_media_player_bridge_.Reset(env, j_media_player_bridge);
113 }
114
115 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
GetJavaMediaPlayerBridge()116 GetJavaMediaPlayerBridge() {
117 base::android::ScopedJavaLocalRef<jobject> j_bridge(
118 j_media_player_bridge_);
119 return j_bridge;
120 }
121
SetDuration(base::TimeDelta duration)122 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
123 duration_ = duration;
124 }
125
SetVideoSurface(gfx::ScopedJavaSurface surface)126 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
127 if (j_media_player_bridge_.is_null()) {
128 if (surface.IsEmpty())
129 return;
130 Prepare();
131 }
132
133 JNIEnv* env = base::android::AttachCurrentThread();
134 CHECK(env);
135 Java_MediaPlayerBridge_setSurface(
136 env, j_media_player_bridge_.obj(), surface.j_surface().obj());
137 }
138
Prepare()139 void MediaPlayerBridge::Prepare() {
140 DCHECK(j_media_player_bridge_.is_null());
141 CreateJavaMediaPlayerBridge();
142 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
143 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
144 url_,
145 base::Bind(&MediaPlayerBridge::SetDataSource,
146 weak_factory_.GetWeakPtr()));
147 return;
148 }
149
150 SetDataSource(url_.spec());
151 }
152
SetDataSource(const std::string & url)153 void MediaPlayerBridge::SetDataSource(const std::string& url) {
154 if (j_media_player_bridge_.is_null())
155 return;
156
157 JNIEnv* env = base::android::AttachCurrentThread();
158 CHECK(env);
159
160 int fd;
161 int64 offset;
162 int64 size;
163 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
164 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
165 env, j_media_player_bridge_.obj(), fd, offset, size)) {
166 OnMediaError(MEDIA_ERROR_FORMAT);
167 return;
168 }
169 } else {
170 // Create a Java String for the URL.
171 ScopedJavaLocalRef<jstring> j_url_string =
172 ConvertUTF8ToJavaString(env, url);
173
174 jobject j_context = base::android::GetApplicationContext();
175 DCHECK(j_context);
176
177 const std::string data_uri_prefix("data:");
178 if (StartsWithASCII(url, data_uri_prefix, true)) {
179 if (!Java_MediaPlayerBridge_setDataUriDataSource(
180 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
181 OnMediaError(MEDIA_ERROR_FORMAT);
182 }
183 return;
184 }
185
186 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
187 env, cookies_);
188 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
189 env, user_agent_);
190
191 if (!Java_MediaPlayerBridge_setDataSource(
192 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
193 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
194 OnMediaError(MEDIA_ERROR_FORMAT);
195 return;
196 }
197 }
198
199 request_media_resources_cb_.Run(player_id());
200 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
201 OnMediaError(MEDIA_ERROR_FORMAT);
202 }
203
InterceptMediaUrl(const std::string & url,int * fd,int64 * offset,int64 * size)204 bool MediaPlayerBridge::InterceptMediaUrl(
205 const std::string& url, int* fd, int64* offset, int64* size) {
206 // Sentinel value to check whether the output arguments have been set.
207 const int kUnsetValue = -1;
208
209 *fd = kUnsetValue;
210 *offset = kUnsetValue;
211 *size = kUnsetValue;
212 media::MediaUrlInterceptor* url_interceptor =
213 manager()->GetMediaUrlInterceptor();
214 if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
215 DCHECK_NE(kUnsetValue, *fd);
216 DCHECK_NE(kUnsetValue, *offset);
217 DCHECK_NE(kUnsetValue, *size);
218 return true;
219 }
220 return false;
221 }
222
OnDidSetDataUriDataSource(JNIEnv * env,jobject obj,jboolean success)223 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
224 jboolean success) {
225 if (!success) {
226 OnMediaError(MEDIA_ERROR_FORMAT);
227 return;
228 }
229
230 request_media_resources_cb_.Run(player_id());
231 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
232 OnMediaError(MEDIA_ERROR_FORMAT);
233 }
234
OnCookiesRetrieved(const std::string & cookies)235 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
236 cookies_ = cookies;
237 manager()->GetMediaResourceGetter()->GetAuthCredentials(
238 url_,
239 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
240 weak_factory_.GetWeakPtr()));
241 }
242
OnAuthCredentialsRetrieved(const base::string16 & username,const base::string16 & password)243 void MediaPlayerBridge::OnAuthCredentialsRetrieved(
244 const base::string16& username, const base::string16& password) {
245 GURL::ReplacementsW replacements;
246 if (!username.empty()) {
247 replacements.SetUsernameStr(username);
248 if (!password.empty())
249 replacements.SetPasswordStr(password);
250 url_ = url_.ReplaceComponents(replacements);
251 }
252 ExtractMediaMetadata(url_.spec());
253 }
254
ExtractMediaMetadata(const std::string & url)255 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
256 if (url.empty()) {
257 OnMediaError(MEDIA_ERROR_FORMAT);
258 return;
259 }
260
261 int fd;
262 int64 offset;
263 int64 size;
264 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
265 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
266 fd, offset, size,
267 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
268 weak_factory_.GetWeakPtr()));
269 } else {
270 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
271 url, cookies_, user_agent_,
272 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
273 weak_factory_.GetWeakPtr()));
274 }
275 }
276
OnMediaMetadataExtracted(base::TimeDelta duration,int width,int height,bool success)277 void MediaPlayerBridge::OnMediaMetadataExtracted(
278 base::TimeDelta duration, int width, int height, bool success) {
279 if (success) {
280 duration_ = duration;
281 width_ = width;
282 height_ = height;
283 }
284 manager()->OnMediaMetadataChanged(
285 player_id(), duration_, width_, height_, success);
286 }
287
Start()288 void MediaPlayerBridge::Start() {
289 if (j_media_player_bridge_.is_null()) {
290 pending_play_ = true;
291 Prepare();
292 } else {
293 if (prepared_)
294 StartInternal();
295 else
296 pending_play_ = true;
297 }
298 }
299
Pause(bool is_media_related_action)300 void MediaPlayerBridge::Pause(bool is_media_related_action) {
301 if (j_media_player_bridge_.is_null()) {
302 pending_play_ = false;
303 } else {
304 if (prepared_ && IsPlaying())
305 PauseInternal();
306 else
307 pending_play_ = false;
308 }
309 }
310
IsPlaying()311 bool MediaPlayerBridge::IsPlaying() {
312 if (!prepared_)
313 return pending_play_;
314
315 JNIEnv* env = base::android::AttachCurrentThread();
316 CHECK(env);
317 jboolean result = Java_MediaPlayerBridge_isPlaying(
318 env, j_media_player_bridge_.obj());
319 return result;
320 }
321
GetVideoWidth()322 int MediaPlayerBridge::GetVideoWidth() {
323 if (!prepared_)
324 return width_;
325 JNIEnv* env = base::android::AttachCurrentThread();
326 return Java_MediaPlayerBridge_getVideoWidth(
327 env, j_media_player_bridge_.obj());
328 }
329
GetVideoHeight()330 int MediaPlayerBridge::GetVideoHeight() {
331 if (!prepared_)
332 return height_;
333 JNIEnv* env = base::android::AttachCurrentThread();
334 return Java_MediaPlayerBridge_getVideoHeight(
335 env, j_media_player_bridge_.obj());
336 }
337
SeekTo(base::TimeDelta timestamp)338 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
339 // Record the time to seek when OnMediaPrepared() is called.
340 pending_seek_ = timestamp;
341 should_seek_on_prepare_ = true;
342
343 if (j_media_player_bridge_.is_null())
344 Prepare();
345 else if (prepared_)
346 SeekInternal(timestamp);
347 }
348
GetCurrentTime()349 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
350 if (!prepared_)
351 return pending_seek_;
352 JNIEnv* env = base::android::AttachCurrentThread();
353 return base::TimeDelta::FromMilliseconds(
354 Java_MediaPlayerBridge_getCurrentPosition(
355 env, j_media_player_bridge_.obj()));
356 }
357
GetDuration()358 base::TimeDelta MediaPlayerBridge::GetDuration() {
359 if (!prepared_)
360 return duration_;
361 JNIEnv* env = base::android::AttachCurrentThread();
362 return base::TimeDelta::FromMilliseconds(
363 Java_MediaPlayerBridge_getDuration(
364 env, j_media_player_bridge_.obj()));
365 }
366
Release()367 void MediaPlayerBridge::Release() {
368 if (j_media_player_bridge_.is_null())
369 return;
370
371 time_update_timer_.Stop();
372 if (prepared_) {
373 pending_seek_ = GetCurrentTime();
374 should_seek_on_prepare_ = true;
375 }
376
377 prepared_ = false;
378 pending_play_ = false;
379 SetVideoSurface(gfx::ScopedJavaSurface());
380 JNIEnv* env = base::android::AttachCurrentThread();
381 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
382 j_media_player_bridge_.Reset();
383 DetachListener();
384 }
385
SetVolume(double volume)386 void MediaPlayerBridge::SetVolume(double volume) {
387 if (j_media_player_bridge_.is_null()) {
388 volume_ = volume;
389 return;
390 }
391
392 JNIEnv* env = base::android::AttachCurrentThread();
393 CHECK(env);
394 Java_MediaPlayerBridge_setVolume(
395 env, j_media_player_bridge_.obj(), volume);
396 }
397
OnVideoSizeChanged(int width,int height)398 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
399 width_ = width;
400 height_ = height;
401 MediaPlayerAndroid::OnVideoSizeChanged(width, height);
402 }
403
OnPlaybackComplete()404 void MediaPlayerBridge::OnPlaybackComplete() {
405 time_update_timer_.Stop();
406 MediaPlayerAndroid::OnPlaybackComplete();
407 }
408
OnMediaInterrupted()409 void MediaPlayerBridge::OnMediaInterrupted() {
410 time_update_timer_.Stop();
411 MediaPlayerAndroid::OnMediaInterrupted();
412 }
413
OnMediaPrepared()414 void MediaPlayerBridge::OnMediaPrepared() {
415 if (j_media_player_bridge_.is_null())
416 return;
417
418 prepared_ = true;
419 duration_ = GetDuration();
420
421 // If media player was recovered from a saved state, consume all the pending
422 // events.
423 if (should_seek_on_prepare_) {
424 PendingSeekInternal(pending_seek_);
425 pending_seek_ = base::TimeDelta::FromMilliseconds(0);
426 should_seek_on_prepare_ = false;
427 }
428
429 if (pending_play_) {
430 StartInternal();
431 pending_play_ = false;
432 }
433
434 UpdateAllowedOperations();
435 manager()->OnMediaMetadataChanged(
436 player_id(), duration_, width_, height_, true);
437 }
438
GetAllowedOperations()439 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
440 JNIEnv* env = base::android::AttachCurrentThread();
441 CHECK(env);
442
443 return Java_MediaPlayerBridge_getAllowedOperations(
444 env, j_media_player_bridge_.obj());
445 }
446
UpdateAllowedOperations()447 void MediaPlayerBridge::UpdateAllowedOperations() {
448 JNIEnv* env = base::android::AttachCurrentThread();
449 CHECK(env);
450
451 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
452
453 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
454 can_seek_forward_ = Java_AllowedOperations_canSeekForward(
455 env, allowedOperations.obj());
456 can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
457 env, allowedOperations.obj());
458 }
459
StartInternal()460 void MediaPlayerBridge::StartInternal() {
461 JNIEnv* env = base::android::AttachCurrentThread();
462 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
463 if (!time_update_timer_.IsRunning()) {
464 time_update_timer_.Start(
465 FROM_HERE,
466 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
467 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
468 }
469 }
470
PauseInternal()471 void MediaPlayerBridge::PauseInternal() {
472 JNIEnv* env = base::android::AttachCurrentThread();
473 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
474 time_update_timer_.Stop();
475 }
476
PendingSeekInternal(const base::TimeDelta & time)477 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
478 SeekInternal(time);
479 }
480
SeekInternal(base::TimeDelta time)481 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
482 if (time > duration_)
483 time = duration_;
484
485 // Seeking to an invalid position may cause media player to stuck in an
486 // error state.
487 if (time < base::TimeDelta()) {
488 DCHECK_EQ(-1.0, time.InMillisecondsF());
489 return;
490 }
491
492 JNIEnv* env = base::android::AttachCurrentThread();
493 CHECK(env);
494 int time_msec = static_cast<int>(time.InMilliseconds());
495 Java_MediaPlayerBridge_seekTo(
496 env, j_media_player_bridge_.obj(), time_msec);
497 }
498
OnTimeUpdateTimerFired()499 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
500 manager()->OnTimeUpdate(
501 player_id(), GetCurrentTime(), base::TimeTicks::Now());
502 }
503
RegisterMediaPlayerBridge(JNIEnv * env)504 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
505 bool ret = RegisterNativesImpl(env);
506 DCHECK(g_MediaPlayerBridge_clazz);
507 return ret;
508 }
509
CanPause()510 bool MediaPlayerBridge::CanPause() {
511 return can_pause_;
512 }
513
CanSeekForward()514 bool MediaPlayerBridge::CanSeekForward() {
515 return can_seek_forward_;
516 }
517
CanSeekBackward()518 bool MediaPlayerBridge::CanSeekBackward() {
519 return can_seek_backward_;
520 }
521
IsPlayerReady()522 bool MediaPlayerBridge::IsPlayerReady() {
523 return prepared_;
524 }
525
GetUrl()526 GURL MediaPlayerBridge::GetUrl() {
527 return url_;
528 }
529
GetFirstPartyForCookies()530 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
531 return first_party_for_cookies_;
532 }
533
534 } // namespace media
535