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"
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("app")) {
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::SetJavaMediaPlayerBridge(
108 jobject j_media_player_bridge) {
109 JNIEnv* env = base::android::AttachCurrentThread();
112 j_media_player_bridge_.Reset(env, j_media_player_bridge);
115 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
116 GetJavaMediaPlayerBridge() {
117 base::android::ScopedJavaLocalRef<jobject> j_bridge(
118 j_media_player_bridge_);
122 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
123 duration_ = duration;
126 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
127 if (j_media_player_bridge_.is_null()) {
128 if (surface.IsEmpty())
133 JNIEnv* env = base::android::AttachCurrentThread();
135 Java_MediaPlayerBridge_setSurface(
136 env, j_media_player_bridge_.obj(), surface.j_surface().obj());
139 void MediaPlayerBridge::Prepare() {
140 DCHECK(j_media_player_bridge_.is_null());
141 CreateJavaMediaPlayerBridge();
142 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
143 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
145 base::Bind(&MediaPlayerBridge::SetDataSource,
146 weak_factory_.GetWeakPtr()));
150 SetDataSource(url_.spec());
153 void MediaPlayerBridge::SetDataSource(const std::string& url) {
154 if (j_media_player_bridge_.is_null())
157 JNIEnv* env = base::android::AttachCurrentThread();
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);
170 // Create a Java String for the URL.
171 ScopedJavaLocalRef<jstring> j_url_string =
172 ConvertUTF8ToJavaString(env, url);
174 jobject j_context = base::android::GetApplicationContext();
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);
186 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
188 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
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);
199 request_media_resources_cb_.Run(player_id());
200 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
201 OnMediaError(MEDIA_ERROR_FORMAT);
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;
210 *offset = 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);
223 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
226 OnMediaError(MEDIA_ERROR_FORMAT);
230 request_media_resources_cb_.Run(player_id());
231 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
232 OnMediaError(MEDIA_ERROR_FORMAT);
235 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
237 manager()->GetMediaResourceGetter()->GetAuthCredentials(
239 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
240 weak_factory_.GetWeakPtr()));
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);
252 ExtractMediaMetadata(url_.spec());
255 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
257 OnMediaError(MEDIA_ERROR_FORMAT);
264 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
265 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
267 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
268 weak_factory_.GetWeakPtr()));
270 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
271 url, cookies_, user_agent_,
272 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
273 weak_factory_.GetWeakPtr()));
277 void MediaPlayerBridge::OnMediaMetadataExtracted(
278 base::TimeDelta duration, int width, int height, bool success) {
280 duration_ = duration;
284 manager()->OnMediaMetadataChanged(
285 player_id(), duration_, width_, height_, success);
288 void MediaPlayerBridge::Start() {
289 if (j_media_player_bridge_.is_null()) {
290 pending_play_ = true;
296 pending_play_ = true;
300 void MediaPlayerBridge::Pause(bool is_media_related_action) {
301 if (j_media_player_bridge_.is_null()) {
302 pending_play_ = false;
304 if (prepared_ && IsPlaying())
307 pending_play_ = false;
311 bool MediaPlayerBridge::IsPlaying() {
313 return pending_play_;
315 JNIEnv* env = base::android::AttachCurrentThread();
317 jboolean result = Java_MediaPlayerBridge_isPlaying(
318 env, j_media_player_bridge_.obj());
322 int MediaPlayerBridge::GetVideoWidth() {
325 JNIEnv* env = base::android::AttachCurrentThread();
326 return Java_MediaPlayerBridge_getVideoWidth(
327 env, j_media_player_bridge_.obj());
330 int MediaPlayerBridge::GetVideoHeight() {
333 JNIEnv* env = base::android::AttachCurrentThread();
334 return Java_MediaPlayerBridge_getVideoHeight(
335 env, j_media_player_bridge_.obj());
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;
343 if (j_media_player_bridge_.is_null())
346 SeekInternal(timestamp);
349 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
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()));
358 base::TimeDelta MediaPlayerBridge::GetDuration() {
361 JNIEnv* env = base::android::AttachCurrentThread();
362 return base::TimeDelta::FromMilliseconds(
363 Java_MediaPlayerBridge_getDuration(
364 env, j_media_player_bridge_.obj()));
367 void MediaPlayerBridge::Release() {
368 if (j_media_player_bridge_.is_null())
371 time_update_timer_.Stop();
373 pending_seek_ = GetCurrentTime();
374 should_seek_on_prepare_ = true;
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();
386 void MediaPlayerBridge::SetVolume(double volume) {
387 if (j_media_player_bridge_.is_null()) {
392 JNIEnv* env = base::android::AttachCurrentThread();
394 Java_MediaPlayerBridge_setVolume(
395 env, j_media_player_bridge_.obj(), volume);
398 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
401 MediaPlayerAndroid::OnVideoSizeChanged(width, height);
404 void MediaPlayerBridge::OnPlaybackComplete() {
405 time_update_timer_.Stop();
406 MediaPlayerAndroid::OnPlaybackComplete();
409 void MediaPlayerBridge::OnMediaInterrupted() {
410 time_update_timer_.Stop();
411 MediaPlayerAndroid::OnMediaInterrupted();
414 void MediaPlayerBridge::OnMediaPrepared() {
415 if (j_media_player_bridge_.is_null())
419 duration_ = GetDuration();
421 // If media player was recovered from a saved state, consume all the pending
423 if (should_seek_on_prepare_) {
424 PendingSeekInternal(pending_seek_);
425 pending_seek_ = base::TimeDelta::FromMilliseconds(0);
426 should_seek_on_prepare_ = false;
431 pending_play_ = false;
434 UpdateAllowedOperations();
435 manager()->OnMediaMetadataChanged(
436 player_id(), duration_, width_, height_, true);
439 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
440 JNIEnv* env = base::android::AttachCurrentThread();
443 return Java_MediaPlayerBridge_getAllowedOperations(
444 env, j_media_player_bridge_.obj());
447 void MediaPlayerBridge::UpdateAllowedOperations() {
448 JNIEnv* env = base::android::AttachCurrentThread();
451 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
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());
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(
466 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
467 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
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();
477 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
481 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
482 if (time > duration_)
485 // Seeking to an invalid position may cause media player to stuck in an
487 if (time < base::TimeDelta()) {
488 DCHECK_EQ(-1.0, time.InMillisecondsF());
492 JNIEnv* env = base::android::AttachCurrentThread();
494 int time_msec = static_cast<int>(time.InMilliseconds());
495 Java_MediaPlayerBridge_seekTo(
496 env, j_media_player_bridge_.obj(), time_msec);
499 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
500 manager()->OnTimeUpdate(
501 player_id(), GetCurrentTime(), base::TimeTicks::Now());
504 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
505 bool ret = RegisterNativesImpl(env);
506 DCHECK(g_MediaPlayerBridge_clazz);
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_;