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