Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / base / android / media_player_bridge.cc
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.
4
5 #include "media/base/android/media_player_bridge.h"
6
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
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::ScopedJavaLocalRef;
21
22 namespace media {
23
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       url_(url),
41       first_party_for_cookies_(first_party_for_cookies),
42       user_agent_(user_agent),
43       hide_url_log_(hide_url_log),
44       width_(0),
45       height_(0),
46       can_pause_(true),
47       can_seek_forward_(true),
48       can_seek_backward_(true),
49       is_surface_in_use_(false),
50       volume_(-1.0),
51       allow_credentials_(allow_credentials),
52       weak_factory_(this) {
53   listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
54                                           weak_factory_.GetWeakPtr()));
55 }
56
57 MediaPlayerBridge::~MediaPlayerBridge() {
58   if (!j_media_player_bridge_.is_null()) {
59     JNIEnv* env = base::android::AttachCurrentThread();
60     CHECK(env);
61     Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
62   }
63   Release();
64 }
65
66 void MediaPlayerBridge::Initialize() {
67   cookies_.clear();
68   if (url_.SchemeIsFile() || url_.SchemeIs("app")) {
69     ExtractMediaMetadata(url_.spec());
70     return;
71   }
72
73   media::MediaResourceGetter* resource_getter =
74       manager()->GetMediaResourceGetter();
75   if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
76     resource_getter->GetPlatformPathFromURL(
77         url_,
78         base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
79                    weak_factory_.GetWeakPtr()));
80     return;
81   }
82
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());
87     return;
88   }
89
90   resource_getter->GetCookies(url_,
91                               first_party_for_cookies_,
92                               base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
93                                          weak_factory_.GetWeakPtr()));
94 }
95
96 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
97   JNIEnv* env = base::android::AttachCurrentThread();
98   CHECK(env);
99
100   j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
101       env, reinterpret_cast<intptr_t>(this)));
102
103   if (volume_ >= 0)
104     SetVolume(volume_);
105
106   SetMediaPlayerListener();
107 }
108
109 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
110     jobject j_media_player_bridge) {
111   JNIEnv* env = base::android::AttachCurrentThread();
112   CHECK(env);
113
114   j_media_player_bridge_.Reset(env, j_media_player_bridge);
115 }
116
117 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
118     GetJavaMediaPlayerBridge() {
119   base::android::ScopedJavaLocalRef<jobject> j_bridge(
120       j_media_player_bridge_);
121   return j_bridge;
122 }
123
124 void MediaPlayerBridge::SetMediaPlayerListener() {
125   jobject j_context = base::android::GetApplicationContext();
126   DCHECK(j_context);
127
128   listener_->CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
129 }
130
131 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
132   duration_ = duration;
133 }
134
135 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
136   if (j_media_player_bridge_.is_null()) {
137     if (surface.IsEmpty())
138       return;
139     Prepare();
140   }
141
142   JNIEnv* env = base::android::AttachCurrentThread();
143   CHECK(env);
144   is_surface_in_use_ = true;
145   Java_MediaPlayerBridge_setSurface(
146       env, j_media_player_bridge_.obj(), surface.j_surface().obj());
147 }
148
149 void MediaPlayerBridge::Prepare() {
150   DCHECK(j_media_player_bridge_.is_null());
151   CreateJavaMediaPlayerBridge();
152   if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
153     manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
154         url_,
155         base::Bind(&MediaPlayerBridge::SetDataSource,
156                    weak_factory_.GetWeakPtr()));
157     return;
158   }
159
160   SetDataSource(url_.spec());
161 }
162
163 void MediaPlayerBridge::SetDataSource(const std::string& url) {
164   if (j_media_player_bridge_.is_null())
165     return;
166
167   JNIEnv* env = base::android::AttachCurrentThread();
168   CHECK(env);
169
170   int fd;
171   int64 offset;
172   int64 size;
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);
177       return;
178     }
179   } else {
180     // Create a Java String for the URL.
181     ScopedJavaLocalRef<jstring> j_url_string =
182         ConvertUTF8ToJavaString(env, url);
183
184     jobject j_context = base::android::GetApplicationContext();
185     DCHECK(j_context);
186
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);
192       }
193       return;
194     }
195
196     ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
197         env, cookies_);
198     ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
199         env, user_agent_);
200
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);
205       return;
206     }
207   }
208
209   request_media_resources_cb_.Run(player_id());
210   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
211     OnMediaError(MEDIA_ERROR_FORMAT);
212 }
213
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;
218
219   *fd = kUnsetValue;
220   *offset = kUnsetValue;
221   *size = 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);
228     return true;
229   }
230   return false;
231 }
232
233 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
234     jboolean success) {
235   if (!success) {
236     OnMediaError(MEDIA_ERROR_FORMAT);
237     return;
238   }
239
240   request_media_resources_cb_.Run(player_id());
241   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
242     OnMediaError(MEDIA_ERROR_FORMAT);
243 }
244
245 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
246   cookies_ = cookies;
247   manager()->GetMediaResourceGetter()->GetAuthCredentials(
248       url_,
249       base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
250                  weak_factory_.GetWeakPtr()));
251 }
252
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);
261   }
262   ExtractMediaMetadata(url_.spec());
263 }
264
265 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
266   if (url.empty()) {
267     OnMediaError(MEDIA_ERROR_FORMAT);
268     return;
269   }
270
271   int fd;
272   int64 offset;
273   int64 size;
274   if (InterceptMediaUrl(url, &fd, &offset, &size)) {
275     manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
276         fd, offset, size,
277         base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
278                    weak_factory_.GetWeakPtr()));
279   } else {
280     manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
281         url, cookies_, user_agent_,
282         base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
283                    weak_factory_.GetWeakPtr()));
284   }
285 }
286
287 void MediaPlayerBridge::OnMediaMetadataExtracted(
288     base::TimeDelta duration, int width, int height, bool success) {
289   if (success) {
290     duration_ = duration;
291     width_ = width;
292     height_ = height;
293   }
294   manager()->OnMediaMetadataChanged(
295       player_id(), duration_, width_, height_, success);
296 }
297
298 void MediaPlayerBridge::Start() {
299   if (j_media_player_bridge_.is_null()) {
300     pending_play_ = true;
301     Prepare();
302   } else {
303     if (prepared_)
304       StartInternal();
305     else
306       pending_play_ = true;
307   }
308 }
309
310 void MediaPlayerBridge::Pause(bool is_media_related_action) {
311   if (j_media_player_bridge_.is_null()) {
312     pending_play_ = false;
313   } else {
314     if (prepared_ && IsPlaying())
315       PauseInternal();
316     else
317       pending_play_ = false;
318   }
319 }
320
321 bool MediaPlayerBridge::IsPlaying() {
322   if (!prepared_)
323     return pending_play_;
324
325   JNIEnv* env = base::android::AttachCurrentThread();
326   CHECK(env);
327   jboolean result = Java_MediaPlayerBridge_isPlaying(
328       env, j_media_player_bridge_.obj());
329   return result;
330 }
331
332 int MediaPlayerBridge::GetVideoWidth() {
333   if (!prepared_)
334     return width_;
335   JNIEnv* env = base::android::AttachCurrentThread();
336   return Java_MediaPlayerBridge_getVideoWidth(
337       env, j_media_player_bridge_.obj());
338 }
339
340 int MediaPlayerBridge::GetVideoHeight() {
341   if (!prepared_)
342     return height_;
343   JNIEnv* env = base::android::AttachCurrentThread();
344   return Java_MediaPlayerBridge_getVideoHeight(
345       env, j_media_player_bridge_.obj());
346 }
347
348 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
349   // Record the time to seek when OnMediaPrepared() is called.
350   pending_seek_ = timestamp;
351
352   if (j_media_player_bridge_.is_null())
353     Prepare();
354   else if (prepared_)
355     SeekInternal(timestamp);
356 }
357
358 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
359   if (!prepared_)
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()));
365 }
366
367 base::TimeDelta MediaPlayerBridge::GetDuration() {
368   if (!prepared_)
369     return duration_;
370   JNIEnv* env = base::android::AttachCurrentThread();
371   return base::TimeDelta::FromMilliseconds(
372       Java_MediaPlayerBridge_getDuration(
373           env, j_media_player_bridge_.obj()));
374 }
375
376 void MediaPlayerBridge::Release() {
377   if (j_media_player_bridge_.is_null())
378     return;
379
380   time_update_timer_.Stop();
381   if (prepared_)
382     pending_seek_ = GetCurrentTime();
383   prepared_ = false;
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();
391 }
392
393 void MediaPlayerBridge::SetVolume(double volume) {
394   if (j_media_player_bridge_.is_null()) {
395     volume_ = volume;
396     return;
397   }
398
399   JNIEnv* env = base::android::AttachCurrentThread();
400   CHECK(env);
401   Java_MediaPlayerBridge_setVolume(
402       env, j_media_player_bridge_.obj(), volume);
403 }
404
405 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
406   width_ = width;
407   height_ = height;
408   manager()->OnVideoSizeChanged(player_id(), width, height);
409 }
410
411 void MediaPlayerBridge::OnMediaError(int error_type) {
412   manager()->OnError(player_id(), error_type);
413 }
414
415 void MediaPlayerBridge::OnBufferingUpdate(int percent) {
416   manager()->OnBufferingUpdate(player_id(), percent);
417 }
418
419 void MediaPlayerBridge::OnPlaybackComplete() {
420   time_update_timer_.Stop();
421   manager()->OnPlaybackComplete(player_id());
422 }
423
424 void MediaPlayerBridge::OnMediaInterrupted() {
425   time_update_timer_.Stop();
426   manager()->OnMediaInterrupted(player_id());
427 }
428
429 void MediaPlayerBridge::OnSeekComplete() {
430   manager()->OnSeekComplete(player_id(), GetCurrentTime());
431 }
432
433 void MediaPlayerBridge::OnMediaPrepared() {
434   if (j_media_player_bridge_.is_null())
435     return;
436
437   prepared_ = true;
438   duration_ = GetDuration();
439
440   // If media player was recovered from a saved state, consume all the pending
441   // events.
442   PendingSeekInternal(pending_seek_);
443
444   if (pending_play_) {
445     StartInternal();
446     pending_play_ = false;
447   }
448
449   UpdateAllowedOperations();
450   manager()->OnMediaMetadataChanged(
451       player_id(), duration_, width_, height_, true);
452 }
453
454 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
455   JNIEnv* env = base::android::AttachCurrentThread();
456   CHECK(env);
457
458   return Java_MediaPlayerBridge_getAllowedOperations(
459       env, j_media_player_bridge_.obj());
460 }
461
462 void MediaPlayerBridge::UpdateAllowedOperations() {
463   JNIEnv* env = base::android::AttachCurrentThread();
464   CHECK(env);
465
466   ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
467
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());
473 }
474
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(
480         FROM_HERE,
481         base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
482         this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
483   }
484 }
485
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();
490 }
491
492 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
493   SeekInternal(time);
494 }
495
496 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
497   if (time > duration_)
498     time = duration_;
499
500   // Seeking to an invalid position may cause media player to stuck in an
501   // error state.
502   if (time < base::TimeDelta()) {
503     DCHECK_EQ(-1.0, time.InMillisecondsF());
504     return;
505   }
506
507   JNIEnv* env = base::android::AttachCurrentThread();
508   CHECK(env);
509   int time_msec = static_cast<int>(time.InMilliseconds());
510   Java_MediaPlayerBridge_seekTo(
511       env, j_media_player_bridge_.obj(), time_msec);
512 }
513
514 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
515   manager()->OnTimeUpdate(
516       player_id(), GetCurrentTime(), base::TimeTicks::Now());
517 }
518
519 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
520   bool ret = RegisterNativesImpl(env);
521   DCHECK(g_MediaPlayerBridge_clazz);
522   return ret;
523 }
524
525 bool MediaPlayerBridge::CanPause() {
526   return can_pause_;
527 }
528
529 bool MediaPlayerBridge::CanSeekForward() {
530   return can_seek_forward_;
531 }
532
533 bool MediaPlayerBridge::CanSeekBackward() {
534   return can_seek_backward_;
535 }
536
537 bool MediaPlayerBridge::IsPlayerReady() {
538   return prepared_;
539 }
540
541 GURL MediaPlayerBridge::GetUrl() {
542   return url_;
543 }
544
545 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
546   return first_party_for_cookies_;
547 }
548
549 bool MediaPlayerBridge::IsSurfaceInUse() const {
550   return is_surface_in_use_;
551 }
552
553 }  // namespace media