Home | History | Annotate | Download | only in android
      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 "jni/MediaPlayerBridge_jni.h"
     13 #include "media/base/android/media_player_manager.h"
     14 #include "media/base/android/media_resource_getter.h"
     15 #include "media/base/android/media_source_player.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 // Android MediaMetadataRetriever may fail to extract the metadata from the
     24 // media under some circumstances. This makes the user unable to perform
     25 // seek. To solve this problem, we use a temporary duration of 100 seconds when
     26 // the duration is unknown. And we scale the seek position later when duration
     27 // is available.
     28 static const int kTemporaryDuration = 100;
     29 
     30 namespace media {
     31 
     32 #if !defined(GOOGLE_TV)
     33 // static
     34 MediaPlayerAndroid* MediaPlayerAndroid::Create(
     35     int player_id,
     36     const GURL& url,
     37     SourceType source_type,
     38     const GURL& first_party_for_cookies,
     39     bool hide_url_log,
     40     MediaPlayerManager* manager,
     41     bool load_media_resource) {
     42   if (source_type == SOURCE_TYPE_URL) {
     43     MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge(
     44         player_id,
     45         url,
     46         first_party_for_cookies,
     47         hide_url_log,
     48         manager);
     49     if (load_media_resource)
     50       media_player_bridge->Initialize();
     51     return media_player_bridge;
     52   } else {
     53     return new MediaSourcePlayer(
     54         player_id,
     55         manager);
     56   }
     57 }
     58 #endif
     59 
     60 MediaPlayerBridge::MediaPlayerBridge(
     61     int player_id,
     62     const GURL& url,
     63     const GURL& first_party_for_cookies,
     64     bool hide_url_log,
     65     MediaPlayerManager* manager)
     66     : MediaPlayerAndroid(player_id,
     67                          manager),
     68       prepared_(false),
     69       pending_play_(false),
     70       url_(url),
     71       first_party_for_cookies_(first_party_for_cookies),
     72       hide_url_log_(hide_url_log),
     73       duration_(base::TimeDelta::FromSeconds(kTemporaryDuration)),
     74       width_(0),
     75       height_(0),
     76       can_pause_(true),
     77       can_seek_forward_(true),
     78       can_seek_backward_(true),
     79       weak_this_(this),
     80       listener_(base::MessageLoopProxy::current(),
     81                 weak_this_.GetWeakPtr()) {
     82 }
     83 
     84 MediaPlayerBridge::~MediaPlayerBridge() {
     85   Release();
     86 }
     87 
     88 void MediaPlayerBridge::Initialize() {
     89   if (url_.SchemeIsFile()) {
     90     cookies_.clear();
     91     ExtractMediaMetadata(url_.spec());
     92     return;
     93   }
     94 
     95   media::MediaResourceGetter* resource_getter =
     96       manager()->GetMediaResourceGetter();
     97   if (url_.SchemeIsFileSystem()) {
     98     cookies_.clear();
     99     resource_getter->GetPlatformPathFromFileSystemURL(url_, base::Bind(
    100         &MediaPlayerBridge::ExtractMediaMetadata, weak_this_.GetWeakPtr()));
    101     return;
    102   }
    103 
    104   resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind(
    105       &MediaPlayerBridge::OnCookiesRetrieved, weak_this_.GetWeakPtr()));
    106 }
    107 
    108 void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
    109   JNIEnv* env = base::android::AttachCurrentThread();
    110   CHECK(env);
    111 
    112   j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(env));
    113 
    114   SetMediaPlayerListener();
    115 }
    116 
    117 void MediaPlayerBridge::SetJavaMediaPlayerBridge(
    118     jobject j_media_player_bridge) {
    119   JNIEnv* env = base::android::AttachCurrentThread();
    120   CHECK(env);
    121 
    122   j_media_player_bridge_.Reset(env, j_media_player_bridge);
    123 }
    124 
    125 void MediaPlayerBridge::SetMediaPlayerListener() {
    126   jobject j_context = base::android::GetApplicationContext();
    127   DCHECK(j_context);
    128 
    129   listener_.CreateMediaPlayerListener(j_context, j_media_player_bridge_.obj());
    130 }
    131 
    132 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
    133   duration_ = duration;
    134 }
    135 
    136 void MediaPlayerBridge::SetVideoSurface(gfx::ScopedJavaSurface surface) {
    137   if (j_media_player_bridge_.is_null()) {
    138     if (surface.IsEmpty())
    139       return;
    140     Prepare();
    141   }
    142 
    143   JNIEnv* env = base::android::AttachCurrentThread();
    144   CHECK(env);
    145 
    146   Java_MediaPlayerBridge_setSurface(
    147       env, j_media_player_bridge_.obj(), surface.j_surface().obj());
    148 }
    149 
    150 void MediaPlayerBridge::Prepare() {
    151   if (j_media_player_bridge_.is_null())
    152     CreateJavaMediaPlayerBridge();
    153   if (url_.SchemeIsFileSystem()) {
    154     manager()->GetMediaResourceGetter()->GetPlatformPathFromFileSystemURL(
    155             url_, base::Bind(&MediaPlayerBridge::SetDataSource,
    156                              weak_this_.GetWeakPtr()));
    157   } else {
    158     SetDataSource(url_.spec());
    159   }
    160 }
    161 
    162 void MediaPlayerBridge::SetDataSource(const std::string& url) {
    163   if (j_media_player_bridge_.is_null())
    164     return;
    165 
    166   JNIEnv* env = base::android::AttachCurrentThread();
    167   CHECK(env);
    168 
    169   // Create a Java String for the URL.
    170   ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
    171   ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
    172       env, cookies_);
    173 
    174   jobject j_context = base::android::GetApplicationContext();
    175   DCHECK(j_context);
    176 
    177   if (Java_MediaPlayerBridge_setDataSource(
    178       env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
    179       j_cookies.obj(), hide_url_log_)) {
    180     RequestMediaResourcesFromManager();
    181     Java_MediaPlayerBridge_prepareAsync(
    182         env, j_media_player_bridge_.obj());
    183   } else {
    184     OnMediaError(MEDIA_ERROR_FORMAT);
    185   }
    186 }
    187 
    188 void MediaPlayerBridge::OnCookiesRetrieved(const std::string& cookies) {
    189   cookies_ = cookies;
    190   ExtractMediaMetadata(url_.spec());
    191 }
    192 
    193 void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
    194   manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
    195       url, cookies_, base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
    196                                 weak_this_.GetWeakPtr()));
    197 }
    198 
    199 void MediaPlayerBridge::OnMediaMetadataExtracted(
    200     base::TimeDelta duration, int width, int height, bool success) {
    201   if (success) {
    202     duration_ = duration;
    203     width_ = width;
    204     height_ = height;
    205   }
    206   OnMediaMetadataChanged(duration_, width_, height_, success);
    207 }
    208 
    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 
    221 void MediaPlayerBridge::Pause() {
    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 
    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 
    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 
    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 
    259 void MediaPlayerBridge::SeekTo(base::TimeDelta time) {
    260   // Record the time to seek when OnMediaPrepared() is called.
    261   pending_seek_ = time;
    262 
    263   if (j_media_player_bridge_.is_null())
    264     Prepare();
    265   else if (prepared_)
    266     SeekInternal(time);
    267 }
    268 
    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 
    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 
    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   ReleaseMediaResourcesFromManager();
    302   listener_.ReleaseMediaPlayerListenerResources();
    303 }
    304 
    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 
    315 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
    316   width_ = width;
    317   height_ = height;
    318   MediaPlayerAndroid::OnVideoSizeChanged(width, height);
    319 }
    320 
    321 void MediaPlayerBridge::OnPlaybackComplete() {
    322   time_update_timer_.Stop();
    323   MediaPlayerAndroid::OnPlaybackComplete();
    324 }
    325 
    326 void MediaPlayerBridge::OnMediaInterrupted() {
    327   time_update_timer_.Stop();
    328   MediaPlayerAndroid::OnMediaInterrupted();
    329 }
    330 
    331 void MediaPlayerBridge::OnMediaPrepared() {
    332   if (j_media_player_bridge_.is_null())
    333     return;
    334 
    335   prepared_ = true;
    336 
    337   base::TimeDelta dur = duration_;
    338   duration_ = GetDuration();
    339 
    340   if (duration_ != dur && 0 != dur.InMilliseconds()) {
    341     // Scale the |pending_seek_| according to the new duration.
    342     pending_seek_ = base::TimeDelta::FromSeconds(
    343         pending_seek_.InSecondsF() * duration_.InSecondsF() / dur.InSecondsF());
    344   }
    345 
    346   // If media player was recovered from a saved state, consume all the pending
    347   // events.
    348   PendingSeekInternal(pending_seek_);
    349 
    350   if (pending_play_) {
    351     StartInternal();
    352     pending_play_ = false;
    353   }
    354 
    355   GetAllowedOperations();
    356   OnMediaMetadataChanged(duration_, width_, height_, true);
    357 }
    358 
    359 void MediaPlayerBridge::GetAllowedOperations() {
    360   JNIEnv* env = base::android::AttachCurrentThread();
    361   CHECK(env);
    362 
    363   ScopedJavaLocalRef<jobject> allowedOperations =
    364       Java_MediaPlayerBridge_getAllowedOperations(
    365           env, j_media_player_bridge_.obj());
    366   can_pause_ = Java_AllowedOperations_canPause(env, allowedOperations.obj());
    367   can_seek_forward_ = Java_AllowedOperations_canSeekForward(
    368       env, allowedOperations.obj());
    369   can_seek_backward_ = Java_AllowedOperations_canSeekBackward(
    370       env, allowedOperations.obj());
    371 }
    372 
    373 void MediaPlayerBridge::StartInternal() {
    374   JNIEnv* env = base::android::AttachCurrentThread();
    375   Java_MediaPlayerBridge_start(env, j_media_player_bridge_.obj());
    376   if (!time_update_timer_.IsRunning()) {
    377     time_update_timer_.Start(
    378         FROM_HERE,
    379         base::TimeDelta::FromMilliseconds(kTimeUpdateInterval),
    380         this, &MediaPlayerBridge::OnTimeUpdated);
    381   }
    382 }
    383 
    384 void MediaPlayerBridge::PauseInternal() {
    385   JNIEnv* env = base::android::AttachCurrentThread();
    386   Java_MediaPlayerBridge_pause(env, j_media_player_bridge_.obj());
    387   time_update_timer_.Stop();
    388 }
    389 
    390 void MediaPlayerBridge::PendingSeekInternal(base::TimeDelta time) {
    391   SeekInternal(time);
    392 }
    393 
    394 void MediaPlayerBridge::SeekInternal(base::TimeDelta time) {
    395   if (time > duration_)
    396     time = duration_;
    397 
    398   // Seeking to an invalid position may cause media player to stuck in an
    399   // error state.
    400   if (time < base::TimeDelta()) {
    401     DCHECK_EQ(-1.0, time.InMillisecondsF());
    402     return;
    403   }
    404 
    405   JNIEnv* env = base::android::AttachCurrentThread();
    406   CHECK(env);
    407 
    408   int time_msec = static_cast<int>(time.InMilliseconds());
    409   Java_MediaPlayerBridge_seekTo(
    410       env, j_media_player_bridge_.obj(), time_msec);
    411 }
    412 
    413 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
    414   bool ret = RegisterNativesImpl(env);
    415   DCHECK(g_MediaPlayerBridge_clazz);
    416   return ret;
    417 }
    418 
    419 bool MediaPlayerBridge::CanPause() {
    420   return can_pause_;
    421 }
    422 
    423 bool MediaPlayerBridge::CanSeekForward() {
    424   return can_seek_forward_;
    425 }
    426 
    427 bool MediaPlayerBridge::CanSeekBackward() {
    428   return can_seek_backward_;
    429 }
    430 
    431 bool MediaPlayerBridge::IsPlayerReady() {
    432   return prepared_;
    433 }
    434 
    435 GURL MediaPlayerBridge::GetUrl() {
    436   return url_;
    437 }
    438 
    439 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
    440   return first_party_for_cookies_;
    441 }
    442 
    443 }  // namespace media
    444