• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 using base::android::ConvertUTF8ToJavaString;
18 using base::android::ScopedJavaLocalRef;
19 
20 // Time update happens every 250ms.
21 static const int kTimeUpdateInterval = 250;
22 
23 namespace media {
24 
MediaPlayerBridge(int player_id,const GURL & url,const GURL & first_party_for_cookies,bool hide_url_log,MediaPlayerManager * manager)25 MediaPlayerBridge::MediaPlayerBridge(
26     int player_id,
27     const GURL& url,
28     const GURL& first_party_for_cookies,
29     bool hide_url_log,
30     MediaPlayerManager* manager)
31     : MediaPlayerAndroid(player_id,
32                          manager),
33       prepared_(false),
34       pending_play_(false),
35       url_(url),
36       first_party_for_cookies_(first_party_for_cookies),
37       hide_url_log_(hide_url_log),
38       width_(0),
39       height_(0),
40       can_pause_(true),
41       can_seek_forward_(true),
42       can_seek_backward_(true),
43       weak_this_(this),
44       listener_(base::MessageLoopProxy::current(),
45                 weak_this_.GetWeakPtr()) {
46 }
47 
~MediaPlayerBridge()48 MediaPlayerBridge::~MediaPlayerBridge() {
49   if (!j_media_player_bridge_.is_null()) {
50     JNIEnv* env = base::android::AttachCurrentThread();
51     CHECK(env);
52     Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
53   }
54   Release();
55 }
56 
Initialize()57 void MediaPlayerBridge::Initialize() {
58   if (url_.SchemeIsFile()) {
59     cookies_.clear();
60     ExtractMediaMetadata(url_.spec());
61     return;
62   }
63 
64   media::MediaResourceGetter* resource_getter =
65       manager()->GetMediaResourceGetter();
66   if (url_.SchemeIsFileSystem()) {
67     cookies_.clear();
68     resource_getter->GetPlatformPathFromFileSystemURL(url_, base::Bind(
69         &MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr()));
70     return;
71   }
72 
73   resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind(
74       &MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr()));
75 }
76 
CreateJavaMediaPlayerBridge()77 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
78   JNIEnv* env = base::android::AttachCurrentThread();
79   CHECK(env);
80 
81   j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
82       env, reinterpret_cast<intptr_t>(this)));
83 
84   SetMediaPlayerListener();
85 }
86 
SetJavaMediaPlayerBridge(jobject j_media_player_bridge)87 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
88     jobject j_media_player_bridge) {
89   JNIEnv* env = base::android::AttachCurrentThread();
90   CHECK(env);
91 
92   j_media_player_bridge_.Reset(env, j_media_player_bridge);
93 }
94 
95 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
GetJavaMediaPlayerBridge()96     GetJavaMediaPlayerBridge() {
97   base::android::ScopedJavaLocalRef<jobject> j_bridge(
98       j_media_player_bridge_);
99   return j_bridge;
100 }
101 
SetMediaPlayerListener()102 void MediaPlayerBridge::SetMediaPlayerListener() {
103   jobject j_context = base::android::GetApplicationContext();
104   DCHECK(j_context);
105 
106   listener_.CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
107 }
108 
SetDuration(base::TimeDelta duration)109 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
110   duration_ = duration;
111 }
112 
SetVideoSurface(gfx::ScopedJavaSurface surface)113 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
114   if (j_media_player_bridge_.is_null()) {
115     if (surface.IsEmpty())
116       return;
117     Prepare();
118   }
119 
120   JNIEnv* env = base::android::AttachCurrentThread();
121   CHECK(env);
122 
123   Java_MediaPlayerBridge_setSurface(
124       env, j_media_player_bridge_.obj(), surface.j_surface().obj());
125 }
126 
Prepare()127 void MediaPlayerBridge::Prepare() {
128   DCHECK(j_media_player_bridge_.is_null());
129   CreateJavaMediaPlayerBridge();
130   if (url_.SchemeIsFileSystem()) {
131     manager()->GetMediaResourceGetter()->GetPlatformPathFromFileSystemURL(
132             url_, base::Bind(&MediaPlayerBridge::SetDataSource,
133                              weak_this_.GetWeakPtr()));
134   } else {
135     SetDataSource(url_.spec());
136   }
137 }
138 
SetDataSource(const std::string & url)139 void MediaPlayerBridge::SetDataSource(const std::string& url) {
140   if (j_media_player_bridge_.is_null())
141     return;
142 
143   JNIEnv* env = base::android::AttachCurrentThread();
144   CHECK(env);
145 
146   // Create a Java String for the URL.
147   ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
148   ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
149       env, cookies_);
150 
151   jobject j_context = base::android::GetApplicationContext();
152   DCHECK(j_context);
153 
154   const std::string data_uri_prefix("data:");
155   if (StartsWithASCII(url, data_uri_prefix, true)) {
156     if (!Java_MediaPlayerBridge_setDataUriDataSource(
157         env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
158       OnMediaError(MEDIA_ERROR_FORMAT);
159     }
160     return;
161   }
162 
163   if (!Java_MediaPlayerBridge_setDataSource(
164       env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
165       j_cookies.obj(), hide_url_log_)) {
166     OnMediaError(MEDIA_ERROR_FORMAT);
167     return;
168   }
169 
170   manager()->RequestMediaResources(player_id());
171   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
172     OnMediaError(MEDIA_ERROR_FORMAT);
173 }
174 
OnDidSetDataUriDataSource(JNIEnv * env,jobject obj,jboolean success)175 void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
176     jboolean success) {
177   if (!success) {
178     OnMediaError(MEDIA_ERROR_FORMAT);
179     return;
180   }
181 
182   manager()->RequestMediaResources(player_id());
183   if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
184     OnMediaError(MEDIA_ERROR_FORMAT);
185 }
186 
OnCookiesRetrieved(const std::string & cookies)187 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
188   cookies_ = cookies;
189   ExtractMediaMetadata(url_.spec());
190 }
191 
ExtractMediaMetadata(const std::string & url)192 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
193   manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
194       url, cookies_, base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
195                                 weak_this_.GetWeakPtr()));
196 }
197 
OnMediaMetadataExtracted(base::TimeDelta duration,int width,int height,bool success)198 void MediaPlayerBridge::OnMediaMetadataExtracted(
199     base::TimeDelta duration, int width, int height, bool success) {
200   if (success) {
201     duration_ = duration;
202     width_ = width;
203     height_ = height;
204   }
205   manager()->OnMediaMetadataChanged(
206       player_id(), duration_, width_, height_, success);
207 }
208 
Start()209 void MediaPlayerBridge::Start() {
210   if (j_media_player_bridge_.is_null()) {
211     pending_play_ = true;
212     Prepare();
213   } else {
214     if (prepared_)
215       StartInternal();
216     else
217       pending_play_ = true;
218   }
219 }
220 
Pause(bool is_media_related_action)221 void MediaPlayerBridge::Pause(bool is_media_related_action) {
222   if (j_media_player_bridge_.is_null()) {
223     pending_play_ = false;
224   } else {
225     if (prepared_ && IsPlaying())
226       PauseInternal();
227     else
228       pending_play_ = false;
229   }
230 }
231 
IsPlaying()232 bool MediaPlayerBridge::IsPlaying() {
233   if (!prepared_)
234     return pending_play_;
235 
236   JNIEnv* env = base::android::AttachCurrentThread();
237   CHECK(env);
238   jboolean result = Java_MediaPlayerBridge_isPlaying(
239       env, j_media_player_bridge_.obj());
240   return result;
241 }
242 
GetVideoWidth()243 int MediaPlayerBridge::GetVideoWidth() {
244   if (!prepared_)
245     return width_;
246   JNIEnv* env = base::android::AttachCurrentThread();
247   return Java_MediaPlayerBridge_getVideoWidth(
248       env, j_media_player_bridge_.obj());
249 }
250 
GetVideoHeight()251 int MediaPlayerBridge::GetVideoHeight() {
252   if (!prepared_)
253     return height_;
254   JNIEnv* env = base::android::AttachCurrentThread();
255   return Java_MediaPlayerBridge_getVideoHeight(
256       env, j_media_player_bridge_.obj());
257 }
258 
SeekTo(const base::TimeDelta & timestamp)259 void MediaPlayerBridge::SeekTo(const base::TimeDelta& timestamp) {
260   // Record the time to seek when OnMediaPrepared() is called.
261   pending_seek_ = timestamp;
262 
263   if (j_media_player_bridge_.is_null())
264     Prepare();
265   else if (prepared_)
266     SeekInternal(timestamp);
267 }
268 
GetCurrentTime()269 base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
270   if (!prepared_)
271     return pending_seek_;
272   JNIEnv* env = base::android::AttachCurrentThread();
273   return base::TimeDelta::FromMilliseconds(
274       Java_MediaPlayerBridge_getCurrentPosition(
275           env, j_media_player_bridge_.obj()));
276 }
277 
GetDuration()278 base::TimeDelta MediaPlayerBridge::GetDuration() {
279   if (!prepared_)
280     return duration_;
281   JNIEnv* env = base::android::AttachCurrentThread();
282   return base::TimeDelta::FromMilliseconds(
283       Java_MediaPlayerBridge_getDuration(
284           env, j_media_player_bridge_.obj()));
285 }
286 
Release()287 void MediaPlayerBridge::Release() {
288   if (j_media_player_bridge_.is_null())
289     return;
290 
291   time_update_timer_.Stop();
292   if (prepared_)
293     pending_seek_ = GetCurrentTime();
294   prepared_ = false;
295   pending_play_ = false;
296   SetVideoSurface(gfx::ScopedJavaSurface());
297 
298   JNIEnv* env = base::android::AttachCurrentThread();
299   Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
300   j_media_player_bridge_.Reset();
301   manager()->ReleaseMediaResources(player_id());
302   listener_.ReleaseMediaPlayerListenerResources();
303 }
304 
SetVolume(double volume)305 void MediaPlayerBridge::SetVolume(double volume) {
306   if (j_media_player_bridge_.is_null())
307     return;
308 
309   JNIEnv* env = base::android::AttachCurrentThread();
310   CHECK(env);
311   Java_MediaPlayerBridge_setVolume(
312       env, j_media_player_bridge_.obj(), volume);
313 }
314 
OnVideoSizeChanged(int width,int height)315 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
316   width_ = width;
317   height_ = height;
318   manager()->OnVideoSizeChanged(player_id(), width, height);
319 }
320 
OnMediaError(int error_type)321 void MediaPlayerBridge::OnMediaError(int error_type) {
322   manager()->OnError(player_id(), error_type);
323 }
324 
OnBufferingUpdate(int percent)325 void MediaPlayerBridge::OnBufferingUpdate(int percent) {
326   manager()->OnBufferingUpdate(player_id(), percent);
327 }
328 
OnPlaybackComplete()329 void MediaPlayerBridge::OnPlaybackComplete() {
330   time_update_timer_.Stop();
331   manager()->OnPlaybackComplete(player_id());
332 }
333 
OnMediaInterrupted()334 void MediaPlayerBridge::OnMediaInterrupted() {
335   time_update_timer_.Stop();
336   manager()->OnMediaInterrupted(player_id());
337 }
338 
OnSeekComplete()339 void MediaPlayerBridge::OnSeekComplete() {
340   manager()->OnSeekComplete(player_id(), GetCurrentTime());
341 }
342 
OnMediaPrepared()343 void MediaPlayerBridge::OnMediaPrepared() {
344   if (j_media_player_bridge_.is_null())
345     return;
346 
347   prepared_ = true;
348   duration_ = GetDuration();
349 
350   // If media player was recovered from a saved state, consume all the pending
351   // events.
352   PendingSeekInternal(pending_seek_);
353 
354   if (pending_play_) {
355     StartInternal();
356     pending_play_ = false;
357   }
358 
359   UpdateAllowedOperations();
360   manager()->OnMediaMetadataChanged(
361       player_id(), duration_, width_, height_, true);
362 }
363 
GetAllowedOperations()364 ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
365   JNIEnv* env = base::android::AttachCurrentThread();
366   CHECK(env);
367 
368   return Java_MediaPlayerBridge_getAllowedOperations(
369       env, j_media_player_bridge_.obj());
370 }
371 
UpdateAllowedOperations()372 void MediaPlayerBridge::UpdateAllowedOperations() {
373   JNIEnv* env = base::android::AttachCurrentThread();
374   CHECK(env);
375 
376   ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
377 
378   can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
379   can_seek_forward_ = Java_AllowedOperations_canSeekForward(
380       env, allowedOperations.obj());
381   can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
382       env, allowedOperations.obj());
383 }
384 
StartInternal()385 void MediaPlayerBridge::StartInternal() {
386   JNIEnv* env = base::android::AttachCurrentThread();
387   Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
388   if (!time_update_timer_.IsRunning()) {
389     time_update_timer_.Start(
390         FROM_HERE,
391         base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
392         this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
393   }
394 }
395 
PauseInternal()396 void MediaPlayerBridge::PauseInternal() {
397   JNIEnv* env = base::android::AttachCurrentThread();
398   Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
399   time_update_timer_.Stop();
400 }
401 
PendingSeekInternal(const base::TimeDelta & time)402 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
403   SeekInternal(time);
404 }
405 
SeekInternal(base::TimeDelta time)406 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
407   if (time > duration_)
408     time = duration_;
409 
410   // Seeking to an invalid position may cause media player to stuck in an
411   // error state.
412   if (time < base::TimeDelta()) {
413     DCHECK_EQ(-1.0, time.InMillisecondsF());
414     return;
415   }
416 
417   JNIEnv* env = base::android::AttachCurrentThread();
418   CHECK(env);
419   int time_msec = static_cast<int>(time.InMilliseconds());
420   Java_MediaPlayerBridge_seekTo(
421       env, j_media_player_bridge_.obj(), time_msec);
422 }
423 
OnTimeUpdateTimerFired()424 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
425   manager()->OnTimeUpdate(player_id(), GetCurrentTime());
426 }
427 
RegisterMediaPlayerBridge(JNIEnv * env)428 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
429   bool ret = RegisterNativesImpl(env);
430   DCHECK(g_MediaPlayerBridge_clazz);
431   return ret;
432 }
433 
CanPause()434 bool MediaPlayerBridge::CanPause() {
435   return can_pause_;
436 }
437 
CanSeekForward()438 bool MediaPlayerBridge::CanSeekForward() {
439   return can_seek_forward_;
440 }
441 
CanSeekBackward()442 bool MediaPlayerBridge::CanSeekBackward() {
443   return can_seek_backward_;
444 }
445 
IsPlayerReady()446 bool MediaPlayerBridge::IsPlayerReady() {
447   return prepared_;
448 }
449 
GetUrl()450 GURL MediaPlayerBridge::GetUrl() {
451   return url_;
452 }
453 
GetFirstPartyForCookies()454 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
455   return first_party_for_cookies_;
456 }
457 
458 }  // namespace media
459