1ac56c171e65480eef97c89f92e74e42159526de4Johnny Chen// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2ac56c171e65480eef97c89f92e74e42159526de4Johnny Chen// Use of this source code is governed by a BSD-style license that can be
3ac56c171e65480eef97c89f92e74e42159526de4Johnny Chen// found in the LICENSE file.
4ac56c171e65480eef97c89f92e74e42159526de4Johnny Chen
5ac56c171e65480eef97c89f92e74e42159526de4Johnny Chen#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
19using base::android::ConvertUTF8ToJavaString;
20using base::android::ScopedJavaLocalRef;
21
22namespace media {
23
24MediaPlayerBridge::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) {
53}
54
55MediaPlayerBridge::~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());
60  }
61  Release();
62}
63
64void MediaPlayerBridge::Initialize() {
65  cookies_.clear();
66  if (url_.SchemeIsFile()) {
67    ExtractMediaMetadata(url_.spec());
68    return;
69  }
70
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;
79  }
80
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;
86  }
87
88  resource_getter->GetCookies(url_,
89                              first_party_for_cookies_,
90                              base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
91                                         weak_factory_.GetWeakPtr()));
92}
93
94void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
95  JNIEnv* env = base::android::AttachCurrentThread();
96  CHECK(env);
97
98  j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
99      env, reinterpret_cast<intptr_t>(this)));
100
101  if (volume_ >= 0)
102    SetVolume(volume_);
103
104  AttachListener(j_media_player_bridge_.obj());
105}
106
107void MediaPlayerBridge::SetJavaMediaPlayerBridge(
108    jobject j_media_player_bridge) {
109  JNIEnv* env = base::android::AttachCurrentThread();
110  CHECK(env);
111
112  j_media_player_bridge_.Reset(env, j_media_player_bridge);
113}
114
115base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
116    GetJavaMediaPlayerBridge() {
117  base::android::ScopedJavaLocalRef<jobject> j_bridge(
118      j_media_player_bridge_);
119  return j_bridge;
120}
121
122void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
123  duration_ = duration;
124}
125
126void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
127  if (j_media_player_bridge_.is_null()) {
128    if (surface.IsEmpty())
129      return;
130    Prepare();
131  }
132
133  JNIEnv* env = base::android::AttachCurrentThread();
134  CHECK(env);
135  Java_MediaPlayerBridge_setSurface(
136      env, j_media_player_bridge_.obj(), surface.j_surface().obj());
137}
138
139void MediaPlayerBridge::Prepare() {
140  DCHECK(j_media_player_bridge_.is_null());
141  CreateJavaMediaPlayerBridge();
142  if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
143    manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
144        url_,
145        base::Bind(&MediaPlayerBridge::SetDataSource,
146                   weak_factory_.GetWeakPtr()));
147    return;
148  }
149
150  SetDataSource(url_.spec());
151}
152
153void MediaPlayerBridge::SetDataSource(const std::string& url) {
154  if (j_media_player_bridge_.is_null())
155    return;
156
157  JNIEnv* env = base::android::AttachCurrentThread();
158  CHECK(env);
159
160  int fd;
161  int64 offset;
162  int64 size;
163  if (InterceptMediaUrl(url, &fd, &offset, &size)) {
164    if (!Java_MediaPlayerBridge_setDataSourceFromFd(
165        env, j_media_player_bridge_.obj(), fd, offset, size)) {
166      OnMediaError(MEDIA_ERROR_FORMAT);
167      return;
168    }
169  } else {
170    // Create a Java String for the URL.
171    ScopedJavaLocalRef<jstring> j_url_string =
172        ConvertUTF8ToJavaString(env, url);
173
174    jobject j_context = base::android::GetApplicationContext();
175    DCHECK(j_context);
176
177    const std::string data_uri_prefix("data:");
178    if (StartsWithASCII(url, data_uri_prefix, true)) {
179      if (!Java_MediaPlayerBridge_setDataUriDataSource(
180          env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
181        OnMediaError(MEDIA_ERROR_FORMAT);
182      }
183      return;
184    }
185
186    ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
187        env, cookies_);
188    ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
189        env, user_agent_);
190
191    if (!Java_MediaPlayerBridge_setDataSource(
192        env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
193        j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
194      OnMediaError(MEDIA_ERROR_FORMAT);
195      return;
196    }
197  }
198
199  request_media_resources_cb_.Run(player_id());
200  if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
201    OnMediaError(MEDIA_ERROR_FORMAT);
202}
203
204bool MediaPlayerBridge::InterceptMediaUrl(
205    const std::string& url, int* fd, int64* offset, int64* size) {
206  // Sentinel value to check whether the output arguments have been set.
207  const int kUnsetValue = -1;
208
209  *fd = kUnsetValue;
210  *offset = kUnsetValue;
211  *size = kUnsetValue;
212  media::MediaUrlInterceptor* url_interceptor =
213      manager()->GetMediaUrlInterceptor();
214  if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
215    DCHECK_NE(kUnsetValue, *fd);
216    DCHECK_NE(kUnsetValue, *offset);
217    DCHECK_NE(kUnsetValue, *size);
218    return true;
219  }
220  return false;
221}
222
223void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
224    jboolean success) {
225  if (!success) {
226    OnMediaError(MEDIA_ERROR_FORMAT);
227    return;
228  }
229
230  request_media_resources_cb_.Run(player_id());
231  if (!Java_MediaPlayerBridge_prepareAsync(env, j_media_player_bridge_.obj()))
232    OnMediaError(MEDIA_ERROR_FORMAT);
233}
234
235void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
236  cookies_ = cookies;
237  manager()->GetMediaResourceGetter()->GetAuthCredentials(
238      url_,
239      base::Bind(&MediaPlayerBridge::OnAuthCredentialsRetrieved,
240                 weak_factory_.GetWeakPtr()));
241}
242
243void MediaPlayerBridge::OnAuthCredentialsRetrieved(
244    const base::string16& username, const base::string16& password) {
245  GURL::ReplacementsW replacements;
246  if (!username.empty()) {
247    replacements.SetUsernameStr(username);
248    if (!password.empty())
249      replacements.SetPasswordStr(password);
250    url_ = url_.ReplaceComponents(replacements);
251  }
252  ExtractMediaMetadata(url_.spec());
253}
254
255void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
256  if (url.empty()) {
257    OnMediaError(MEDIA_ERROR_FORMAT);
258    return;
259  }
260
261  int fd;
262  int64 offset;
263  int64 size;
264  if (InterceptMediaUrl(url, &fd, &offset, &size)) {
265    manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
266        fd, offset, size,
267        base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
268                   weak_factory_.GetWeakPtr()));
269  } else {
270    manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
271        url, cookies_, user_agent_,
272        base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
273                   weak_factory_.GetWeakPtr()));
274  }
275}
276
277void MediaPlayerBridge::OnMediaMetadataExtracted(
278    base::TimeDelta duration, int width, int height, bool success) {
279  if (success) {
280    duration_ = duration;
281    width_ = width;
282    height_ = height;
283  }
284  manager()->OnMediaMetadataChanged(
285      player_id(), duration_, width_, height_, success);
286}
287
288void MediaPlayerBridge::Start() {
289  if (j_media_player_bridge_.is_null()) {
290    pending_play_ = true;
291    Prepare();
292  } else {
293    if (prepared_)
294      StartInternal();
295    else
296      pending_play_ = true;
297  }
298}
299
300void MediaPlayerBridge::Pause(bool is_media_related_action) {
301  if (j_media_player_bridge_.is_null()) {
302    pending_play_ = false;
303  } else {
304    if (prepared_ && IsPlaying())
305      PauseInternal();
306    else
307      pending_play_ = false;
308  }
309}
310
311bool MediaPlayerBridge::IsPlaying() {
312  if (!prepared_)
313    return pending_play_;
314
315  JNIEnv* env = base::android::AttachCurrentThread();
316  CHECK(env);
317  jboolean result = Java_MediaPlayerBridge_isPlaying(
318      env, j_media_player_bridge_.obj());
319  return result;
320}
321
322int MediaPlayerBridge::GetVideoWidth() {
323  if (!prepared_)
324    return width_;
325  JNIEnv* env = base::android::AttachCurrentThread();
326  return Java_MediaPlayerBridge_getVideoWidth(
327      env, j_media_player_bridge_.obj());
328}
329
330int MediaPlayerBridge::GetVideoHeight() {
331  if (!prepared_)
332    return height_;
333  JNIEnv* env = base::android::AttachCurrentThread();
334  return Java_MediaPlayerBridge_getVideoHeight(
335      env, j_media_player_bridge_.obj());
336}
337
338void MediaPlayerBridge::SeekTo(base::TimeDelta timestamp) {
339  // Record the time to seek when OnMediaPrepared() is called.
340  pending_seek_ = timestamp;
341  should_seek_on_prepare_ = true;
342
343  if (j_media_player_bridge_.is_null())
344    Prepare();
345  else if (prepared_)
346    SeekInternal(timestamp);
347}
348
349base::TimeDelta MediaPlayerBridge::GetCurrentTime() {
350  if (!prepared_)
351    return pending_seek_;
352  JNIEnv* env = base::android::AttachCurrentThread();
353  return base::TimeDelta::FromMilliseconds(
354      Java_MediaPlayerBridge_getCurrentPosition(
355          env, j_media_player_bridge_.obj()));
356}
357
358base::TimeDelta MediaPlayerBridge::GetDuration() {
359  if (!prepared_)
360    return duration_;
361  JNIEnv* env = base::android::AttachCurrentThread();
362  return base::TimeDelta::FromMilliseconds(
363      Java_MediaPlayerBridge_getDuration(
364          env, j_media_player_bridge_.obj()));
365}
366
367void MediaPlayerBridge::Release() {
368  if (j_media_player_bridge_.is_null())
369    return;
370
371  time_update_timer_.Stop();
372  if (prepared_) {
373    pending_seek_ = GetCurrentTime();
374    should_seek_on_prepare_ = true;
375  }
376
377  prepared_ = false;
378  pending_play_ = false;
379  SetVideoSurface(gfx::ScopedJavaSurface());
380  JNIEnv* env = base::android::AttachCurrentThread();
381  Java_MediaPlayerBridge_release(env, j_media_player_bridge_.obj());
382  j_media_player_bridge_.Reset();
383  DetachListener();
384}
385
386void MediaPlayerBridge::SetVolume(double volume) {
387  if (j_media_player_bridge_.is_null()) {
388    volume_ = volume;
389    return;
390  }
391
392  JNIEnv* env = base::android::AttachCurrentThread();
393  CHECK(env);
394  Java_MediaPlayerBridge_setVolume(
395      env, j_media_player_bridge_.obj(), volume);
396}
397
398void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
399  width_ = width;
400  height_ = height;
401  MediaPlayerAndroid::OnVideoSizeChanged(width, height);
402}
403
404void MediaPlayerBridge::OnPlaybackComplete() {
405  time_update_timer_.Stop();
406  MediaPlayerAndroid::OnPlaybackComplete();
407}
408
409void MediaPlayerBridge::OnMediaInterrupted() {
410  time_update_timer_.Stop();
411  MediaPlayerAndroid::OnMediaInterrupted();
412}
413
414void MediaPlayerBridge::OnMediaPrepared() {
415  if (j_media_player_bridge_.is_null())
416    return;
417
418  prepared_ = true;
419  duration_ = GetDuration();
420
421  // If media player was recovered from a saved state, consume all the pending
422  // events.
423  if (should_seek_on_prepare_) {
424    PendingSeekInternal(pending_seek_);
425    pending_seek_ = base::TimeDelta::FromMilliseconds(0);
426    should_seek_on_prepare_ = false;
427  }
428
429  if (pending_play_) {
430    StartInternal();
431    pending_play_ = false;
432  }
433
434  UpdateAllowedOperations();
435  manager()->OnMediaMetadataChanged(
436      player_id(), duration_, width_, height_, true);
437}
438
439ScopedJavaLocalRef<jobject> MediaPlayerBridge::GetAllowedOperations() {
440  JNIEnv* env = base::android::AttachCurrentThread();
441  CHECK(env);
442
443  return Java_MediaPlayerBridge_getAllowedOperations(
444      env, j_media_player_bridge_.obj());
445}
446
447void MediaPlayerBridge::UpdateAllowedOperations() {
448  JNIEnv* env = base::android::AttachCurrentThread();
449  CHECK(env);
450
451  ScopedJavaLocalRef<jobject> allowedOperations = GetAllowedOperations();
452
453  can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
454  can_seek_forward_ = Java_AllowedOperations_canSeekForward(
455      env, allowedOperations.obj());
456  can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
457      env, allowedOperations.obj());
458}
459
460void MediaPlayerBridge::StartInternal() {
461  JNIEnv* env = base::android::AttachCurrentThread();
462  Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
463  if (!time_update_timer_.IsRunning()) {
464    time_update_timer_.Start(
465        FROM_HERE,
466        base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
467        this, &MediaPlayerBridge::OnTimeUpdateTimerFired);
468  }
469}
470
471void MediaPlayerBridge::PauseInternal() {
472  JNIEnv* env = base::android::AttachCurrentThread();
473  Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
474  time_update_timer_.Stop();
475}
476
477void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
478  SeekInternal(time);
479}
480
481void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
482  if (time > duration_)
483    time = duration_;
484
485  // Seeking to an invalid position may cause media player to stuck in an
486  // error state.
487  if (time < base::TimeDelta()) {
488    DCHECK_EQ(-1.0, time.InMillisecondsF());
489    return;
490  }
491
492  JNIEnv* env = base::android::AttachCurrentThread();
493  CHECK(env);
494  int time_msec = static_cast<int>(time.InMilliseconds());
495  Java_MediaPlayerBridge_seekTo(
496      env, j_media_player_bridge_.obj(), time_msec);
497}
498
499void MediaPlayerBridge::OnTimeUpdateTimerFired() {
500  manager()->OnTimeUpdate(
501      player_id(), GetCurrentTime(), base::TimeTicks::Now());
502}
503
504bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
505  bool ret = RegisterNativesImpl(env);
506  DCHECK(g_MediaPlayerBridge_clazz);
507  return ret;
508}
509
510bool MediaPlayerBridge::CanPause() {
511  return can_pause_;
512}
513
514bool MediaPlayerBridge::CanSeekForward() {
515  return can_seek_forward_;
516}
517
518bool MediaPlayerBridge::CanSeekBackward() {
519  return can_seek_backward_;
520}
521
522bool MediaPlayerBridge::IsPlayerReady() {
523  return prepared_;
524}
525
526GURL MediaPlayerBridge::GetUrl() {
527  return url_;
528}
529
530GURL MediaPlayerBridge::GetFirstPartyForCookies() {
531  return first_party_for_cookies_;
532}
533
534}  // namespace media
535