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.
5 #include "media/base/android/media_player_bridge.h"
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/strings/string_util.h"
12 #include "jni/MediaPlayerBridge_jni.h"
13 #include "media/base/android/media_common_android.h"
14 #include "media/base/android/media_player_manager.h"
15 #include "media/base/android/media_resource_getter.h"
16 #include "media/base/android/media_url_interceptor.h"
17 #include "media/base/timestamp_constants.h"
19 using base::android::ConvertUTF8ToJavaString
;
20 using base::android::ScopedJavaLocalRef
;
24 MediaPlayerBridge::MediaPlayerBridge(
27 const GURL
& first_party_for_cookies
,
28 const std::string
& user_agent
,
30 MediaPlayerManager
* manager
,
31 const RequestMediaResourcesCB
& request_media_resources_cb
,
32 const GURL
& frame_url
,
33 bool allow_credentials
)
34 : MediaPlayerAndroid(player_id
,
36 request_media_resources_cb
,
40 should_seek_on_prepare_(false),
42 first_party_for_cookies_(first_party_for_cookies
),
43 user_agent_(user_agent
),
44 hide_url_log_(hide_url_log
),
48 can_seek_forward_(true),
49 can_seek_backward_(true),
51 allow_credentials_(allow_credentials
),
55 MediaPlayerBridge::~MediaPlayerBridge() {
56 if (!j_media_player_bridge_
.is_null()) {
57 JNIEnv
* env
= base::android::AttachCurrentThread();
59 Java_MediaPlayerBridge_destroy(env
, j_media_player_bridge_
.obj());
64 void MediaPlayerBridge::Initialize() {
66 if (url_
.SchemeIsFile() || url_
.SchemeIs("data")) {
67 ExtractMediaMetadata(url_
.spec());
71 media::MediaResourceGetter
* resource_getter
=
72 manager()->GetMediaResourceGetter();
73 if (url_
.SchemeIsFileSystem() || url_
.SchemeIsBlob()) {
74 resource_getter
->GetPlatformPathFromURL(
76 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata
,
77 weak_factory_
.GetWeakPtr()));
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());
88 resource_getter
->GetCookies(url_
,
89 first_party_for_cookies_
,
90 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved
,
91 weak_factory_
.GetWeakPtr()));
94 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
95 JNIEnv
* env
= base::android::AttachCurrentThread();
98 j_media_player_bridge_
.Reset(Java_MediaPlayerBridge_create(
99 env
, reinterpret_cast<intptr_t>(this)));
104 AttachListener(j_media_player_bridge_
.obj());
107 void MediaPlayerBridge::SetDuration(base::TimeDelta duration
) {
108 duration_
= duration
;
111 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface
) {
112 if (j_media_player_bridge_
.is_null()) {
113 if (surface
.IsEmpty())
118 JNIEnv
* env
= base::android::AttachCurrentThread();
120 Java_MediaPlayerBridge_setSurface(
121 env
, j_media_player_bridge_
.obj(), surface
.j_surface().obj());
124 void MediaPlayerBridge::Prepare() {
125 DCHECK(j_media_player_bridge_
.is_null());
126 CreateJavaMediaPlayerBridge();
127 if (url_
.SchemeIsFileSystem() || url_
.SchemeIsBlob()) {
128 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
130 base::Bind(&MediaPlayerBridge::SetDataSource
,
131 weak_factory_
.GetWeakPtr()));
135 SetDataSource(url_
.spec());
138 void MediaPlayerBridge::SetDataSource(const std::string
& url
) {
139 if (j_media_player_bridge_
.is_null())
142 JNIEnv
* env
= base::android::AttachCurrentThread();
148 if (InterceptMediaUrl(url
, &fd
, &offset
, &size
)) {
149 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
150 env
, j_media_player_bridge_
.obj(), fd
, offset
, size
)) {
151 OnMediaError(MEDIA_ERROR_FORMAT
);
155 // Create a Java String for the URL.
156 ScopedJavaLocalRef
<jstring
> j_url_string
=
157 ConvertUTF8ToJavaString(env
, url
);
159 jobject j_context
= base::android::GetApplicationContext();
162 const std::string
data_uri_prefix("data:");
163 if (base::StartsWith(url
, data_uri_prefix
, base::CompareCase::SENSITIVE
)) {
164 if (!Java_MediaPlayerBridge_setDataUriDataSource(
165 env
, j_media_player_bridge_
.obj(), j_context
, j_url_string
.obj())) {
166 OnMediaError(MEDIA_ERROR_FORMAT
);
171 ScopedJavaLocalRef
<jstring
> j_cookies
= ConvertUTF8ToJavaString(
173 ScopedJavaLocalRef
<jstring
> j_user_agent
= ConvertUTF8ToJavaString(
176 if (!Java_MediaPlayerBridge_setDataSource(
177 env
, j_media_player_bridge_
.obj(), j_context
, j_url_string
.obj(),
178 j_cookies
.obj(), j_user_agent
.obj(), hide_url_log_
)) {
179 OnMediaError(MEDIA_ERROR_FORMAT
);
184 request_media_resources_cb_
.Run(player_id());
185 if (!Java_MediaPlayerBridge_prepareAsync(env
, j_media_player_bridge_
.obj()))
186 OnMediaError(MEDIA_ERROR_FORMAT
);
189 bool MediaPlayerBridge::InterceptMediaUrl(
190 const std::string
& url
, int* fd
, int64
* offset
, int64
* size
) {
191 // Sentinel value to check whether the output arguments have been set.
192 const int kUnsetValue
= -1;
195 *offset
= kUnsetValue
;
197 media::MediaUrlInterceptor
* url_interceptor
=
198 manager()->GetMediaUrlInterceptor();
199 if (url_interceptor
&& url_interceptor
->Intercept(url
, fd
, offset
, size
)) {
200 DCHECK_NE(kUnsetValue
, *fd
);
201 DCHECK_NE(kUnsetValue
, *offset
);
202 DCHECK_NE(kUnsetValue
, *size
);
208 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv
* env
, jobject obj
,
211 OnMediaError(MEDIA_ERROR_FORMAT
);
215 request_media_resources_cb_
.Run(player_id());
216 if (!Java_MediaPlayerBridge_prepareAsync(env
, j_media_player_bridge_
.obj()))
217 OnMediaError(MEDIA_ERROR_FORMAT
);
220 void MediaPlayerBridge::OnCookiesRetrieved(const std::string
& cookies
) {
222 manager()->GetMediaResourceGetter()->GetAuthCredentials(
224 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved
,
225 weak_factory_
.GetWeakPtr()));
228 void MediaPlayerBridge::OnAuthCredentialsRetrieved(
229 const base::string16
& username
, const base::string16
& password
) {
230 GURL::ReplacementsW replacements
;
231 if (!username
.empty()) {
232 replacements
.SetUsernameStr(username
);
233 if (!password
.empty())
234 replacements
.SetPasswordStr(password
);
235 url_
= url_
.ReplaceComponents(replacements
);
237 ExtractMediaMetadata(url_
.spec());
240 void MediaPlayerBridge::ExtractMediaMetadata(const std::string
& url
) {
242 OnMediaError(MEDIA_ERROR_FORMAT
);
249 if (InterceptMediaUrl(url
, &fd
, &offset
, &size
)) {
250 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
252 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted
,
253 weak_factory_
.GetWeakPtr()));
255 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
256 url
, cookies_
, user_agent_
,
257 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted
,
258 weak_factory_
.GetWeakPtr()));
262 void MediaPlayerBridge::OnMediaMetadataExtracted(
263 base::TimeDelta duration
, int width
, int height
, bool success
) {
265 duration_
= duration
;
269 manager()->OnMediaMetadataChanged(
270 player_id(), duration_
, width_
, height_
, success
);
273 void MediaPlayerBridge::Start() {
274 if (j_media_player_bridge_
.is_null()) {
275 pending_play_
= true;
281 pending_play_
= true;
285 void MediaPlayerBridge::Pause(bool is_media_related_action
) {
286 if (j_media_player_bridge_
.is_null()) {
287 pending_play_
= false;
289 if (prepared_
&& IsPlaying())
292 pending_play_
= false;
296 bool MediaPlayerBridge::IsPlaying() {
298 return pending_play_
;
300 JNIEnv
* env
= base::android::AttachCurrentThread();
302 jboolean result
= Java_MediaPlayerBridge_isPlaying(
303 env
, j_media_player_bridge_
.obj());
307 int MediaPlayerBridge::GetVideoWidth() {
310 JNIEnv
* env
= base::android::AttachCurrentThread();
311 return Java_MediaPlayerBridge_getVideoWidth(
312 env
, j_media_player_bridge_
.obj());
315 int MediaPlayerBridge::GetVideoHeight() {
318 JNIEnv
* env
= base::android::AttachCurrentThread();
319 return Java_MediaPlayerBridge_getVideoHeight(
320 env
, j_media_player_bridge_
.obj());
323 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp
) {
324 // Record the time to seek when OnMediaPrepared() is called.
325 pending_seek_
= timestamp
;
326 should_seek_on_prepare_
= true;
328 if (j_media_player_bridge_
.is_null())
331 SeekInternal(GetCurrentTime(), timestamp
);
334 base::TimeDelta
MediaPlayerBridge::GetCurrentTime() {
336 return pending_seek_
;
337 JNIEnv
* env
= base::android::AttachCurrentThread();
338 return base::TimeDelta::FromMilliseconds(
339 Java_MediaPlayerBridge_getCurrentPosition(
340 env
, j_media_player_bridge_
.obj()));
343 base::TimeDelta
MediaPlayerBridge::GetDuration() {
346 JNIEnv
* env
= base::android::AttachCurrentThread();
347 const int duration_ms
=
348 Java_MediaPlayerBridge_getDuration(env
, j_media_player_bridge_
.obj());
349 return duration_ms
< 0 ? media::kInfiniteDuration()
350 : base::TimeDelta::FromMilliseconds(duration_ms
);
353 void MediaPlayerBridge::Release() {
354 if (j_media_player_bridge_
.is_null())
357 time_update_timer_
.Stop();
359 pending_seek_
= GetCurrentTime();
360 should_seek_on_prepare_
= true;
364 pending_play_
= false;
365 SetVideoSurface(gfx::ScopedJavaSurface());
366 JNIEnv
* env
= base::android::AttachCurrentThread();
367 Java_MediaPlayerBridge_release(env
, j_media_player_bridge_
.obj());
368 j_media_player_bridge_
.Reset();
372 void MediaPlayerBridge::SetVolume(double volume
) {
373 if (j_media_player_bridge_
.is_null()) {
378 JNIEnv
* env
= base::android::AttachCurrentThread();
381 Java_MediaPlayerBridge_setVolume(
382 env
, j_media_player_bridge_
.obj(), volume
);
385 void MediaPlayerBridge::OnVideoSizeChanged(int width
, int height
) {
388 MediaPlayerAndroid::OnVideoSizeChanged(width
, height
);
391 void MediaPlayerBridge::OnPlaybackComplete() {
392 time_update_timer_
.Stop();
393 MediaPlayerAndroid::OnPlaybackComplete();
396 void MediaPlayerBridge::OnMediaInterrupted() {
397 time_update_timer_
.Stop();
398 MediaPlayerAndroid::OnMediaInterrupted();
401 void MediaPlayerBridge::OnMediaPrepared() {
402 if (j_media_player_bridge_
.is_null())
406 duration_
= GetDuration();
408 // If media player was recovered from a saved state, consume all the pending
410 if (should_seek_on_prepare_
) {
411 PendingSeekInternal(pending_seek_
);
412 pending_seek_
= base::TimeDelta::FromMilliseconds(0);
413 should_seek_on_prepare_
= false;
418 pending_play_
= false;
421 UpdateAllowedOperations();
422 manager()->OnMediaMetadataChanged(
423 player_id(), duration_
, width_
, height_
, true);
426 ScopedJavaLocalRef
<jobject
> MediaPlayerBridge::GetAllowedOperations() {
427 JNIEnv
* env
= base::android::AttachCurrentThread();
430 return Java_MediaPlayerBridge_getAllowedOperations(
431 env
, j_media_player_bridge_
.obj());
434 void MediaPlayerBridge::UpdateAllowedOperations() {
435 JNIEnv
* env
= base::android::AttachCurrentThread();
438 ScopedJavaLocalRef
<jobject
> allowedOperations
= GetAllowedOperations();
440 can_pause_
= Java_AllowedOperations_canPause(env
, allowedOperations
.obj());
441 can_seek_forward_
= Java_AllowedOperations_canSeekForward(
442 env
, allowedOperations
.obj());
443 can_seek_backward_
= Java_AllowedOperations_canSeekBackward(
444 env
, allowedOperations
.obj());
447 void MediaPlayerBridge::StartInternal() {
448 if (!manager()->RequestPlay(player_id())) {
453 JNIEnv
* env
= base::android::AttachCurrentThread();
454 Java_MediaPlayerBridge_start(env
, j_media_player_bridge_
.obj());
455 if (!time_update_timer_
.IsRunning()) {
456 time_update_timer_
.Start(
458 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval
),
459 this, &MediaPlayerBridge::OnTimeUpdateTimerFired
);
463 void MediaPlayerBridge::PauseInternal() {
464 JNIEnv
* env
= base::android::AttachCurrentThread();
465 Java_MediaPlayerBridge_pause(env
, j_media_player_bridge_
.obj());
466 time_update_timer_
.Stop();
469 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta
& time
) {
470 SeekInternal(GetCurrentTime(), time
);
473 bool MediaPlayerBridge::SeekInternal(base::TimeDelta current_time
,
474 base::TimeDelta time
) {
475 // Seeking on content like live streams may cause the media player to
476 // get stuck in an error state.
477 if (time
< current_time
&& !CanSeekBackward())
480 if (time
>= current_time
&& !CanSeekForward())
483 if (time
> duration_
)
486 // Seeking to an invalid position may cause media player to stuck in an
488 if (time
< base::TimeDelta()) {
489 DCHECK_EQ(-1.0, time
.InMillisecondsF());
493 JNIEnv
* env
= base::android::AttachCurrentThread();
495 int time_msec
= static_cast<int>(time
.InMilliseconds());
496 Java_MediaPlayerBridge_seekTo(
497 env
, j_media_player_bridge_
.obj(), time_msec
);
501 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
502 manager()->OnTimeUpdate(
503 player_id(), GetCurrentTime(), base::TimeTicks::Now());
506 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv
* env
) {
507 return RegisterNativesImpl(env
);
510 bool MediaPlayerBridge::CanPause() {
514 bool MediaPlayerBridge::CanSeekForward() {
515 return can_seek_forward_
;
518 bool MediaPlayerBridge::CanSeekBackward() {
519 return can_seek_backward_
;
522 bool MediaPlayerBridge::IsPlayerReady() {
526 GURL
MediaPlayerBridge::GetUrl() {
530 GURL
MediaPlayerBridge::GetFirstPartyForCookies() {
531 return first_party_for_cookies_
;