Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / base / android / media_player_bridge.cc
blob62d589de3729fa2b90d6c60fc5b8efb57411c675
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/strings/string_util.h"
12 #include "jni/MediaPlayerBridge_jni.h"
13 #include "media/base/android/media_common_android.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"
17 #include "media/base/timestamp_constants.h"
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::ScopedJavaLocalRef;
22 namespace media {
24 MediaPlayerBridge::MediaPlayerBridge(
25 int player_id,
26 const GURL& url,
27 const GURL& first_party_for_cookies,
28 const std::string& user_agent,
29 bool hide_url_log,
30 MediaPlayerManager* manager,
31 const RequestMediaResourcesCB& request_media_resources_cb,
32 const GURL& frame_url,
33 bool allow_credentials)
34 : MediaPlayerAndroid(player_id,
35 manager,
36 request_media_resources_cb,
37 frame_url),
38 prepared_(false),
39 pending_play_(false),
40 should_seek_on_prepare_(false),
41 url_(url),
42 first_party_for_cookies_(first_party_for_cookies),
43 user_agent_(user_agent),
44 hide_url_log_(hide_url_log),
45 width_(0),
46 height_(0),
47 can_pause_(true),
48 can_seek_forward_(true),
49 can_seek_backward_(true),
50 volume_(-1.0),
51 allow_credentials_(allow_credentials),
52 weak_factory_(this) {
55 MediaPlayerBridge::~MediaPlayerBridge() {
56 if (!j_media_player_bridge_.is_null()) {
57 JNIEnv* env = base::android::AttachCurrentThread();
58 CHECK(env);
59 Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
61 Release();
64 void MediaPlayerBridge::Initialize() {
65 cookies_.clear();
66 if (url_.SchemeIsFile() || url_.SchemeIs("data")) {
67 ExtractMediaMetadata(url_.spec());
68 return;
71 media::MediaResourceGetter* resource_getter =
72 manager()->GetMediaResourceGetter();
73 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
74 resource_getter->GetPlatformPathFromURL(
75 url_,
76 base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
77 weak_factory_.GetWeakPtr()));
78 return;
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());
85 return;
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();
96 CHECK(env);
98 j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
99 env, reinterpret_cast<intptr_t>(this)));
101 if (volume_ >= 0)
102 SetVolume(volume_);
104 AttachListener(j_media_player_bridge_.obj());
107 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
108 duration_ = duration;
111 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
112 if (j_media_player_bridge_.is_null()) {
113 if (surface.IsEmpty())
114 return;
115 Prepare();
118 JNIEnv* env = base::android::AttachCurrentThread();
119 CHECK(env);
120 Java_MediaPlayerBridge_setSurface(
121 env, j_media_player_bridge_.obj(), surface.j_surface().obj());
124 void MediaPlayerBridge::Prepare() {
125 DCHECK(j_media_player_bridge_.is_null());
126 CreateJavaMediaPlayerBridge();
127 if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
128 manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
129 url_,
130 base::Bind(&MediaPlayerBridge::SetDataSource,
131 weak_factory_.GetWeakPtr()));
132 return;
135 SetDataSource(url_.spec());
138 void MediaPlayerBridge::SetDataSource(const std::string& url) {
139 if (j_media_player_bridge_.is_null())
140 return;
142 JNIEnv* env = base::android::AttachCurrentThread();
143 CHECK(env);
145 int fd;
146 int64 offset;
147 int64 size;
148 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
149 if (!Java_MediaPlayerBridge_setDataSourceFromFd(
150 env, j_media_player_bridge_.obj(), fd, offset, size)) {
151 OnMediaError(MEDIA_ERROR_FORMAT);
152 return;
154 } else {
155 // Create a Java String for the URL.
156 ScopedJavaLocalRef<jstring> j_url_string =
157 ConvertUTF8ToJavaString(env, url);
159 jobject j_context = base::android::GetApplicationContext();
160 DCHECK(j_context);
162 const std::string data_uri_prefix("data:");
163 if (base::StartsWith(url, data_uri_prefix, base::CompareCase::SENSITIVE)) {
164 if (!Java_MediaPlayerBridge_setDataUriDataSource(
165 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
166 OnMediaError(MEDIA_ERROR_FORMAT);
168 return;
171 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
172 env, cookies_);
173 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
174 env, user_agent_);
176 if (!Java_MediaPlayerBridge_setDataSource(
177 env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
178 j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
179 OnMediaError(MEDIA_ERROR_FORMAT);
180 return;
184 request_media_resources_cb_.Run(player_id());
185 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
186 OnMediaError(MEDIA_ERROR_FORMAT);
189 bool MediaPlayerBridge::InterceptMediaUrl(
190 const std::string& url, int* fd, int64* offset, int64* size) {
191 // Sentinel value to check whether the output arguments have been set.
192 const int kUnsetValue = -1;
194 *fd = kUnsetValue;
195 *offset = kUnsetValue;
196 *size = kUnsetValue;
197 media::MediaUrlInterceptor* url_interceptor =
198 manager()->GetMediaUrlInterceptor();
199 if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
200 DCHECK_NE(kUnsetValue, *fd);
201 DCHECK_NE(kUnsetValue, *offset);
202 DCHECK_NE(kUnsetValue, *size);
203 return true;
205 return false;
208 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
209 jboolean success) {
210 if (!success) {
211 OnMediaError(MEDIA_ERROR_FORMAT);
212 return;
215 request_media_resources_cb_.Run(player_id());
216 if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
217 OnMediaError(MEDIA_ERROR_FORMAT);
220 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
221 cookies_ = cookies;
222 manager()->GetMediaResourceGetter()->GetAuthCredentials(
223 url_,
224 base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
225 weak_factory_.GetWeakPtr()));
228 void MediaPlayerBridge::OnAuthCredentialsRetrieved(
229 const base::string16& username, const base::string16& password) {
230 GURL::ReplacementsW replacements;
231 if (!username.empty()) {
232 replacements.SetUsernameStr(username);
233 if (!password.empty())
234 replacements.SetPasswordStr(password);
235 url_ = url_.ReplaceComponents(replacements);
237 ExtractMediaMetadata(url_.spec());
240 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
241 if (url.empty()) {
242 OnMediaError(MEDIA_ERROR_FORMAT);
243 return;
246 int fd;
247 int64 offset;
248 int64 size;
249 if (InterceptMediaUrl(url, &fd, &offset, &size)) {
250 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
251 fd, offset, size,
252 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
253 weak_factory_.GetWeakPtr()));
254 } else {
255 manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
256 url, cookies_, user_agent_,
257 base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
258 weak_factory_.GetWeakPtr()));
262 void MediaPlayerBridge::OnMediaMetadataExtracted(
263 base::TimeDelta duration, int width, int height, bool success) {
264 if (success) {
265 duration_ = duration;
266 width_ = width;
267 height_ = height;
269 manager()->OnMediaMetadataChanged(
270 player_id(), duration_, width_, height_, success);
273 void MediaPlayerBridge::Start() {
274 if (j_media_player_bridge_.is_null()) {
275 pending_play_ = true;
276 Prepare();
277 } else {
278 if (prepared_)
279 StartInternal();
280 else
281 pending_play_ = true;
285 void MediaPlayerBridge::Pause(bool is_media_related_action) {
286 if (j_media_player_bridge_.is_null()) {
287 pending_play_ = false;
288 } else {
289 if (prepared_ && IsPlaying())
290 PauseInternal();
291 else
292 pending_play_ = false;
296 bool MediaPlayerBridge::IsPlaying() {
297 if (!prepared_)
298 return pending_play_;
300 JNIEnv* env = base::android::AttachCurrentThread();
301 CHECK(env);
302 jboolean result = Java_MediaPlayerBridge_isPlaying(
303 env, j_media_player_bridge_.obj());
304 return result;
307 int MediaPlayerBridge::GetVideoWidth() {
308 if (!prepared_)
309 return width_;
310 JNIEnv* env = base::android::AttachCurrentThread();
311 return Java_MediaPlayerBridge_getVideoWidth(
312 env, j_media_player_bridge_.obj());
315 int MediaPlayerBridge::GetVideoHeight() {
316 if (!prepared_)
317 return height_;
318 JNIEnv* env = base::android::AttachCurrentThread();
319 return Java_MediaPlayerBridge_getVideoHeight(
320 env, j_media_player_bridge_.obj());
323 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
324 // Record the time to seek when OnMediaPrepared() is called.
325 pending_seek_ = timestamp;
326 should_seek_on_prepare_ = true;
328 if (j_media_player_bridge_.is_null())
329 Prepare();
330 else if (prepared_)
331 SeekInternal(GetCurrentTime(), timestamp);
334 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
335 if (!prepared_)
336 return pending_seek_;
337 JNIEnv* env = base::android::AttachCurrentThread();
338 return base::TimeDelta::FromMilliseconds(
339 Java_MediaPlayerBridge_getCurrentPosition(
340 env, j_media_player_bridge_.obj()));
343 base::TimeDelta MediaPlayerBridge::GetDuration() {
344 if (!prepared_)
345 return duration_;
346 JNIEnv* env = base::android::AttachCurrentThread();
347 const int duration_ms =
348 Java_MediaPlayerBridge_getDuration(env, j_media_player_bridge_.obj());
349 return duration_ms < 0 ? media::kInfiniteDuration()
350 : base::TimeDelta::FromMilliseconds(duration_ms);
353 void MediaPlayerBridge::Release() {
354 if (j_media_player_bridge_.is_null())
355 return;
357 time_update_timer_.Stop();
358 if (prepared_) {
359 pending_seek_ = GetCurrentTime();
360 should_seek_on_prepare_ = true;
363 prepared_ = false;
364 pending_play_ = false;
365 SetVideoSurface(gfx::ScopedJavaSurface());
366 JNIEnv* env = base::android::AttachCurrentThread();
367 Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
368 j_media_player_bridge_.Reset();
369 DetachListener();
372 void MediaPlayerBridge::SetVolume(double volume) {
373 if (j_media_player_bridge_.is_null()) {
374 volume_ = volume;
375 return;
378 JNIEnv* env = base::android::AttachCurrentThread();
379 CHECK(env);
381 Java_MediaPlayerBridge_setVolume(
382 env, j_media_player_bridge_.obj(), volume);
385 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
386 width_ = width;
387 height_ = height;
388 MediaPlayerAndroid::OnVideoSizeChanged(width, height);
391 void MediaPlayerBridge::OnPlaybackComplete() {
392 time_update_timer_.Stop();
393 MediaPlayerAndroid::OnPlaybackComplete();
396 void MediaPlayerBridge::OnMediaInterrupted() {
397 time_update_timer_.Stop();
398 MediaPlayerAndroid::OnMediaInterrupted();
401 void MediaPlayerBridge::OnMediaPrepared() {
402 if (j_media_player_bridge_.is_null())
403 return;
405 prepared_ = true;
406 duration_ = GetDuration();
408 // If media player was recovered from a saved state, consume all the pending
409 // events.
410 if (should_seek_on_prepare_) {
411 PendingSeekInternal(pending_seek_);
412 pending_seek_ = base::TimeDelta::FromMilliseconds(0);
413 should_seek_on_prepare_ = false;
416 if (pending_play_) {
417 StartInternal();
418 pending_play_ = false;
421 UpdateAllowedOperations();
422 manager()->OnMediaMetadataChanged(
423 player_id(), duration_, width_, height_, true);
426 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
427 JNIEnv* env = base::android::AttachCurrentThread();
428 CHECK(env);
430 return Java_MediaPlayerBridge_getAllowedOperations(
431 env, j_media_player_bridge_.obj());
434 void MediaPlayerBridge::UpdateAllowedOperations() {
435 JNIEnv* env = base::android::AttachCurrentThread();
436 CHECK(env);
438 ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
440 can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
441 can_seek_forward_ = Java_AllowedOperations_canSeekForward(
442 env, allowedOperations.obj());
443 can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
444 env, allowedOperations.obj());
447 void MediaPlayerBridge::StartInternal() {
448 if (!manager()->RequestPlay(player_id(), duration_)) {
449 Pause(true);
450 return;
453 JNIEnv* env = base::android::AttachCurrentThread();
454 Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
455 if (!time_update_timer_.IsRunning()) {
456 time_update_timer_.Start(
457 FROM_HERE,
458 base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
459 this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
463 void MediaPlayerBridge::PauseInternal() {
464 JNIEnv* env = base::android::AttachCurrentThread();
465 Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
466 time_update_timer_.Stop();
469 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
470 SeekInternal(GetCurrentTime(), time);
473 bool MediaPlayerBridge::SeekInternal(base::TimeDelta current_time,
474 base::TimeDelta time) {
475 // Seeking on content like live streams may cause the media player to
476 // get stuck in an error state.
477 if (time < current_time && !CanSeekBackward())
478 return false;
480 if (time >= current_time && !CanSeekForward())
481 return false;
483 if (time > duration_)
484 time = duration_;
486 // Seeking to an invalid position may cause media player to stuck in an
487 // error state.
488 if (time < base::TimeDelta()) {
489 DCHECK_EQ(-1.0, time.InMillisecondsF());
490 return false;
493 JNIEnv* env = base::android::AttachCurrentThread();
494 CHECK(env);
495 int time_msec = static_cast<int>(time.InMilliseconds());
496 Java_MediaPlayerBridge_seekTo(
497 env, j_media_player_bridge_.obj(), time_msec);
498 return true;
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