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/buffers.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::StartsWithASCII(url
, data_uri_prefix
, true)) {
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(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())
359 time_update_timer_
.Stop();
361 pending_seek_
= GetCurrentTime();
362 should_seek_on_prepare_
= true;
366 pending_play_
= false;
367 SetVideoSurface(gfx::ScopedJavaSurface());
368 JNIEnv
* env
= base::android::AttachCurrentThread();
369 Java_MediaPlayerBridge_release(env
, j_media_player_bridge_
.obj());
370 j_media_player_bridge_
.Reset();
374 void MediaPlayerBridge::SetVolume(double volume
) {
377 if (j_media_player_bridge_
.is_null())
380 JNIEnv
* env
= base::android::AttachCurrentThread();
383 // Update the audible state if we are playing.
384 jboolean is_playing
= Java_MediaPlayerBridge_isPlaying(
385 env
, j_media_player_bridge_
.obj());
387 SetAudible(volume_
> 0);
389 Java_MediaPlayerBridge_setVolume(
390 env
, j_media_player_bridge_
.obj(), volume_
);
393 void MediaPlayerBridge::OnVideoSizeChanged(int width
, int height
) {
396 MediaPlayerAndroid::OnVideoSizeChanged(width
, height
);
399 void MediaPlayerBridge::OnPlaybackComplete() {
401 time_update_timer_
.Stop();
402 MediaPlayerAndroid::OnPlaybackComplete();
405 void MediaPlayerBridge::OnMediaInterrupted() {
407 time_update_timer_
.Stop();
408 MediaPlayerAndroid::OnMediaInterrupted();
411 void MediaPlayerBridge::OnMediaPrepared() {
412 if (j_media_player_bridge_
.is_null())
416 duration_
= GetDuration();
418 // If media player was recovered from a saved state, consume all the pending
420 if (should_seek_on_prepare_
) {
421 PendingSeekInternal(pending_seek_
);
422 pending_seek_
= base::TimeDelta::FromMilliseconds(0);
423 should_seek_on_prepare_
= false;
428 pending_play_
= false;
431 UpdateAllowedOperations();
432 manager()->OnMediaMetadataChanged(
433 player_id(), duration_
, width_
, height_
, true);
436 ScopedJavaLocalRef
<jobject
> MediaPlayerBridge::GetAllowedOperations() {
437 JNIEnv
* env
= base::android::AttachCurrentThread();
440 return Java_MediaPlayerBridge_getAllowedOperations(
441 env
, j_media_player_bridge_
.obj());
444 void MediaPlayerBridge::UpdateAllowedOperations() {
445 JNIEnv
* env
= base::android::AttachCurrentThread();
448 ScopedJavaLocalRef
<jobject
> allowedOperations
= GetAllowedOperations();
450 can_pause_
= Java_AllowedOperations_canPause(env
, allowedOperations
.obj());
451 can_seek_forward_
= Java_AllowedOperations_canSeekForward(
452 env
, allowedOperations
.obj());
453 can_seek_backward_
= Java_AllowedOperations_canSeekBackward(
454 env
, allowedOperations
.obj());
457 void MediaPlayerBridge::StartInternal() {
458 if (!manager()->RequestPlay(player_id())) {
463 JNIEnv
* env
= base::android::AttachCurrentThread();
464 Java_MediaPlayerBridge_start(env
, j_media_player_bridge_
.obj());
465 if (!time_update_timer_
.IsRunning()) {
466 time_update_timer_
.Start(
468 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval
),
469 this, &MediaPlayerBridge::OnTimeUpdateTimerFired
);
472 SetAudible(volume_
> 0);
475 void MediaPlayerBridge::PauseInternal() {
478 JNIEnv
* env
= base::android::AttachCurrentThread();
479 Java_MediaPlayerBridge_pause(env
, j_media_player_bridge_
.obj());
480 time_update_timer_
.Stop();
483 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta
& time
) {
487 void MediaPlayerBridge::SeekInternal(base::TimeDelta time
) {
488 if (time
> duration_
)
491 // Seeking to an invalid position may cause media player to stuck in an
493 if (time
< base::TimeDelta()) {
494 DCHECK_EQ(-1.0, time
.InMillisecondsF());
498 JNIEnv
* env
= base::android::AttachCurrentThread();
500 int time_msec
= static_cast<int>(time
.InMilliseconds());
501 Java_MediaPlayerBridge_seekTo(
502 env
, j_media_player_bridge_
.obj(), time_msec
);
505 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
506 manager()->OnTimeUpdate(
507 player_id(), GetCurrentTime(), base::TimeTicks::Now());
510 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv
* env
) {
511 return RegisterNativesImpl(env
);
514 bool MediaPlayerBridge::CanPause() {
518 bool MediaPlayerBridge::CanSeekForward() {
519 return can_seek_forward_
;
522 bool MediaPlayerBridge::CanSeekBackward() {
523 return can_seek_backward_
;
526 bool MediaPlayerBridge::IsPlayerReady() {
530 GURL
MediaPlayerBridge::GetUrl() {
534 GURL
MediaPlayerBridge::GetFirstPartyForCookies() {
535 return first_party_for_cookies_
;