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_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 
     19 using base::android::ConvertUTF8ToJavaString;
     20 using base::android::ScopedJavaLocalRef;
     21 
     22 namespace media {
     23 
     24 MediaPlayerBridge::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 
     55 MediaPlayerBridge::~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 
     64 void 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 
     94 void 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 
    107 void 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 
    115 base::android::ScopedJavaLocalRef<jobject> MediaPlayerBridge::
    116     GetJavaMediaPlayerBridge() {
    117   base::android::ScopedJavaLocalRef<jobject> j_bridge(
    118       j_media_player_bridge_);
    119   return j_bridge;
    120 }
    121 
    122 void MediaPlayerBridge::SetDuration(base::TimeDelta duration) {
    123   duration_ = duration;
    124 }
    125 
    126 void 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 
    139 void 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 
    153 void 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 
    204 bool 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 
    223 void 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 
    235 void 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 
    243 void 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 
    255 void 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 
    277 void 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 
    288 void 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 
    300 void 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 
    311 bool 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 
    322 int 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 
    330 int 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 
    338 void 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 
    349 base::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 
    358 base::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 
    367 void 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 
    386 void 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 
    398 void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
    399   width_ = width;
    400   height_ = height;
    401   MediaPlayerAndroid::OnVideoSizeChanged(width, height);
    402 }
    403 
    404 void MediaPlayerBridge::OnPlaybackComplete() {
    405   time_update_timer_.Stop();
    406   MediaPlayerAndroid::OnPlaybackComplete();
    407 }
    408 
    409 void MediaPlayerBridge::OnMediaInterrupted() {
    410   time_update_timer_.Stop();
    411   MediaPlayerAndroid::OnMediaInterrupted();
    412 }
    413 
    414 void 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 
    439 ScopedJavaLocalRef<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 
    447 void 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 
    460 void 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 
    471 void 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 
    477 void MediaPlayerBridge::PendingSeekInternal(const base::TimeDelta& time) {
    478   SeekInternal(time);
    479 }
    480 
    481 void 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 
    499 void MediaPlayerBridge::OnTimeUpdateTimerFired() {
    500   manager()->OnTimeUpdate(
    501       player_id(), GetCurrentTime(), base::TimeTicks::Now());
    502 }
    503 
    504 bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) {
    505   bool ret = RegisterNativesImpl(env);
    506   DCHECK(g_MediaPlayerBridge_clazz);
    507   return ret;
    508 }
    509 
    510 bool MediaPlayerBridge::CanPause() {
    511   return can_pause_;
    512 }
    513 
    514 bool MediaPlayerBridge::CanSeekForward() {
    515   return can_seek_forward_;
    516 }
    517 
    518 bool MediaPlayerBridge::CanSeekBackward() {
    519   return can_seek_backward_;
    520 }
    521 
    522 bool MediaPlayerBridge::IsPlayerReady() {
    523   return prepared_;
    524 }
    525 
    526 GURL MediaPlayerBridge::GetUrl() {
    527   return url_;
    528 }
    529 
    530 GURL MediaPlayerBridge::GetFirstPartyForCookies() {
    531   return first_party_for_cookies_;
    532 }
    533 
    534 }  // namespace media
    535