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,
41 first_party_for_cookies_(first_party_for_cookies),
42 user_agent_(user_agent),
43 hide_url_log_(hide_url_log),
47 can_seek_forward_(true),
48 can_seek_backward_(true),
49 is_surface_in_use_(false),
51 allow_credentials_(allow_credentials),
53 listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
54 weak_factory_.GetWeakPtr()));
57 MediaPlayerBridge::~MediaPlayerBridge() {
58 if (!j_media_player_bridge_.is_null()) {
59 JNIEnv* env = base::android::AttachCurrentThread();
61 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
66 void MediaPlayerBridge::Initialize() {
68 if (url_.SchemeIsFile() || url_.SchemeIs("app")) {
69 ExtractMediaMetadata(url_.spec());
73 media::MediaResourceGetter* resource_getter =
74 manager()->GetMediaResourceGetter();
75 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
76 resource_getter->GetPlatformPathFromURL(
78 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
79 weak_factory_.GetWeakPtr()));
83 // Start extracting the metadata immediately if the request is anonymous.
84 // Otherwise, wait for user credentials to be retrieved first.
85 if (!allow_credentials_) {
86 ExtractMediaMetadata(url_.spec());
90 resource_getter->GetCookies(url_,
91 first_party_for_cookies_,
92 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
93 weak_factory_.GetWeakPtr()));
96 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
97 JNIEnv* env = base::android::AttachCurrentThread();
100 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
101 env, reinterpret_cast<intptr_t>(this)));
106 SetMediaPlayerListener();
109 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
110 jobject j_media_player_bridge) {
111 JNIEnv* env = base::android::AttachCurrentThread();
114 j_media_player_bridge_.Reset(env, j_media_player_bridge);
117 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
118 GetJavaMediaPlayerBridge() {
119 base::android::ScopedJavaLocalRef<jobject> j_bridge(
120 j_media_player_bridge_);
124 void MediaPlayerBridge::SetMediaPlayerListener() {
125 jobject j_context = base::android::GetApplicationContext();
128 listener_->CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
131 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
132 duration_ = duration;
135 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
136 if (j_media_player_bridge_.is_null()) {
137 if (surface.IsEmpty())
142 JNIEnv* env = base::android::AttachCurrentThread();
144 is_surface_in_use_ = true;
145 Java_MediaPlayerBridge_setSurface(
146 env, j_media_player_bridge_.obj(), surface.j_surface().obj());
149 void MediaPlayerBridge::Prepare() {
150 DCHECK(j_media_player_bridge_.is_null());
151 CreateJavaMediaPlayerBridge();
152 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
153 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
155 base::Bind(&MediaPlayerBridge::SetDataSource,
156 weak_factory_.GetWeakPtr()));
160 SetDataSource(url_.spec());
163 void MediaPlayerBridge::SetDataSource(const std::string& url) {
164 if (j_media_player_bridge_.is_null())
167 JNIEnv* env = base::android::AttachCurrentThread();
173 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
174 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
175 env, j_media_player_bridge_.obj(), fd, offset, size)) {
176 OnMediaError(MEDIA_ERROR_FORMAT);
180 // Create a Java String for the URL.
181 ScopedJavaLocalRef<jstring> j_url_string =
182 ConvertUTF8ToJavaString(env, url);
184 jobject j_context = base::android::GetApplicationContext();
187 const std::string data_uri_prefix("data:");
188 if (StartsWithASCII(url, data_uri_prefix, true)) {
189 if (!Java_MediaPlayerBridge_setDataUriDataSource(
190 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
191 OnMediaError(MEDIA_ERROR_FORMAT);
196 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
198 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
201 if (!Java_MediaPlayerBridge_setDataSource(
202 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
203 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
204 OnMediaError(MEDIA_ERROR_FORMAT);
209 request_media_resources_cb_.Run(player_id());
210 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
211 OnMediaError(MEDIA_ERROR_FORMAT);
214 bool MediaPlayerBridge::InterceptMediaUrl(
215 const std::string& url, int* fd, int64* offset, int64* size) {
216 // Sentinel value to check whether the output arguments have been set.
217 const int kUnsetValue = -1;
220 *offset = kUnsetValue;
222 media::MediaUrlInterceptor* url_interceptor =
223 manager()->GetMediaUrlInterceptor();
224 if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
225 DCHECK_NE(kUnsetValue, *fd);
226 DCHECK_NE(kUnsetValue, *offset);
227 DCHECK_NE(kUnsetValue, *size);
233 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
236 OnMediaError(MEDIA_ERROR_FORMAT);
240 request_media_resources_cb_.Run(player_id());
241 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
242 OnMediaError(MEDIA_ERROR_FORMAT);
245 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
247 manager()->GetMediaResourceGetter()->GetAuthCredentials(
249 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
250 weak_factory_.GetWeakPtr()));
253 void MediaPlayerBridge::OnAuthCredentialsRetrieved(
254 const base::string16& username, const base::string16& password) {
255 GURL::ReplacementsW replacements;
256 if (!username.empty()) {
257 replacements.SetUsernameStr(username);
258 if (!password.empty())
259 replacements.SetPasswordStr(password);
260 url_ = url_.ReplaceComponents(replacements);
262 ExtractMediaMetadata(url_.spec());
265 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
267 OnMediaError(MEDIA_ERROR_FORMAT);
274 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
275 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
277 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
278 weak_factory_.GetWeakPtr()));
280 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
281 url, cookies_, user_agent_,
282 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
283 weak_factory_.GetWeakPtr()));
287 void MediaPlayerBridge::OnMediaMetadataExtracted(
288 base::TimeDelta duration, int width, int height, bool success) {
290 duration_ = duration;
294 manager()->OnMediaMetadataChanged(
295 player_id(), duration_, width_, height_, success);
298 void MediaPlayerBridge::Start() {
299 if (j_media_player_bridge_.is_null()) {
300 pending_play_ = true;
306 pending_play_ = true;
310 void MediaPlayerBridge::Pause(bool is_media_related_action) {
311 if (j_media_player_bridge_.is_null()) {
312 pending_play_ = false;
314 if (prepared_ && IsPlaying())
317 pending_play_ = false;
321 bool MediaPlayerBridge::IsPlaying() {
323 return pending_play_;
325 JNIEnv* env = base::android::AttachCurrentThread();
327 jboolean result = Java_MediaPlayerBridge_isPlaying(
328 env, j_media_player_bridge_.obj());
332 int MediaPlayerBridge::GetVideoWidth() {
335 JNIEnv* env = base::android::AttachCurrentThread();
336 return Java_MediaPlayerBridge_getVideoWidth(
337 env, j_media_player_bridge_.obj());
340 int MediaPlayerBridge::GetVideoHeight() {
343 JNIEnv* env = base::android::AttachCurrentThread();
344 return Java_MediaPlayerBridge_getVideoHeight(
345 env, j_media_player_bridge_.obj());
348 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
349 // Record the time to seek when OnMediaPrepared() is called.
350 pending_seek_ = timestamp;
352 if (j_media_player_bridge_.is_null())
355 SeekInternal(timestamp);
358 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
360 return pending_seek_;
361 JNIEnv* env = base::android::AttachCurrentThread();
362 return base::TimeDelta::FromMilliseconds(
363 Java_MediaPlayerBridge_getCurrentPosition(
364 env, j_media_player_bridge_.obj()));
367 base::TimeDelta MediaPlayerBridge::GetDuration() {
370 JNIEnv* env = base::android::AttachCurrentThread();
371 return base::TimeDelta::FromMilliseconds(
372 Java_MediaPlayerBridge_getDuration(
373 env, j_media_player_bridge_.obj()));
376 void MediaPlayerBridge::Release() {
377 if (j_media_player_bridge_.is_null())
380 time_update_timer_.Stop();
382 pending_seek_ = GetCurrentTime();
384 pending_play_ = false;
385 is_surface_in_use_ = false;
386 SetVideoSurface(gfx::ScopedJavaSurface());
387 JNIEnv* env = base::android::AttachCurrentThread();
388 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
389 j_media_player_bridge_.Reset();
390 listener_->ReleaseMediaPlayerListenerResources();
393 void MediaPlayerBridge::SetVolume(double volume) {
394 if (j_media_player_bridge_.is_null()) {
399 JNIEnv* env = base::android::AttachCurrentThread();
401 Java_MediaPlayerBridge_setVolume(
402 env, j_media_player_bridge_.obj(), volume);
405 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
408 manager()->OnVideoSizeChanged(player_id(), width, height);
411 void MediaPlayerBridge::OnMediaError(int error_type) {
412 manager()->OnError(player_id(), error_type);
415 void MediaPlayerBridge::OnBufferingUpdate(int percent) {
416 manager()->OnBufferingUpdate(player_id(), percent);
419 void MediaPlayerBridge::OnPlaybackComplete() {
420 time_update_timer_.Stop();
421 manager()->OnPlaybackComplete(player_id());
424 void MediaPlayerBridge::OnMediaInterrupted() {
425 time_update_timer_.Stop();
426 manager()->OnMediaInterrupted(player_id());
429 void MediaPlayerBridge::OnSeekComplete() {
430 manager()->OnSeekComplete(player_id(), GetCurrentTime());
433 void MediaPlayerBridge::OnMediaPrepared() {
434 if (j_media_player_bridge_.is_null())
438 duration_ = GetDuration();
440 // If media player was recovered from a saved state, consume all the pending
442 PendingSeekInternal(pending_seek_);
446 pending_play_ = false;
449 UpdateAllowedOperations();
450 manager()->OnMediaMetadataChanged(
451 player_id(), duration_, width_, height_, true);
454 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
455 JNIEnv* env = base::android::AttachCurrentThread();
458 return Java_MediaPlayerBridge_getAllowedOperations(
459 env, j_media_player_bridge_.obj());
462 void MediaPlayerBridge::UpdateAllowedOperations() {
463 JNIEnv* env = base::android::AttachCurrentThread();
466 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
468 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
469 can_seek_forward_ = Java_AllowedOperations_canSeekForward(
470 env, allowedOperations.obj());
471 can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
472 env, allowedOperations.obj());
475 void MediaPlayerBridge::StartInternal() {
476 JNIEnv* env = base::android::AttachCurrentThread();
477 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
478 if (!time_update_timer_.IsRunning()) {
479 time_update_timer_.Start(
481 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
482 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
486 void MediaPlayerBridge::PauseInternal() {
487 JNIEnv* env = base::android::AttachCurrentThread();
488 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
489 time_update_timer_.Stop();
492 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
496 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
497 if (time > duration_)
500 // Seeking to an invalid position may cause media player to stuck in an
502 if (time < base::TimeDelta()) {
503 DCHECK_EQ(-1.0, time.InMillisecondsF());
507 JNIEnv* env = base::android::AttachCurrentThread();
509 int time_msec = static_cast<int>(time.InMilliseconds());
510 Java_MediaPlayerBridge_seekTo(
511 env, j_media_player_bridge_.obj(), time_msec);
514 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
515 manager()->OnTimeUpdate(
516 player_id(), GetCurrentTime(), base::TimeTicks::Now());
519 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
520 bool ret = RegisterNativesImpl(env);
521 DCHECK(g_MediaPlayerBridge_clazz);
525 bool MediaPlayerBridge::CanPause() {
529 bool MediaPlayerBridge::CanSeekForward() {
530 return can_seek_forward_;
533 bool MediaPlayerBridge::CanSeekBackward() {
534 return can_seek_backward_;
537 bool MediaPlayerBridge::IsPlayerReady() {
541 GURL MediaPlayerBridge::GetUrl() {
545 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
546 return first_party_for_cookies_;
549 bool MediaPlayerBridge::IsSurfaceInUse() const {
550 return is_surface_in_use_;