[M120 Migration][MM] Handle live stream duration and currenttime
[platform/framework/web/chromium-efl.git] / tizen_src / chromium_impl / media / filters / media_player_bridge_capi_tv.cc
index 5b79cef..b18eaa3 100644 (file)
@@ -4,16 +4,38 @@
 
 #include "media/filters/media_player_bridge_capi_tv.h"
 
+#include "base/strings/string_split.h"
+#include "media/base/efl/media_player_util_efl.h"
 #include "tizen_src/chromium_impl/media/filters/media_player_tizen_client.h"
 
+#include <jsoncpp/json/json.h>
+
 namespace {
 const int kSeekableTimeUpdateInterval = 500;
 }
 
+static void PlayerPreLoadingCb(void* data) {
+  if (!data) {
+    LOG(ERROR) << "input data is null";
+    return;
+  }
+  media::MediaPlayerBridgeCapiTV* player =
+      static_cast<media::MediaPlayerBridgeCapiTV*>(data);
+  player->OnPlayerPreloading();
+}
+
+static void DrmErrorCb(int err_code, char* err_str, void* data) {
+  DCHECK(data);
+  media::MediaPlayerBridgeCapiTV* player =
+      static_cast<media::MediaPlayerBridgeCapiTV*>(data);
+
+  player->OnDrmError(err_code, err_str);
+}
+
 namespace media {
 
 MediaPlayerBridgeCapiTV::MediaPlayerBridgeCapiTV(const GURL& url,
-                                                 std::string& user_agent,
+                                                 const std::string& user_agent,
                                                  double volume)
     : MediaPlayerBridgeCapi(url, user_agent, volume), weak_factory_(this) {
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
@@ -31,29 +53,124 @@ void MediaPlayerBridgeCapiTV::SetContentMimeType(const std::string& mime_type) {
 }
 
 void MediaPlayerBridgeCapiTV::Prepare() {
+  bool media_resource_acquired = false;
+  std::string translated_url;
+  std::string drm_info;
+
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(
+        kPlaybackLoad, player_id_, url_.spec(), mime_type_,
+        &media_resource_acquired, &translated_url, &drm_info);
+
+  LOG(INFO) << "media_resource_acquired: " << media_resource_acquired
+            << ",translated_url:" << translated_url
+            << ",drm_info: " << drm_info;
+
+  if (mime_type_.find("application/vnd.oipf.contentaccessstreaming+xml") !=
+      std::string::npos) {
+    LOG(INFO) << "CASD url,waiting for hbbtv parse the real url and set "
+                 "translated url";
+    return;
+  }
+
+  if (GetMediaPlayerClient() &&
+      GetMediaPlayerClient()->PlaybackNotificationEnabled() &&
+      blink::IsHbbTV()) {
+    if (!SetDrmInfo(drm_info)) {
+      LOG(ERROR) << "SetDrmInfo failed";
+      return;
+    }
+    if (!translated_url.empty())
+      url_ = media::GetCleanURL(translated_url);
+  }
+
+  if (url_.spec().find(".mpd") != std::string::npos)
+    stream_type_ = DASH_STREAM;
+  else if (url_.spec().find(".m3u") != std::string::npos)
+    stream_type_ = HLS_STREAM;
+  else if (mime_type_.find("application/dash+xml") != std::string::npos) {
+    char steaming_type_dash[] = "DASH";
+    char steaming_type_dash_ex[] = "DASHEX";
+    player_set_streaming_type(
+        player_, blink::IsHbbTV() ? steaming_type_dash_ex : steaming_type_dash);
+    stream_type_ = DASH_STREAM;
+  }
+
+  if (blink::IsHbbTV() && CheckHighBitRate() && stream_type_ == DASH_STREAM)
+    AppendUrlHighBitRate(url_.spec());
+  player_prepared_ = false;
+
+  parental_rating_pass_ = true;
   MediaPlayerBridgeCapi::Prepare();
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
 }
 
 void MediaPlayerBridgeCapiTV::Release() {
+  is_preloaded_ = false;
+  player_prepared_ = false;
   StopSeekableTimeUpdateTimer();
   MediaPlayerBridgeCapi::Release();
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
 }
 
 bool MediaPlayerBridgeCapiTV::Play() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << ",current_time:" << GetCurrentTime().InSecondsF();
+
+  // HBBTV run in preloading and preplay mode. If no prepread do prepare
+  // in PlayerPrePlay(), otherwise do normal Play.
+  if (blink::IsHbbTV() && !player_prepared_) {
+    if (!PlayerPrePlay())
+      LOG(ERROR) << "HBBTV prePlay fail.";
+    return false;
+  }
+
+  if (blink::IsHbbTV() && !parental_rating_pass_) {
+    LOG(INFO) << "parental rating authenticatoin is not pass yet,waiting...";
+    delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
+    MediaPlayerBridgeCapi::ExecuteDelayedPlayerState();
+    return false;
+  }
+
   if (!MediaPlayerBridgeCapi::Play())
     return false;
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStart, player_id_);
   return true;
 }
 
+void MediaPlayerBridgeCapiTV::Pause(bool is_media_related_action) {
+  if (!player_prepared_) {
+    LOG(INFO) << "(" << static_cast<void*>(this)
+              << "), pause while player is not prepared, pause delay";
+    delayed_player_state_ = PLAYER_STATE_DELAYED_PAUSE;
+    return;
+  }
+  MediaPlayerBridgeCapi::Pause(is_media_related_action);
+}
+
 void MediaPlayerBridgeCapiTV::PlaybackCompleteUpdate() {
   MediaPlayerBridgeCapi::PlaybackCompleteUpdate();
+  if (GetMediaPlayerClient()) {
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
+    if (is_live_stream_)
+      GetMediaPlayerClient()->OnLivePlaybackComplete();
+  }
 }  // namespace media
 
 void MediaPlayerBridgeCapiTV::PlayerPrepared() {
+  player_prepared_ = true;
   MediaPlayerBridgeCapi::PlayerPrepared();
 
+  int canSeek = 0;
+  int retVal = player_seek_available(player_, &canSeek);
+  if (retVal == PLAYER_ERROR_NONE && !canSeek)
+    is_player_seek_available_ = false;
+
   GetAdaptiveStreamingInfo();
-  if (/*!blink::IsHbbTV() && */is_live_stream_) {
+  if (!blink::IsHbbTV() && is_live_stream_) {
     UpdateSeekableTime();
     StartSeekableTimeUpdateTimer();
   }
@@ -79,7 +196,7 @@ void MediaPlayerBridgeCapiTV::OnSeekableTimeUpdateTimerFired() {
 void MediaPlayerBridgeCapiTV::GetAdaptiveStreamingInfo() {
   int ret = player_get_adaptive_streaming_info(player_, &is_live_stream_,
                                                PLAYER_ADAPTIVE_INFO_IS_LIVE);
-  if (ret != PLAYER_ERROR_NONE || is_live_stream_) {
+  if (ret == PLAYER_ERROR_NONE && is_live_stream_) {
     LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
               << " A live stream.";
   }
@@ -123,7 +240,7 @@ bool MediaPlayerBridgeCapiTV::GetLiveStreamingDuration(int64_t* min,
   };
   char* live_ptr = live_duration;
   const auto err = player_get_adaptive_streaming_info(
-      player_, live_duration, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
+      player_, &live_ptr, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
   if (err != PLAYER_ERROR_NONE) {
     LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
                << " |player_get_adaptive_streaming_info| failed " << err;
@@ -172,4 +289,379 @@ bool MediaPlayerBridgeCapiTV::GetLiveStreamingDuration(int64_t* min,
   return true;
 }
 
+void MediaPlayerBridgeCapiTV::ParseDashInfo() {
+  std::string dash_info = GetDashInfo();
+  Json::CharReaderBuilder builder;
+  Json::CharReader* reader(builder.newCharReader());
+  Json::Value value;
+
+  if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
+                    &value, nullptr)) {
+    mrs_url_ = value["mrsUrl"].asString();
+    std::string periodId = value["periodId"].asString();
+    if (periodId != "")
+      content_id_ = clean_url_ + "#period=" + periodId;
+  }
+  LOG(INFO) << "mrsUrl: " << mrs_url_ << ",contentId: " << content_id_;
+}
+
+std::string MediaPlayerBridgeCapiTV::GetDashInfo() {
+  char* dash_info = nullptr;
+  // dash_info format:
+  // {"type":0,"minBufferTime":4000,"mrsUrl":"","periodId":"first"}
+  const int ret = player_get_dash_info(player_, &dash_info);
+  if (ret != PLAYER_ERROR_NONE) {
+    if (dash_info)
+      free(dash_info);
+    LOG(ERROR) << "Fail to call player_get_dash_info,ret:" << ret;
+    return "";
+  }
+
+  if (!dash_info) {
+    LOG(ERROR) << "empty dash_info.";
+    return "";
+  }
+
+  const std::string info(dash_info);
+  free(dash_info);
+  LOG(INFO) << "dash info str: " << info;
+
+  return info;
+}
+
+bool MediaPlayerBridgeCapiTV::GetDashLiveDuration(int64_t* duration) {
+  DCHECK(duration);
+  std::string dash_info = GetDashInfo();
+  Json::CharReaderBuilder builder;
+  Json::CharReader* reader(builder.newCharReader());
+  Json::Value value;
+  int64_t out_duration = 0;
+
+  if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
+                    &value, nullptr)) {
+    out_duration = value["mediaPresentationDuration"].asInt();
+  }
+
+  // According to dash spec, if duration == -1, set max time as duration.
+  if (out_duration == -1)
+    out_duration = base::TimeDelta::Max().InMilliseconds();
+  *duration = out_duration;
+
+  return true;
+}
+
+void MediaPlayerBridgeCapiTV::AppendUrlHighBitRate(const std::string& url) {
+  // don't use url.append("|STARTBITRATE=HIGHEST") to get hbbtv url
+  // "|" will be replaced with "%7C"
+  const char high_bitrate[] = "|STARTBITRATE=HIGHEST";
+  int len = url.length() + strlen(high_bitrate) + 1;
+  char* str_url = new char[len];
+  if (!str_url) {
+    return;
+  }
+
+  memset(str_url, 0, len);
+  strncat(str_url, url.c_str(), url.length());
+  strncat(str_url, high_bitrate, strlen(high_bitrate));
+  hbbtv_url_ = str_url;
+  BLINKFREE(str_url);
+  LOG(INFO) << "hbbtv url:" << hbbtv_url_.c_str();
+}
+
+bool MediaPlayerBridgeCapiTV::CheckHighBitRate() {
+  if (!GetMediaPlayerClient()) {
+    LOG(ERROR) << "MediaPlayerClient is null";
+    return false;
+  }
+  content::WebContentsDelegate* web_contents_delegate =
+      GetMediaPlayerClient()->GetWebContentsDelegate();
+  if (!web_contents_delegate) {
+    LOG(ERROR) << "get web_contents_delegate fail";
+    return false;
+  }
+  bool ret = web_contents_delegate->IsHighBitRate();
+  LOG(INFO) << "get high bit rate: " << std::boolalpha << ret;
+  return ret;
+}
+
+void MediaPlayerBridgeCapiTV::HandleDownloadableFontInfo(
+    const std::string& scheme_id_uri,
+    const std::string& value,
+    const std::string& data,
+    int type) {
+  content::WebContentsDelegate* web_contents_delegate =
+      GetMediaPlayerClient()->GetWebContentsDelegate();
+  if (!web_contents_delegate)
+    return;
+  web_contents_delegate->NotifyDownloadableFontInfo(scheme_id_uri, value, data,
+                                                    type);
+}
+
+bool MediaPlayerBridgeCapiTV::PreloadIfNeeded(int& ret) {
+  LOG(INFO) << "PreloadIfNeeded, is_preloaded_ " << is_preloaded_;
+  if (blink::IsHbbTV() && !is_preloaded_) {
+    ret = player_preloading_async(player_, -1, PlayerPreLoadingCb, this);
+    return true;
+  }
+
+  // fixup : kPlaybackReady status need checked by HBBTVResourceAcquired() patch
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
+  else
+    LOG(ERROR) << "(" << static_cast<void*>(this)
+               << "), GetMediaPlayerClient return null";
+  SetDisplayAtPausedState();
+  return false;
+}
+
+void MediaPlayerBridgeCapiTV::SetDisplayAtPausedState() {
+  int ret = PLAYER_ERROR_NONE;
+  if (!pending_seek_duration_.is_zero())
+    ret = player_display_video_at_paused_state(player_, false);
+  else
+    ret = player_display_video_at_paused_state(player_, true);
+
+  if (ret != PLAYER_ERROR_NONE)
+    LOG(ERROR) << "player_display_video_at_paused_state() failed";
+}
+
+void MediaPlayerBridgeCapiTV::UpdateDuration() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  if (blink::IsHbbTV() && !is_preloaded_) {
+    LOG(INFO) << "HBBTV preload not finished, no need update duration. ";
+    return;
+  }
+  if (is_live_stream_) {
+    int64_t duration = 0;
+    if (stream_type_ == HLS_STREAM) {
+      int64_t min = 0;
+      if (!GetLiveStreamingDuration(&min, &duration)) {
+        LOG(ERROR) << "Fail to get duration for hls live stream.";
+        return;
+      }
+    } else if (stream_type_ == DASH_STREAM) {
+      if (!GetDashLiveDuration(&duration)) {
+        if (duration_ != base::TimeDelta::Max()) {
+          duration_ = base::TimeDelta::Max();
+          OnDurationChange(player_id_, duration_.InSecondsF());
+        }
+        return;
+      }
+    } else {
+      LOG(ERROR) << "Unknown live stream type : " << stream_type_;
+    }
+
+    if (duration == 0) {
+      LOG(WARNING) << "live stream type : convert duration to max";
+      duration_ = base::TimeDelta::Max();
+      OnDurationChange(player_id_, duration_.InSecondsF());
+    } else if (duration_.InSecondsF() !=
+               ConvertMilliSecondsToSeconds(duration)) {
+      duration_ = base::Milliseconds(duration);
+      OnDurationChange(player_id_, duration_.InSecondsF());
+    }
+  } else {  // non-live stream sequence
+    MediaPlayerBridgeCapi::UpdateDuration();
+  }
+}
+
+void MediaPlayerBridgeCapiTV::UpdateMediaType() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+
+  if (blink::IsHbbTV() && !is_preloaded_) {
+    LOG(INFO) << "HBBTV preload not finished, no need update media type. ";
+    return;
+  }
+  MediaPlayerBridgeCapi::UpdateMediaType();
+}
+
+bool MediaPlayerBridgeCapiTV::CheckLiveStreaming() const {
+  int isLive = 0;
+  int retVal = player_get_adaptive_streaming_info(player_, &isLive,
+                                                  PLAYER_ADAPTIVE_INFO_IS_LIVE);
+  if (retVal != PLAYER_ERROR_NONE) {
+    LOG(ERROR) << "player_get_adaptive_streaming_info failed, error: "
+               << retVal;
+    return false;
+  }
+
+  if (!isLive)
+    return false;
+
+  LOG(INFO) << "This is a live streaming.";
+  return true;
+}
+
+void MediaPlayerBridgeCapiTV::OnPlayerPreloading() {
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&MediaPlayerBridgeCapiTV::PlayerPreloaded,
+                                weak_factory_.GetWeakPtr()));
+}
+
+bool MediaPlayerBridgeCapiTV::HBBTVResourceAcquired() {
+  bool media_resource_acquired = false;
+  if (!GetMediaPlayerClient()) {
+    LOG(ERROR) << "MediaPlayerTizenClient is nullptr";
+    return false;
+  }
+
+  GetMediaPlayerClient()->NotifyPlaybackState(
+      kPlaybackReady, player_id_, url_.spec(), mime_type_,
+      &media_resource_acquired, NULL, NULL);
+  if (!media_resource_acquired) {
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
+    LOG(ERROR) << "HBBTV media resource acquired failed";
+  }
+  return media_resource_acquired;
+}
+
+void MediaPlayerBridgeCapiTV::PlayerPreloaded() {
+  LOG(INFO) << "PlayerPreloaded,this: " << this
+            << ",player_prepared_:" << player_prepared_;
+
+  is_preloaded_ = true;
+  is_live_stream_ = CheckLiveStreaming();
+  if (stream_type_ == DASH_STREAM)
+    ParseDashInfo();
+  if (is_live_stream_ && stream_type_ != OTHER_STREAM) {
+    // UpdateSeekableTime();
+    // StartSeekableTimeUpdateTimer();
+  }
+  // UpdateAudioTrackInfo();
+  // UpdateVideoTrackInfo();
+  // UpdateTextTrackInfo();
+  UpdateDuration();
+  UpdateMediaType();
+  if (GetMediaType() == MediaType::Invalid) {
+    LOG(ERROR) << "Media type is not valid!";
+    return;
+  }
+
+  OnReadyStateChange(player_id_,
+                     blink::WebMediaPlayer::kReadyStateHaveFutureData);
+  OnNetworkStateChange(player_id_, blink::WebMediaPlayer::kNetworkStateLoading);
+
+  if (!player_prepared_ && HBBTVResourceAcquired()) {
+    int ret = SetPlayerPrepareAsync();
+    if (ret != PLAYER_ERROR_NONE) {
+      OnHandlePlayerError(ret, FROM_HERE);
+    }
+  }
+}
+
+bool MediaPlayerBridgeCapiTV::PlayerPrePlay() {
+  LOG(INFO) << "PlayerPrePlay, this: " << this
+            << ",is_player_prepared : " << player_prepared_;
+  if (IsPlayerSuspended()) {
+    LOG(INFO) << "PlayerPrePlay while player is suspended";
+    return false;
+  }
+
+  if (!HBBTVResourceAcquired()) {
+    LOG(INFO)
+        << "PlayerPrePlay while it's not in case of HBBTV Resource Acquired";
+    return false;
+  }
+
+  delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
+  LOG(INFO) << "begin to |player_prepare_async|";
+
+  SetDisplayAtPausedState();
+  int ret = SetPlayerPrepareAsync();
+  if (ret != PLAYER_ERROR_NONE) {
+    OnHandlePlayerError(ret, FROM_HERE);
+    return false;
+  }
+
+  return true;
+}
+void MediaPlayerBridgeCapiTV::Initialize(VideoRendererSink* sink) {
+  player_set_drm_error_cb(player_, DrmErrorCb, this);
+  MediaPlayerBridgeCapi::Initialize(sink);
+}
+
+bool MediaPlayerBridgeCapiTV::SetDrmInfo(std::string& drm_info) {
+  // The DRM info from HbbTV comes as a JSON-like string in the following
+  // format:
+  // pair = Property ':' Value
+  // string = pair {',' pair}
+  std::vector<std::string> drm_info_pairs = base::SplitStringUsingSubstr(
+      drm_info, ", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (auto it = drm_info_pairs.begin(); it != drm_info_pairs.end(); ++it) {
+    std::vector<std::string> drm_info_pair = base::SplitStringUsingSubstr(
+        *it, ": ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+    if (drm_info_pair.size() != 2)
+      continue;
+
+    std::string trim_key = drm_info_pair.at(0);
+    std::string trim_value = drm_info_pair.at(1);
+    LOG(INFO) << "trim_key: " << trim_key.c_str()
+              << ",trim_value: " << trim_value.c_str();
+
+    if (!SetMediaDRMInfo(trim_key, trim_value)) {
+      LOG(ERROR)
+          << "MediaPlayerBridgeCapiTV::SetDrmInfo SetMediaDRMInfo failed";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool MediaPlayerBridgeCapiTV::SetMediaDRMInfo(const std::string& type_data,
+                                              const std::string& value_data) {
+  LOG(INFO) << "player_set_drm_info(type_length(" << type_data.length() << ") "
+            << " value_length(" << value_data.length() << ")) ";
+  int ret = PLAYER_ERROR_INVALID_OPERATION;
+  const void* type_data_ptr = type_data.c_str();
+  const void* value_data_ptr = value_data.c_str();
+  player_drm_type_e drm_type = PLAYER_DRM_TYPE_PLAYREADY;
+  ret = player_set_drm_info(player_, type_data_ptr, type_data.length(),
+                            value_data_ptr, value_data.length(), drm_type);
+  if (ret != PLAYER_ERROR_NONE) {
+    MediaPlayerBridgeCapi::OnHandlePlayerError(ret, FROM_HERE);
+    return false;
+  }
+
+  return true;
+}
+
+void MediaPlayerBridgeCapiTV::OnDrmError(int err_code, char* err_str) {
+  LOG(ERROR) << "OnDrmError err_str[" << err_str << "]";
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MediaPlayerBridgeCapiTV::OnDrmError,
+                       weak_factory_.GetWeakPtr(), err_code, err_str));
+  }
+  MediaPlayerBridgeCapi::OnHandlePlayerError(err_code, FROM_HERE);
+}
+
+void MediaPlayerBridgeCapiTV::HandleParentalRatingInfo(const std::string& info,
+                                                       const std::string& url) {
+  content::WebContentsDelegate* web_contents_delegate =
+      GetMediaPlayerClient()->GetWebContentsDelegate();
+  if (!web_contents_delegate){
+    LOG(ERROR) << "web_contents_delegate is null";
+    return;
+  }
+  web_contents_delegate->NotifyParentalRatingInfo(info, url);
+}
+
+void MediaPlayerBridgeCapiTV::SetParentalRatingResult(bool is_pass) {
+  LOG(INFO) << "ParentalRatingResult:" << std::boolalpha << is_pass;
+  parental_rating_pass_ = is_pass;
+
+  // if authentication fail, raise MEDIA_ERROR_DECODE
+  if (!parental_rating_pass_) {
+    OnHandlePlayerError(PLAYER_ERROR_INVALID_OPERATION, FROM_HERE);
+    return;
+  }
+
+  if (player_prepared_) {
+    LOG(INFO) << "player already prepared,execute play";
+    ExecuteDelayedPlayerState();
+  }
+}
 }  // namespace media