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_player_manager.h"
15 #include "media/base/android/media_resource_getter.h"
16 #include "media/base/android/media_url_interceptor.h"
18 using base::android::ConvertUTF8ToJavaString;
19 using base::android::ScopedJavaLocalRef;
21 // Time update happens every 250ms.
22 const int kTimeUpdateInterval = 250;
25 const char kBlobScheme[] = "blob";
29 MediaPlayerBridge::MediaPlayerBridge(
32 const GURL& first_party_for_cookies,
33 const std::string& user_agent,
35 MediaPlayerManager* manager,
36 const RequestMediaResourcesCB& request_media_resources_cb,
37 const ReleaseMediaResourcesCB& release_media_resources_cb,
38 const GURL& frame_url,
39 bool allow_credentials)
40 : MediaPlayerAndroid(player_id,
42 request_media_resources_cb,
43 release_media_resources_cb,
48 first_party_for_cookies_(first_party_for_cookies),
49 user_agent_(user_agent),
50 hide_url_log_(hide_url_log),
54 can_seek_forward_(true),
55 can_seek_backward_(true),
56 is_surface_in_use_(false),
58 allow_credentials_(allow_credentials),
60 listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
61 weak_factory_.GetWeakPtr()));
64 MediaPlayerBridge::~MediaPlayerBridge() {
65 if (!j_media_player_bridge_.is_null()) {
66 JNIEnv* env = base::android::AttachCurrentThread();
68 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
73 void MediaPlayerBridge::Initialize() {
75 if (url_.SchemeIsFile() || url_.SchemeIs("app")) {
76 ExtractMediaMetadata(url_.spec());
80 media::MediaResourceGetter* resource_getter =
81 manager()->GetMediaResourceGetter();
82 if (url_.SchemeIsFileSystem() || url_.SchemeIs(kBlobScheme)) {
83 resource_getter->GetPlatformPathFromURL(
85 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
86 weak_factory_.GetWeakPtr()));
90 // Start extracting the metadata immediately if the request is anonymous.
91 // Otherwise, wait for user credentials to be retrieved first.
92 if (!allow_credentials_) {
93 ExtractMediaMetadata(url_.spec());
97 resource_getter->GetCookies(url_,
98 first_party_for_cookies_,
99 base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
100 weak_factory_.GetWeakPtr()));
103 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
104 JNIEnv* env = base::android::AttachCurrentThread();
107 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
108 env, reinterpret_cast<intptr_t>(this)));
113 SetMediaPlayerListener();
116 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
117 jobject j_media_player_bridge) {
118 JNIEnv* env = base::android::AttachCurrentThread();
121 j_media_player_bridge_.Reset(env, j_media_player_bridge);
124 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
125 GetJavaMediaPlayerBridge() {
126 base::android::ScopedJavaLocalRef<jobject> j_bridge(
127 j_media_player_bridge_);
131 void MediaPlayerBridge::SetMediaPlayerListener() {
132 jobject j_context = base::android::GetApplicationContext();
135 listener_->CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
138 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
139 duration_ = duration;
142 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
143 if (j_media_player_bridge_.is_null()) {
144 if (surface.IsEmpty())
149 JNIEnv* env = base::android::AttachCurrentThread();
151 is_surface_in_use_ = true;
152 Java_MediaPlayerBridge_setSurface(
153 env, j_media_player_bridge_.obj(), surface.j_surface().obj());
156 void MediaPlayerBridge::Prepare() {
157 DCHECK(j_media_player_bridge_.is_null());
158 CreateJavaMediaPlayerBridge();
159 if (url_.SchemeIsFileSystem() || url_.SchemeIs(kBlobScheme)) {
160 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
162 base::Bind(&MediaPlayerBridge::SetDataSource,
163 weak_factory_.GetWeakPtr()));
167 SetDataSource(url_.spec());
170 void MediaPlayerBridge::SetDataSource(const std::string& url) {
171 if (j_media_player_bridge_.is_null())
174 JNIEnv* env = base::android::AttachCurrentThread();
180 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
181 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
182 env, j_media_player_bridge_.obj(), fd, offset, size)) {
183 OnMediaError(MEDIA_ERROR_FORMAT);
187 // Create a Java String for the URL.
188 ScopedJavaLocalRef<jstring> j_url_string =
189 ConvertUTF8ToJavaString(env, url);
191 jobject j_context = base::android::GetApplicationContext();
194 const std::string data_uri_prefix("data:");
195 if (StartsWithASCII(url, data_uri_prefix, true)) {
196 if (!Java_MediaPlayerBridge_setDataUriDataSource(
197 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
198 OnMediaError(MEDIA_ERROR_FORMAT);
203 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
205 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
208 if (!Java_MediaPlayerBridge_setDataSource(
209 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
210 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
211 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 bool MediaPlayerBridge::InterceptMediaUrl(
222 const std::string& url, int* fd, int64* offset, int64* size) {
223 // Sentinel value to check whether the output arguments have been set.
224 const int kUnsetValue = -1;
227 *offset = kUnsetValue;
229 media::MediaUrlInterceptor* url_interceptor =
230 manager()->GetMediaUrlInterceptor();
231 if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
232 DCHECK_NE(kUnsetValue, *fd);
233 DCHECK_NE(kUnsetValue, *offset);
234 DCHECK_NE(kUnsetValue, *size);
240 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
243 OnMediaError(MEDIA_ERROR_FORMAT);
247 request_media_resources_cb_.Run(player_id());
248 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
249 OnMediaError(MEDIA_ERROR_FORMAT);
252 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
254 ExtractMediaMetadata(url_.spec());
257 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
261 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
262 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
264 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
265 weak_factory_.GetWeakPtr()));
267 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
268 url, cookies_, user_agent_,
269 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
270 weak_factory_.GetWeakPtr()));
274 void MediaPlayerBridge::OnMediaMetadataExtracted(
275 base::TimeDelta duration, int width, int height, bool success) {
277 duration_ = duration;
281 manager()->OnMediaMetadataChanged(
282 player_id(), duration_, width_, height_, success);
285 void MediaPlayerBridge::Start() {
286 if (j_media_player_bridge_.is_null()) {
287 pending_play_ = true;
293 pending_play_ = true;
297 void MediaPlayerBridge::Pause(bool is_media_related_action) {
298 if (j_media_player_bridge_.is_null()) {
299 pending_play_ = false;
301 if (prepared_ && IsPlaying())
304 pending_play_ = false;
308 bool MediaPlayerBridge::IsPlaying() {
310 return pending_play_;
312 JNIEnv* env = base::android::AttachCurrentThread();
314 jboolean result = Java_MediaPlayerBridge_isPlaying(
315 env, j_media_player_bridge_.obj());
319 int MediaPlayerBridge::GetVideoWidth() {
322 JNIEnv* env = base::android::AttachCurrentThread();
323 return Java_MediaPlayerBridge_getVideoWidth(
324 env, j_media_player_bridge_.obj());
327 int MediaPlayerBridge::GetVideoHeight() {
330 JNIEnv* env = base::android::AttachCurrentThread();
331 return Java_MediaPlayerBridge_getVideoHeight(
332 env, j_media_player_bridge_.obj());
335 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
336 // Record the time to seek when OnMediaPrepared() is called.
337 pending_seek_ = timestamp;
339 if (j_media_player_bridge_.is_null())
342 SeekInternal(timestamp);
345 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
347 return pending_seek_;
348 JNIEnv* env = base::android::AttachCurrentThread();
349 return base::TimeDelta::FromMilliseconds(
350 Java_MediaPlayerBridge_getCurrentPosition(
351 env, j_media_player_bridge_.obj()));
354 base::TimeDelta MediaPlayerBridge::GetDuration() {
357 JNIEnv* env = base::android::AttachCurrentThread();
358 return base::TimeDelta::FromMilliseconds(
359 Java_MediaPlayerBridge_getDuration(
360 env, j_media_player_bridge_.obj()));
363 void MediaPlayerBridge::Release() {
364 if (j_media_player_bridge_.is_null())
367 time_update_timer_.Stop();
369 pending_seek_ = GetCurrentTime();
371 pending_play_ = false;
372 is_surface_in_use_ = false;
373 SetVideoSurface(gfx::ScopedJavaSurface());
374 JNIEnv* env = base::android::AttachCurrentThread();
375 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
376 j_media_player_bridge_.Reset();
377 release_media_resources_cb_.Run(player_id());
378 listener_->ReleaseMediaPlayerListenerResources();
381 void MediaPlayerBridge::SetVolume(double volume) {
382 if (j_media_player_bridge_.is_null()) {
387 JNIEnv* env = base::android::AttachCurrentThread();
389 Java_MediaPlayerBridge_setVolume(
390 env, j_media_player_bridge_.obj(), volume);
393 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
396 manager()->OnVideoSizeChanged(player_id(), width, height);
399 void MediaPlayerBridge::OnMediaError(int error_type) {
400 manager()->OnError(player_id(), error_type);
403 void MediaPlayerBridge::OnBufferingUpdate(int percent) {
404 manager()->OnBufferingUpdate(player_id(), percent);
407 void MediaPlayerBridge::OnPlaybackComplete() {
408 time_update_timer_.Stop();
409 manager()->OnPlaybackComplete(player_id());
412 void MediaPlayerBridge::OnMediaInterrupted() {
413 time_update_timer_.Stop();
414 manager()->OnMediaInterrupted(player_id());
417 void MediaPlayerBridge::OnSeekComplete() {
418 manager()->OnSeekComplete(player_id(), GetCurrentTime());
421 void MediaPlayerBridge::OnMediaPrepared() {
422 if (j_media_player_bridge_.is_null())
426 duration_ = GetDuration();
428 // If media player was recovered from a saved state, consume all the pending
430 PendingSeekInternal(pending_seek_);
434 pending_play_ = false;
437 UpdateAllowedOperations();
438 manager()->OnMediaMetadataChanged(
439 player_id(), duration_, width_, height_, true);
442 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
443 JNIEnv* env = base::android::AttachCurrentThread();
446 return Java_MediaPlayerBridge_getAllowedOperations(
447 env, j_media_player_bridge_.obj());
450 void MediaPlayerBridge::UpdateAllowedOperations() {
451 JNIEnv* env = base::android::AttachCurrentThread();
454 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
456 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
457 can_seek_forward_ = Java_AllowedOperations_canSeekForward(
458 env, allowedOperations.obj());
459 can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
460 env, allowedOperations.obj());
463 void MediaPlayerBridge::StartInternal() {
464 JNIEnv* env = base::android::AttachCurrentThread();
465 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
466 if (!time_update_timer_.IsRunning()) {
467 time_update_timer_.Start(
469 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
470 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
474 void MediaPlayerBridge::PauseInternal() {
475 JNIEnv* env = base::android::AttachCurrentThread();
476 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
477 time_update_timer_.Stop();
480 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
484 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
485 if (time > duration_)
488 // Seeking to an invalid position may cause media player to stuck in an
490 if (time < base::TimeDelta()) {
491 DCHECK_EQ(-1.0, time.InMillisecondsF());
495 JNIEnv* env = base::android::AttachCurrentThread();
497 int time_msec = static_cast<int>(time.InMilliseconds());
498 Java_MediaPlayerBridge_seekTo(
499 env, j_media_player_bridge_.obj(), time_msec);
502 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
503 manager()->OnTimeUpdate(player_id(), GetCurrentTime());
506 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
507 bool ret = RegisterNativesImpl(env);
508 DCHECK(g_MediaPlayerBridge_clazz);
512 bool MediaPlayerBridge::CanPause() {
516 bool MediaPlayerBridge::CanSeekForward() {
517 return can_seek_forward_;
520 bool MediaPlayerBridge::CanSeekBackward() {
521 return can_seek_backward_;
524 bool MediaPlayerBridge::IsPlayerReady() {
528 GURL MediaPlayerBridge::GetUrl() {
532 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
533 return first_party_for_cookies_;
536 bool MediaPlayerBridge::IsSurfaceInUse() const {
537 return is_surface_in_use_;