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/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 #include "media/base/buffers.h"
20 using base::android::ConvertUTF8ToJavaString
;
21 using base::android::ScopedJavaLocalRef
;
25 MediaPlayerBridge::MediaPlayerBridge(
28 const GURL
& first_party_for_cookies
,
29 const std::string
& user_agent
,
31 MediaPlayerManager
* manager
,
32 const RequestMediaResourcesCB
& request_media_resources_cb
,
33 const GURL
& frame_url
,
34 bool allow_credentials
)
35 : MediaPlayerAndroid(player_id
,
37 request_media_resources_cb
,
41 should_seek_on_prepare_(false),
43 first_party_for_cookies_(first_party_for_cookies
),
44 user_agent_(user_agent
),
45 hide_url_log_(hide_url_log
),
49 can_seek_forward_(true),
50 can_seek_backward_(true),
52 allow_credentials_(allow_credentials
),
56 MediaPlayerBridge::~MediaPlayerBridge() {
57 if (!j_media_player_bridge_
.is_null()) {
58 JNIEnv
* env
= base::android::AttachCurrentThread();
60 Java_MediaPlayerBridge_destroy(env
, j_media_player_bridge_
.obj());
65 void MediaPlayerBridge::Initialize() {
67 if (url_
.SchemeIsFile() || url_
.SchemeIs("data")) {
68 ExtractMediaMetadata(url_
.spec());
72 media::MediaResourceGetter
* resource_getter
=
73 manager()->GetMediaResourceGetter();
74 if (url_
.SchemeIsFileSystem() || url_
.SchemeIsBlob()) {
75 resource_getter
->GetPlatformPathFromURL(
77 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata
,
78 weak_factory_
.GetWeakPtr()));
82 // Start extracting the metadata immediately if the request is anonymous.
83 // Otherwise, wait for user credentials to be retrieved first.
84 if (!allow_credentials_
) {
85 ExtractMediaMetadata(url_
.spec());
89 resource_getter
->GetCookies(url_
,
90 first_party_for_cookies_
,
91 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved
,
92 weak_factory_
.GetWeakPtr()));
95 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
96 JNIEnv
* env
= base::android::AttachCurrentThread();
99 j_media_player_bridge_
.Reset(Java_MediaPlayerBridge_create(
100 env
, reinterpret_cast<intptr_t>(this)));
105 AttachListener(j_media_player_bridge_
.obj());
108 void MediaPlayerBridge::SetDuration(base::TimeDelta duration
) {
109 duration_
= duration
;
112 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface
) {
113 if (j_media_player_bridge_
.is_null()) {
114 if (surface
.IsEmpty())
119 JNIEnv
* env
= base::android::AttachCurrentThread();
121 Java_MediaPlayerBridge_setSurface(
122 env
, j_media_player_bridge_
.obj(), surface
.j_surface().obj());
125 void MediaPlayerBridge::Prepare() {
126 DCHECK(j_media_player_bridge_
.is_null());
127 CreateJavaMediaPlayerBridge();
128 if (url_
.SchemeIsFileSystem() || url_
.SchemeIsBlob()) {
129 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
131 base::Bind(&MediaPlayerBridge::SetDataSource
,
132 weak_factory_
.GetWeakPtr()));
136 SetDataSource(url_
.spec());
139 void MediaPlayerBridge::SetDataSource(const std::string
& url
) {
140 if (j_media_player_bridge_
.is_null())
143 JNIEnv
* env
= base::android::AttachCurrentThread();
149 if (InterceptMediaUrl(url
, &fd
, &offset
, &size
)) {
150 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
151 env
, j_media_player_bridge_
.obj(), fd
, offset
, size
)) {
152 OnMediaError(MEDIA_ERROR_FORMAT
);
156 // Create a Java String for the URL.
157 ScopedJavaLocalRef
<jstring
> j_url_string
=
158 ConvertUTF8ToJavaString(env
, url
);
160 jobject j_context
= base::android::GetApplicationContext();
163 const std::string
data_uri_prefix("data:");
164 if (StartsWithASCII(url
, data_uri_prefix
, true)) {
165 if (!Java_MediaPlayerBridge_setDataUriDataSource(
166 env
, j_media_player_bridge_
.obj(), j_context
, j_url_string
.obj())) {
167 OnMediaError(MEDIA_ERROR_FORMAT
);
172 ScopedJavaLocalRef
<jstring
> j_cookies
= ConvertUTF8ToJavaString(
174 ScopedJavaLocalRef
<jstring
> j_user_agent
= ConvertUTF8ToJavaString(
177 if (!Java_MediaPlayerBridge_setDataSource(
178 env
, j_media_player_bridge_
.obj(), j_context
, j_url_string
.obj(),
179 j_cookies
.obj(), j_user_agent
.obj(), hide_url_log_
)) {
180 OnMediaError(MEDIA_ERROR_FORMAT
);
185 request_media_resources_cb_
.Run(player_id());
186 if (!Java_MediaPlayerBridge_prepareAsync(env
, j_media_player_bridge_
.obj()))
187 OnMediaError(MEDIA_ERROR_FORMAT
);
190 bool MediaPlayerBridge::InterceptMediaUrl(
191 const std::string
& url
, int* fd
, int64
* offset
, int64
* size
) {
192 // Sentinel value to check whether the output arguments have been set.
193 const int kUnsetValue
= -1;
196 *offset
= kUnsetValue
;
198 media::MediaUrlInterceptor
* url_interceptor
=
199 manager()->GetMediaUrlInterceptor();
200 if (url_interceptor
&& url_interceptor
->Intercept(url
, fd
, offset
, size
)) {
201 DCHECK_NE(kUnsetValue
, *fd
);
202 DCHECK_NE(kUnsetValue
, *offset
);
203 DCHECK_NE(kUnsetValue
, *size
);
209 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv
* env
, jobject obj
,
212 OnMediaError(MEDIA_ERROR_FORMAT
);
216 request_media_resources_cb_
.Run(player_id());
217 if (!Java_MediaPlayerBridge_prepareAsync(env
, j_media_player_bridge_
.obj()))
218 OnMediaError(MEDIA_ERROR_FORMAT
);
221 void MediaPlayerBridge::OnCookiesRetrieved(const std::string
& cookies
) {
223 manager()->GetMediaResourceGetter()->GetAuthCredentials(
225 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved
,
226 weak_factory_
.GetWeakPtr()));
229 void MediaPlayerBridge::OnAuthCredentialsRetrieved(
230 const base::string16
& username
, const base::string16
& password
) {
231 GURL::ReplacementsW replacements
;
232 if (!username
.empty()) {
233 replacements
.SetUsernameStr(username
);
234 if (!password
.empty())
235 replacements
.SetPasswordStr(password
);
236 url_
= url_
.ReplaceComponents(replacements
);
238 ExtractMediaMetadata(url_
.spec());
241 void MediaPlayerBridge::ExtractMediaMetadata(const std::string
& url
) {
243 OnMediaError(MEDIA_ERROR_FORMAT
);
250 if (InterceptMediaUrl(url
, &fd
, &offset
, &size
)) {
251 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
253 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted
,
254 weak_factory_
.GetWeakPtr()));
256 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
257 url
, cookies_
, user_agent_
,
258 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted
,
259 weak_factory_
.GetWeakPtr()));
263 void MediaPlayerBridge::OnMediaMetadataExtracted(
264 base::TimeDelta duration
, int width
, int height
, bool success
) {
266 duration_
= duration
;
270 manager()->OnMediaMetadataChanged(
271 player_id(), duration_
, width_
, height_
, success
);
274 void MediaPlayerBridge::Start() {
275 if (j_media_player_bridge_
.is_null()) {
276 pending_play_
= true;
282 pending_play_
= true;
286 void MediaPlayerBridge::Pause(bool is_media_related_action
) {
287 if (j_media_player_bridge_
.is_null()) {
288 pending_play_
= false;
290 if (prepared_
&& IsPlaying())
293 pending_play_
= false;
297 bool MediaPlayerBridge::IsPlaying() {
299 return pending_play_
;
301 JNIEnv
* env
= base::android::AttachCurrentThread();
303 jboolean result
= Java_MediaPlayerBridge_isPlaying(
304 env
, j_media_player_bridge_
.obj());
308 int MediaPlayerBridge::GetVideoWidth() {
311 JNIEnv
* env
= base::android::AttachCurrentThread();
312 return Java_MediaPlayerBridge_getVideoWidth(
313 env
, j_media_player_bridge_
.obj());
316 int MediaPlayerBridge::GetVideoHeight() {
319 JNIEnv
* env
= base::android::AttachCurrentThread();
320 return Java_MediaPlayerBridge_getVideoHeight(
321 env
, j_media_player_bridge_
.obj());
324 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp
) {
325 // Record the time to seek when OnMediaPrepared() is called.
326 pending_seek_
= timestamp
;
327 should_seek_on_prepare_
= true;
329 if (j_media_player_bridge_
.is_null())
332 SeekInternal(timestamp
);
335 base::TimeDelta
MediaPlayerBridge::GetCurrentTime() {
337 return pending_seek_
;
338 JNIEnv
* env
= base::android::AttachCurrentThread();
339 return base::TimeDelta::FromMilliseconds(
340 Java_MediaPlayerBridge_getCurrentPosition(
341 env
, j_media_player_bridge_
.obj()));
344 base::TimeDelta
MediaPlayerBridge::GetDuration() {
347 JNIEnv
* env
= base::android::AttachCurrentThread();
348 const int duration_ms
=
349 Java_MediaPlayerBridge_getDuration(env
, j_media_player_bridge_
.obj());
350 return duration_ms
< 0 ? media::kInfiniteDuration()
351 : base::TimeDelta::FromMilliseconds(duration_ms
);
354 void MediaPlayerBridge::Release() {
355 if (j_media_player_bridge_
.is_null())
360 time_update_timer_
.Stop();
362 pending_seek_
= GetCurrentTime();
363 should_seek_on_prepare_
= true;
367 pending_play_
= false;
368 SetVideoSurface(gfx::ScopedJavaSurface());
369 JNIEnv
* env
= base::android::AttachCurrentThread();
370 Java_MediaPlayerBridge_release(env
, j_media_player_bridge_
.obj());
371 j_media_player_bridge_
.Reset();
375 void MediaPlayerBridge::SetVolume(double volume
) {
378 if (j_media_player_bridge_
.is_null())
381 JNIEnv
* env
= base::android::AttachCurrentThread();
384 // Update the audible state if we are playing.
385 jboolean is_playing
= Java_MediaPlayerBridge_isPlaying(
386 env
, j_media_player_bridge_
.obj());
388 SetAudible(volume_
> 0);
390 Java_MediaPlayerBridge_setVolume(
391 env
, j_media_player_bridge_
.obj(), volume_
);
394 void MediaPlayerBridge::OnVideoSizeChanged(int width
, int height
) {
397 MediaPlayerAndroid::OnVideoSizeChanged(width
, height
);
400 void MediaPlayerBridge::OnPlaybackComplete() {
402 time_update_timer_
.Stop();
403 MediaPlayerAndroid::OnPlaybackComplete();
406 void MediaPlayerBridge::OnMediaInterrupted() {
408 time_update_timer_
.Stop();
409 MediaPlayerAndroid::OnMediaInterrupted();
412 void MediaPlayerBridge::OnMediaPrepared() {
413 if (j_media_player_bridge_
.is_null())
417 duration_
= GetDuration();
419 // If media player was recovered from a saved state, consume all the pending
421 if (should_seek_on_prepare_
) {
422 PendingSeekInternal(pending_seek_
);
423 pending_seek_
= base::TimeDelta::FromMilliseconds(0);
424 should_seek_on_prepare_
= false;
429 pending_play_
= false;
432 UpdateAllowedOperations();
433 manager()->OnMediaMetadataChanged(
434 player_id(), duration_
, width_
, height_
, true);
437 ScopedJavaLocalRef
<jobject
> MediaPlayerBridge::GetAllowedOperations() {
438 JNIEnv
* env
= base::android::AttachCurrentThread();
441 return Java_MediaPlayerBridge_getAllowedOperations(
442 env
, j_media_player_bridge_
.obj());
445 void MediaPlayerBridge::UpdateAllowedOperations() {
446 JNIEnv
* env
= base::android::AttachCurrentThread();
449 ScopedJavaLocalRef
<jobject
> allowedOperations
= GetAllowedOperations();
451 can_pause_
= Java_AllowedOperations_canPause(env
, allowedOperations
.obj());
452 can_seek_forward_
= Java_AllowedOperations_canSeekForward(
453 env
, allowedOperations
.obj());
454 can_seek_backward_
= Java_AllowedOperations_canSeekBackward(
455 env
, allowedOperations
.obj());
458 void MediaPlayerBridge::StartInternal() {
459 JNIEnv
* env
= base::android::AttachCurrentThread();
460 Java_MediaPlayerBridge_start(env
, j_media_player_bridge_
.obj());
461 if (!time_update_timer_
.IsRunning()) {
462 time_update_timer_
.Start(
464 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval
),
465 this, &MediaPlayerBridge::OnTimeUpdateTimerFired
);
468 SetAudible(volume_
> 0);
471 void MediaPlayerBridge::PauseInternal() {
474 JNIEnv
* env
= base::android::AttachCurrentThread();
475 Java_MediaPlayerBridge_pause(env
, j_media_player_bridge_
.obj());
476 time_update_timer_
.Stop();
479 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta
& time
) {
483 void MediaPlayerBridge::SeekInternal(base::TimeDelta time
) {
484 if (time
> duration_
)
487 // Seeking to an invalid position may cause media player to stuck in an
489 if (time
< base::TimeDelta()) {
490 DCHECK_EQ(-1.0, time
.InMillisecondsF());
494 JNIEnv
* env
= base::android::AttachCurrentThread();
496 int time_msec
= static_cast<int>(time
.InMilliseconds());
497 Java_MediaPlayerBridge_seekTo(
498 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_
;