Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / media / base / android / media_player_bridge.cc
blobed8854f1b688774a4f00eecca7f4790d88a368b7
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;
23 namespace media {
25 MediaPlayerBridge::MediaPlayerBridge(
26 int player_id,
27 const GURL& url,
28 const GURL& first_party_for_cookies,
29 const std::string& user_agent,
30 bool hide_url_log,
31 MediaPlayerManager* manager,
32 const RequestMediaResourcesCB& request_media_resources_cb,
33 const GURL& frame_url,
34 bool allow_credentials)
35 : MediaPlayerAndroid(player_id,
36 manager,
37 request_media_resources_cb,
38 frame_url),
39 prepared_(false),
40 pending_play_(false),
41 should_seek_on_prepare_(false),
42 url_(url),
43 first_party_for_cookies_(first_party_for_cookies),
44 user_agent_(user_agent),
45 hide_url_log_(hide_url_log),
46 width_(0),
47 height_(0),
48 can_pause_(true),
49 can_seek_forward_(true),
50 can_seek_backward_(true),
51 volume_(-1.0),
52 allow_credentials_(allow_credentials),
53 weak_factory_(this) {
56 MediaPlayerBridge::~MediaPlayerBridge() {
57 if (!j_media_player_bridge_.is_null()) {
58 JNIEnv* env = base::android::AttachCurrentThread();
59 CHECK(env);
60 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
62 Release();
65 void MediaPlayerBridge::Initialize() {
66 cookies_.clear();
67 if (url_.SchemeIsFile() || url_.SchemeIs("data")) {
68 ExtractMediaMetadata(url_.spec());
69 return;
72 media::MediaResourceGetter* resource_getter =
73 manager()->GetMediaResourceGetter();
74 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
75 resource_getter->GetPlatformPathFromURL(
76 url_,
77 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
78 weak_factory_.GetWeakPtr()));
79 return;
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());
86 return;
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();
97 CHECK(env);
99 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
100 env, reinterpret_cast<intptr_t>(this)));
102 if (volume_ >= 0)
103 SetVolume(volume_);
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())
115 return;
116 Prepare();
119 JNIEnv* env = base::android::AttachCurrentThread();
120 CHECK(env);
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(
130 url_,
131 base::Bind(&MediaPlayerBridge::SetDataSource,
132 weak_factory_.GetWeakPtr()));
133 return;
136 SetDataSource(url_.spec());
139 void MediaPlayerBridge::SetDataSource(const std::string& url) {
140 if (j_media_player_bridge_.is_null())
141 return;
143 JNIEnv* env = base::android::AttachCurrentThread();
144 CHECK(env);
146 int fd;
147 int64 offset;
148 int64 size;
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);
153 return;
155 } else {
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();
161 DCHECK(j_context);
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);
169 return;
172 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
173 env, cookies_);
174 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
175 env, user_agent_);
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);
181 return;
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;
195 *fd = kUnsetValue;
196 *offset = kUnsetValue;
197 *size = 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);
204 return true;
206 return false;
209 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
210 jboolean success) {
211 if (!success) {
212 OnMediaError(MEDIA_ERROR_FORMAT);
213 return;
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) {
222 cookies_ = cookies;
223 manager()->GetMediaResourceGetter()->GetAuthCredentials(
224 url_,
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) {
242 if (url.empty()) {
243 OnMediaError(MEDIA_ERROR_FORMAT);
244 return;
247 int fd;
248 int64 offset;
249 int64 size;
250 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
251 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
252 fd, offset, size,
253 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
254 weak_factory_.GetWeakPtr()));
255 } else {
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) {
265 if (success) {
266 duration_ = duration;
267 width_ = width;
268 height_ = height;
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;
277 Prepare();
278 } else {
279 if (prepared_)
280 StartInternal();
281 else
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;
289 } else {
290 if (prepared_ && IsPlaying())
291 PauseInternal();
292 else
293 pending_play_ = false;
297 bool MediaPlayerBridge::IsPlaying() {
298 if (!prepared_)
299 return pending_play_;
301 JNIEnv* env = base::android::AttachCurrentThread();
302 CHECK(env);
303 jboolean result = Java_MediaPlayerBridge_isPlaying(
304 env, j_media_player_bridge_.obj());
305 return result;
308 int MediaPlayerBridge::GetVideoWidth() {
309 if (!prepared_)
310 return width_;
311 JNIEnv* env = base::android::AttachCurrentThread();
312 return Java_MediaPlayerBridge_getVideoWidth(
313 env, j_media_player_bridge_.obj());
316 int MediaPlayerBridge::GetVideoHeight() {
317 if (!prepared_)
318 return height_;
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())
330 Prepare();
331 else if (prepared_)
332 SeekInternal(timestamp);
335 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
336 if (!prepared_)
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() {
345 if (!prepared_)
346 return duration_;
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())
356 return;
358 SetAudible(false);
360 time_update_timer_.Stop();
361 if (prepared_) {
362 pending_seek_ = GetCurrentTime();
363 should_seek_on_prepare_ = true;
366 prepared_ = false;
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();
372 DetachListener();
375 void MediaPlayerBridge::SetVolume(double volume) {
376 volume_ = volume;
378 if (j_media_player_bridge_.is_null())
379 return;
381 JNIEnv* env = base::android::AttachCurrentThread();
382 CHECK(env);
384 // Update the audible state if we are playing.
385 jboolean is_playing = Java_MediaPlayerBridge_isPlaying(
386 env, j_media_player_bridge_.obj());
387 if (is_playing)
388 SetAudible(volume_ > 0);
390 Java_MediaPlayerBridge_setVolume(
391 env, j_media_player_bridge_.obj(), volume_);
394 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
395 width_ = width;
396 height_ = height;
397 MediaPlayerAndroid::OnVideoSizeChanged(width, height);
400 void MediaPlayerBridge::OnPlaybackComplete() {
401 SetAudible(false);
402 time_update_timer_.Stop();
403 MediaPlayerAndroid::OnPlaybackComplete();
406 void MediaPlayerBridge::OnMediaInterrupted() {
407 SetAudible(false);
408 time_update_timer_.Stop();
409 MediaPlayerAndroid::OnMediaInterrupted();
412 void MediaPlayerBridge::OnMediaPrepared() {
413 if (j_media_player_bridge_.is_null())
414 return;
416 prepared_ = true;
417 duration_ = GetDuration();
419 // If media player was recovered from a saved state, consume all the pending
420 // events.
421 if (should_seek_on_prepare_) {
422 PendingSeekInternal(pending_seek_);
423 pending_seek_ = base::TimeDelta::FromMilliseconds(0);
424 should_seek_on_prepare_ = false;
427 if (pending_play_) {
428 StartInternal();
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();
439 CHECK(env);
441 return Java_MediaPlayerBridge_getAllowedOperations(
442 env, j_media_player_bridge_.obj());
445 void MediaPlayerBridge::UpdateAllowedOperations() {
446 JNIEnv* env = base::android::AttachCurrentThread();
447 CHECK(env);
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(
463 FROM_HERE,
464 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
465 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
468 SetAudible(volume_ > 0);
471 void MediaPlayerBridge::PauseInternal() {
472 SetAudible(false);
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) {
480 SeekInternal(time);
483 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
484 if (time > duration_)
485 time = duration_;
487 // Seeking to an invalid position may cause media player to stuck in an
488 // error state.
489 if (time < base::TimeDelta()) {
490 DCHECK_EQ(-1.0, time.InMillisecondsF());
491 return;
494 JNIEnv* env = base::android::AttachCurrentThread();
495 CHECK(env);
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() {
511 return can_pause_;
514 bool MediaPlayerBridge::CanSeekForward() {
515 return can_seek_forward_;
518 bool MediaPlayerBridge::CanSeekBackward() {
519 return can_seek_backward_;
522 bool MediaPlayerBridge::IsPlayerReady() {
523 return prepared_;
526 GURL MediaPlayerBridge::GetUrl() {
527 return url_;
530 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
531 return first_party_for_cookies_;
534 } // namespace media