Upstream version 9.37.197.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_player_manager.h"
15 #include "media/base/android/media_resource_getter.h"
16 #include "media/base/android/media_url_interceptor.h"
17
18 using base::android::ConvertUTF8ToJavaString;
19 using base::android::ScopedJavaLocalRef;
20
21 // Time update happens every 250ms.
22 const int kTimeUpdateInterval = 250;
23
24 // blob url scheme.
25 const char kBlobScheme[] = "blob";
26
27 namespace media {
28
29 MediaPlayerBridge::MediaPlayerBridge(
30     int player_id,
31     const GURL& url,
32     const GURL& first_party_for_cookies,
33     const std::string& user_agent,
34     bool hide_url_log,
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,
41                          manager,
42                          request_media_resources_cb,
43                          release_media_resources_cb,
44                          frame_url),
45       prepared_(false),
46       pending_play_(false),
47       url_(url),
48       first_party_for_cookies_(first_party_for_cookies),
49       user_agent_(user_agent),
50       hide_url_log_(hide_url_log),
51       width_(0),
52       height_(0),
53       can_pause_(true),
54       can_seek_forward_(true),
55       can_seek_backward_(true),
56       is_surface_in_use_(false),
57       volume_(-1.0),
58       allow_credentials_(allow_credentials),
59       weak_factory_(this) {
60   listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
61                                           weak_factory_.GetWeakPtr()));
62 }
63
64 MediaPlayerBridge::~MediaPlayerBridge() {
65   if (!j_media_player_bridge_.is_null()) {
66     JNIEnv* env = base::android::AttachCurrentThread();
67     CHECK(env);
68     Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
69   }
70   Release();
71 }
72
73 void MediaPlayerBridge::Initialize() {
74   cookies_.clear();
75   if (url_.SchemeIsFile() || url_.SchemeIs("app")) {
76     ExtractMediaMetadata(url_.spec());
77     return;
78   }
79
80   media::MediaResourceGetter* resource_getter =
81       manager()->GetMediaResourceGetter();
82   if (url_.SchemeIsFileSystem() || url_.SchemeIs(kBlobScheme)) {
83     resource_getter->GetPlatformPathFromURL(
84         url_,
85         base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
86                    weak_factory_.GetWeakPtr()));
87     return;
88   }
89
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());
94     return;
95   }
96
97   resource_getter->GetCookies(url_,
98                               first_party_for_cookies_,
99                               base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
100                                          weak_factory_.GetWeakPtr()));
101 }
102
103 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
104   JNIEnv* env = base::android::AttachCurrentThread();
105   CHECK(env);
106
107   j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
108       env, reinterpret_cast<intptr_t>(this)));
109
110   if (volume_ >= 0)
111     SetVolume(volume_);
112
113   SetMediaPlayerListener();
114 }
115
116 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
117     jobject j_media_player_bridge) {
118   JNIEnv* env = base::android::AttachCurrentThread();
119   CHECK(env);
120
121   j_media_player_bridge_.Reset(env, j_media_player_bridge);
122 }
123
124 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
125     GetJavaMediaPlayerBridge() {
126   base::android::ScopedJavaLocalRef<jobject> j_bridge(
127       j_media_player_bridge_);
128   return j_bridge;
129 }
130
131 void MediaPlayerBridge::SetMediaPlayerListener() {
132   jobject j_context = base::android::GetApplicationContext();
133   DCHECK(j_context);
134
135   listener_->CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
136 }
137
138 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
139   duration_ = duration;
140 }
141
142 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
143   if (j_media_player_bridge_.is_null()) {
144     if (surface.IsEmpty())
145       return;
146     Prepare();
147   }
148
149   JNIEnv* env = base::android::AttachCurrentThread();
150   CHECK(env);
151   is_surface_in_use_ = true;
152   Java_MediaPlayerBridge_setSurface(
153       env, j_media_player_bridge_.obj(), surface.j_surface().obj());
154 }
155
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(
161         url_,
162         base::Bind(&MediaPlayerBridge::SetDataSource,
163                    weak_factory_.GetWeakPtr()));
164     return;
165   }
166
167   SetDataSource(url_.spec());
168 }
169
170 void MediaPlayerBridge::SetDataSource(const std::string& url) {
171   if (j_media_player_bridge_.is_null())
172     return;
173
174   JNIEnv* env = base::android::AttachCurrentThread();
175   CHECK(env);
176
177   int fd;
178   int64 offset;
179   int64 size;
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);
184       return;
185     }
186   } else {
187     // Create a Java String for the URL.
188     ScopedJavaLocalRef<jstring> j_url_string =
189         ConvertUTF8ToJavaString(env, url);
190
191     jobject j_context = base::android::GetApplicationContext();
192     DCHECK(j_context);
193
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);
199       }
200       return;
201     }
202
203     ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
204         env, cookies_);
205     ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
206         env, user_agent_);
207
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);
212       return;
213     }
214   }
215
216   request_media_resources_cb_.Run(player_id());
217   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
218     OnMediaError(MEDIA_ERROR_FORMAT);
219 }
220
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;
225
226   *fd = kUnsetValue;
227   *offset = kUnsetValue;
228   *size = 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);
235     return true;
236   }
237   return false;
238 }
239
240 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
241     jboolean success) {
242   if (!success) {
243     OnMediaError(MEDIA_ERROR_FORMAT);
244     return;
245   }
246
247   request_media_resources_cb_.Run(player_id());
248   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
249     OnMediaError(MEDIA_ERROR_FORMAT);
250 }
251
252 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
253   cookies_ = cookies;
254   ExtractMediaMetadata(url_.spec());
255 }
256
257 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
258   int fd;
259   int64 offset;
260   int64 size;
261   if (InterceptMediaUrl(url, &fd, &offset, &size)) {
262     manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
263         fd, offset, size,
264         base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
265                    weak_factory_.GetWeakPtr()));
266   } else {
267     manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
268         url, cookies_, user_agent_,
269         base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
270                    weak_factory_.GetWeakPtr()));
271   }
272 }
273
274 void MediaPlayerBridge::OnMediaMetadataExtracted(
275     base::TimeDelta duration, int width, int height, bool success) {
276   if (success) {
277     duration_ = duration;
278     width_ = width;
279     height_ = height;
280   }
281   manager()->OnMediaMetadataChanged(
282       player_id(), duration_, width_, height_, success);
283 }
284
285 void MediaPlayerBridge::Start() {
286   if (j_media_player_bridge_.is_null()) {
287     pending_play_ = true;
288     Prepare();
289   } else {
290     if (prepared_)
291       StartInternal();
292     else
293       pending_play_ = true;
294   }
295 }
296
297 void MediaPlayerBridge::Pause(bool is_media_related_action) {
298   if (j_media_player_bridge_.is_null()) {
299     pending_play_ = false;
300   } else {
301     if (prepared_ && IsPlaying())
302       PauseInternal();
303     else
304       pending_play_ = false;
305   }
306 }
307
308 bool MediaPlayerBridge::IsPlaying() {
309   if (!prepared_)
310     return pending_play_;
311
312   JNIEnv* env = base::android::AttachCurrentThread();
313   CHECK(env);
314   jboolean result = Java_MediaPlayerBridge_isPlaying(
315       env, j_media_player_bridge_.obj());
316   return result;
317 }
318
319 int MediaPlayerBridge::GetVideoWidth() {
320   if (!prepared_)
321     return width_;
322   JNIEnv* env = base::android::AttachCurrentThread();
323   return Java_MediaPlayerBridge_getVideoWidth(
324       env, j_media_player_bridge_.obj());
325 }
326
327 int MediaPlayerBridge::GetVideoHeight() {
328   if (!prepared_)
329     return height_;
330   JNIEnv* env = base::android::AttachCurrentThread();
331   return Java_MediaPlayerBridge_getVideoHeight(
332       env, j_media_player_bridge_.obj());
333 }
334
335 void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
336   // Record the time to seek when OnMediaPrepared() is called.
337   pending_seek_ = timestamp;
338
339   if (j_media_player_bridge_.is_null())
340     Prepare();
341   else if (prepared_)
342     SeekInternal(timestamp);
343 }
344
345 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
346   if (!prepared_)
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()));
352 }
353
354 base::TimeDelta MediaPlayerBridge::GetDuration() {
355   if (!prepared_)
356     return duration_;
357   JNIEnv* env = base::android::AttachCurrentThread();
358   return base::TimeDelta::FromMilliseconds(
359       Java_MediaPlayerBridge_getDuration(
360           env, j_media_player_bridge_.obj()));
361 }
362
363 void MediaPlayerBridge::Release() {
364   if (j_media_player_bridge_.is_null())
365     return;
366
367   time_update_timer_.Stop();
368   if (prepared_)
369     pending_seek_ = GetCurrentTime();
370   prepared_ = false;
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();
379 }
380
381 void MediaPlayerBridge::SetVolume(double volume) {
382   if (j_media_player_bridge_.is_null()) {
383     volume_ = volume;
384     return;
385   }
386
387   JNIEnv* env = base::android::AttachCurrentThread();
388   CHECK(env);
389   Java_MediaPlayerBridge_setVolume(
390       env, j_media_player_bridge_.obj(), volume);
391 }
392
393 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
394   width_ = width;
395   height_ = height;
396   manager()->OnVideoSizeChanged(player_id(), width, height);
397 }
398
399 void MediaPlayerBridge::OnMediaError(int error_type) {
400   manager()->OnError(player_id(), error_type);
401 }
402
403 void MediaPlayerBridge::OnBufferingUpdate(int percent) {
404   manager()->OnBufferingUpdate(player_id(), percent);
405 }
406
407 void MediaPlayerBridge::OnPlaybackComplete() {
408   time_update_timer_.Stop();
409   manager()->OnPlaybackComplete(player_id());
410 }
411
412 void MediaPlayerBridge::OnMediaInterrupted() {
413   time_update_timer_.Stop();
414   manager()->OnMediaInterrupted(player_id());
415 }
416
417 void MediaPlayerBridge::OnSeekComplete() {
418   manager()->OnSeekComplete(player_id(), GetCurrentTime());
419 }
420
421 void MediaPlayerBridge::OnMediaPrepared() {
422   if (j_media_player_bridge_.is_null())
423     return;
424
425   prepared_ = true;
426   duration_ = GetDuration();
427
428   // If media player was recovered from a saved state, consume all the pending
429   // events.
430   PendingSeekInternal(pending_seek_);
431
432   if (pending_play_) {
433     StartInternal();
434     pending_play_ = false;
435   }
436
437   UpdateAllowedOperations();
438   manager()->OnMediaMetadataChanged(
439       player_id(), duration_, width_, height_, true);
440 }
441
442 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
443   JNIEnv* env = base::android::AttachCurrentThread();
444   CHECK(env);
445
446   return Java_MediaPlayerBridge_getAllowedOperations(
447       env, j_media_player_bridge_.obj());
448 }
449
450 void MediaPlayerBridge::UpdateAllowedOperations() {
451   JNIEnv* env = base::android::AttachCurrentThread();
452   CHECK(env);
453
454   ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
455
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());
461 }
462
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(
468         FROM_HERE,
469         base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
470         this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
471   }
472 }
473
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();
478 }
479
480 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
481   SeekInternal(time);
482 }
483
484 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
485   if (time > duration_)
486     time = duration_;
487
488   // Seeking to an invalid position may cause media player to stuck in an
489   // error state.
490   if (time < base::TimeDelta()) {
491     DCHECK_EQ(-1.0, time.InMillisecondsF());
492     return;
493   }
494
495   JNIEnv* env = base::android::AttachCurrentThread();
496   CHECK(env);
497   int time_msec = static_cast<int>(time.InMilliseconds());
498   Java_MediaPlayerBridge_seekTo(
499       env, j_media_player_bridge_.obj(), time_msec);
500 }
501
502 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
503   manager()->OnTimeUpdate(player_id(), GetCurrentTime());
504 }
505
506 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
507   bool ret = RegisterNativesImpl(env);
508   DCHECK(g_MediaPlayerBridge_clazz);
509   return ret;
510 }
511
512 bool MediaPlayerBridge::CanPause() {
513   return can_pause_;
514 }
515
516 bool MediaPlayerBridge::CanSeekForward() {
517   return can_seek_forward_;
518 }
519
520 bool MediaPlayerBridge::CanSeekBackward() {
521   return can_seek_backward_;
522 }
523
524 bool MediaPlayerBridge::IsPlayerReady() {
525   return prepared_;
526 }
527
528 GURL MediaPlayerBridge::GetUrl() {
529   return url_;
530 }
531
532 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
533   return first_party_for_cookies_;
534 }
535
536 bool MediaPlayerBridge::IsSurfaceInUse() const {
537   return is_surface_in_use_;
538 }
539
540 }  // namespace media