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 "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()) {
     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
    541