[M120 Migration][MM] Handle live stream duration and currenttime 83/307683/3
authorzhishun.zhou <zhishun.zhou@samsung.com>
Tue, 12 Mar 2024 07:39:02 +0000 (15:39 +0800)
committerYanqing Lu <yanqing.lu@samsung.com>
Tue, 12 Mar 2024 09:17:44 +0000 (09:17 +0000)
1. Because player_get_duration return a 0 duration value for live streams,
   special handling procedures are needed:
     For HLS stream, use player_get_adaptive_streaming_info;
     For Dash stream, use player_get_dash_info.
2. For live stream cases, in PlaybackCompleteCb, emit a ended event to media
   element to mark live playback completed.

Patch from:
https://review.tizen.org/gerrit/#/c/293843/

Change-Id: Ib307ddf121b974785cf1dc9923d2f741cd715b1f
Signed-off-by: xiaofang <fang.xiao@samsung.com>
Signed-off-by: zhishun.zhou <zhishun.zhou@samsung.com>
22 files changed:
media/base/pipeline.h
media/base/pipeline_impl.cc
media/base/pipeline_impl.h
media/base/renderer_client.h
media/mojo/clients/mojo_renderer.cc
media/mojo/clients/mojo_renderer.h
media/mojo/mojom/renderer.mojom
media/mojo/services/mojo_renderer_service.cc
media/mojo/services/mojo_renderer_service.h
third_party/blink/public/platform/web_media_player.h
third_party/blink/public/platform/web_media_player_client.h
third_party/blink/renderer/core/html/media/html_media_element.cc
third_party/blink/renderer/core/html/media/html_media_element.h
third_party/blink/renderer/platform/media/web_media_player_impl.cc
third_party/blink/renderer/platform/media/web_media_player_impl.h
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h
tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc
tizen_src/chromium_impl/media/filters/media_player_bridge_capi.h
tizen_src/chromium_impl/media/filters/media_player_bridge_capi_tv.cc
tizen_src/chromium_impl/media/filters/media_player_bridge_capi_tv.h
tizen_src/chromium_impl/media/filters/media_player_tizen_client.h

index aba0a73..62f5dae 100644 (file)
@@ -94,6 +94,7 @@ class MEDIA_EXPORT Pipeline {
     virtual void OnSeekableTimeChange(base::TimeDelta min_time,
                                       base::TimeDelta max_time,
                                       bool is_live) = 0;
+    virtual void OnLivePlaybackComplete() = 0;
 #endif
   };
 
index 747ff15..e707a0d 100644 (file)
@@ -199,6 +199,7 @@ class PipelineImpl::RendererWrapper final : public DemuxerHost,
   void OnSeekableTimeChange(base::TimeDelta min_time,
                             base::TimeDelta max_time,
                             bool is_live) final;
+  void OnLivePlaybackComplete() final;
 #endif
 
   // Common handlers for notifications from renderers and demuxer.
@@ -1141,6 +1142,12 @@ void PipelineImpl::RendererWrapper::OnSeekableTimeChange(
       FROM_HERE, base::BindOnce(&PipelineImpl::OnSeekableTimeChange,
                                 weak_pipeline_, min_time, max_time, is_live));
 }
+
+void PipelineImpl::RendererWrapper::OnLivePlaybackComplete() {
+  main_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PipelineImpl::OnLivePlaybackComplete, weak_pipeline_));
+}
 #endif
 
 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
@@ -2043,6 +2050,11 @@ void PipelineImpl::OnSeekableTimeChange(base::TimeDelta min_time,
   DVLOG(3) << __func__;
   client_->OnSeekableTimeChange(min_time, max_time, is_live);
 }
+
+void PipelineImpl::OnLivePlaybackComplete() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  client_->OnLivePlaybackComplete();
+}
 #endif
 
 void PipelineImpl::OnSeekDone(bool is_suspended) {
index 93a56b6..d49ba75 100644 (file)
@@ -196,6 +196,7 @@ class MEDIA_EXPORT PipelineImpl : public Pipeline {
   void OnSeekableTimeChange(base::TimeDelta min_time,
                             base::TimeDelta max_time,
                             bool is_live);
+  void OnLivePlaybackComplete();
 #endif
 
   // Task completion callbacks from RendererWrapper.
index 4d6be27..f53f7e6 100644 (file)
@@ -75,6 +75,8 @@ class MEDIA_EXPORT RendererClient {
   virtual void OnRequestSuspend(bool resource_conflicted) {}
 
   virtual void OnRequestSeek(base::TimeDelta time) {}
+
+  virtual void OnLivePlaybackComplete() {}
 #endif
 };
 
index 7fec3cc..da89779 100644 (file)
@@ -471,6 +471,11 @@ void MojoRenderer::OnSeekCompleted() {
 
   std::move(seek_cb_).Run();
 }
+
+void MojoRenderer::OnLivePlaybackComplete() {
+  DVLOG(1) << __func__;
+  client_->OnLivePlaybackComplete();
+}
 #endif
 
 void MojoRenderer::OnCdmAttached(bool success) {
index ba357a9..bb3ca96 100644 (file)
@@ -102,6 +102,7 @@ class MojoRenderer : public Renderer, public mojom::RendererClient {
   void OnSeekableTimeChange(base::TimeDelta min_time,
                             base::TimeDelta max_time,
                             bool is_live) override;
+  void OnLivePlaybackComplete() override;
 #endif
 
   // Binds |remote_renderer_| to the mojo message pipe. Can be called multiple
index 204cb3b..e1342e6 100644 (file)
@@ -129,4 +129,7 @@ interface RendererClient {
   OnSeekableTimeChange(mojo_base.mojom.TimeDelta min_time,
                        mojo_base.mojom.TimeDelta max_time,
                        bool is_live);
+
+  [EnableIf=tizen_multimedia]
+  OnLivePlaybackComplete();
 };
index dc315f6..9cf82ee 100644 (file)
@@ -277,6 +277,11 @@ void MojoRendererService::OnSeekableTimeChange(base::TimeDelta min_time,
            << "is_live " << is_live;
   client_->OnSeekableTimeChange(min_time, max_time, is_live);
 }
+
+void MojoRendererService::OnLivePlaybackComplete() {
+  DVLOG(2) << __func__;
+  client_->OnLivePlaybackComplete();
+}
 #endif
 
 void MojoRendererService::OnVideoFrameRateChange(absl::optional<int> fps) {
index 5b6e16b..4088111 100644 (file)
@@ -115,6 +115,7 @@ class MEDIA_MOJO_EXPORT MojoRendererService final : public mojom::Renderer,
   void OnSeekableTimeChange(base::TimeDelta min_time,
                             base::TimeDelta max_time,
                             bool is_live) final;
+  void OnLivePlaybackComplete() final;
 #endif
 
   // Called when the MediaResourceShim is ready to go (has a config,
index 7bc042b..645766f 100644 (file)
@@ -185,6 +185,7 @@ class WebMediaPlayer {
   virtual void Suspend() {}
   virtual void Resume() {}
   virtual bool SuspendedByPlayer() { return false; }
+  virtual void OnLivePlaybackComplete() {}
 #endif
 
   // Called when the backing media element and the page it is attached to is
index 3a24a57..21830e6 100644 (file)
@@ -225,6 +225,7 @@ class BLINK_PLATFORM_EXPORT WebMediaPlayerClient {
 
 #if defined(TIZEN_MULTIMEDIA)
   virtual void SuspendPlayer() {}
+  virtual void OnLivePlaybackComplete() = 0;
 #endif
 
 #if BUILDFLAG(IS_TIZEN_TV)
index 3c081e3..d4eb2eb 100644 (file)
@@ -503,6 +503,9 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tag_name,
       is_deactivate_(false),
       is_translated_url_(false),
 #endif
+#if defined(TIZEN_MULTIMEDIA)
+      live_playback_complete_(false),
+#endif
       audio_tracks_(MakeGarbageCollected<AudioTrackList>(*this)),
       video_tracks_(MakeGarbageCollected<VideoTrackList>(*this)),
       audio_source_node_(nullptr),
@@ -3701,6 +3704,10 @@ void HTMLMediaElement::TimeChanged() {
     }
   }
   UpdatePlayState();
+
+#if defined(TIZEN_MULTIMEDIA)
+  live_playback_complete_ = false;
+#endif
 }
 
 void HTMLMediaElement::DurationChanged() {
@@ -4915,6 +4922,10 @@ void HTMLMediaElement::SuspendPlayer() {
   SetShouldDelayLoadEvent(false);
   SetNetworkState(kNetworkIdle);
 }
+
+void HTMLMediaElement::OnLivePlaybackComplete() {
+  live_playback_complete_ = true;
+}
 #endif
 
 media::mojom::blink::MediaPlayerHost&
index 45db0e5..da0d0ab 100644 (file)
@@ -389,6 +389,10 @@ class CORE_EXPORT HTMLMediaElement
   void SetParentalRatingResult(bool is_pass) override;
 #endif
 
+#if defined(TIZEN_MULTIMEDIA)
+  void OnLivePlaybackComplete() override;
+#endif
+
   bool HasMediaSource() const { return media_source_attachment_.get(); }
 
   // Return true if element is paused and won't resume automatically if it
index 78775f6..e568676 100644 (file)
@@ -2584,6 +2584,15 @@ void WebMediaPlayerImpl::OnSeekableTimeChange(base::TimeDelta min_time,
   max_seekable_time_ = max_time;
 }
 
+void WebMediaPlayerImpl::OnLivePlaybackComplete() {
+  LOG(INFO) << __func__;
+  if (!client_) {
+    LOG(ERROR) << __func__ << ", client is null";
+    return;
+  }
+  client_->OnLivePlaybackComplete();
+}
+
 void WebMediaPlayerImpl::OnRequestSuspend(bool resource_conflicted) {
   if (pipeline_controller_->IsSuspended()) {
     LOG(INFO) << __func__ << " Already suspended.";
index a7041f9..3340d55 100644 (file)
@@ -424,6 +424,7 @@ class PLATFORM_EXPORT WebMediaPlayerImpl
   void OnSeekableTimeChange(base::TimeDelta min_time,
                             base::TimeDelta max_time,
                             bool is_live) override;
+  void OnLivePlaybackComplete() override;
 
   // Called if a player in the browser process is suspended.
   void OnRequestSuspend(bool resource_conflicted) override;
index 7c4cbbe..5d58dd6 100644 (file)
@@ -528,6 +528,14 @@ void TizenRendererImpl::NotifyPlaybackState(int state,
                                              media_resource_acquired,
                                              translated_url, drm_info);
 }
+
+void TizenRendererImpl::OnLivePlaybackComplete() {
+  if (!client_) {
+    LOG(ERROR) << "client is not exist";
+    return;
+  }
+  client_->OnLivePlaybackComplete();
+}
 #endif
 
 void TizenRendererImpl::OnSeekableTimeChange(base::TimeDelta min_time,
index 2fac96d..5e50009 100644 (file)
@@ -124,6 +124,7 @@ class CONTENT_EXPORT TizenRendererImpl
                            bool* media_resource_acquired = NULL,
                            std::string* translated_url = NULL,
                            std::string* drm_info = NULL) override;
+  void OnLivePlaybackComplete() override;
 #endif
 
 #if defined(TIZEN_TBM_SUPPORT)
index 837352d..fe43bc2 100644 (file)
@@ -626,7 +626,7 @@ base::TimeDelta MediaPlayerBridgeCapi::GetCurrentTime() {
   if (is_end_reached_) {
     if (playback_rate_ < 0)
       return base::TimeDelta();
-    if (duration_.InSecondsF())
+    if (!is_live_stream_ && duration_.InSecondsF())
       return duration_;
   }
 
@@ -654,6 +654,11 @@ void MediaPlayerBridgeCapi::StopCurrentTimeUpdateTimer() {
 }
 
 void MediaPlayerBridgeCapi::OnBufferingUpdateTimerFired() {
+  /* player_get_streaming_download_progress is not apply for live stream
+     here no need triggered*/
+  if (is_live_stream_)
+    return;
+
   int start = 0, current = 0;
   if (player_get_streaming_download_progress(player_, &start, &current) ==
       PLAYER_ERROR_NONE) {
index d1d0bf1..4f408b5 100644 (file)
@@ -147,6 +147,7 @@ class MEDIA_EXPORT MediaPlayerBridgeCapi : public MediaPlayerTizen {
   virtual void UpdateDuration();
 
   GURL url_;
+  bool is_live_stream_{false};
   int player_id_ = 0;
   int delayed_player_state_;
   player_h player_ = nullptr;
index 94c898b..b18eaa3 100644 (file)
@@ -8,6 +8,8 @@
 #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;
 }
@@ -151,8 +153,11 @@ void MediaPlayerBridgeCapiTV::Pause(bool is_media_related_action) {
 
 void MediaPlayerBridgeCapiTV::PlaybackCompleteUpdate() {
   MediaPlayerBridgeCapi::PlaybackCompleteUpdate();
-  if (GetMediaPlayerClient())
+  if (GetMediaPlayerClient()) {
     GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
+    if (is_live_stream_)
+      GetMediaPlayerClient()->OnLivePlaybackComplete();
+  }
 }  // namespace media
 
 void MediaPlayerBridgeCapiTV::PlayerPrepared() {
@@ -191,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.";
   }
@@ -235,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;
@@ -284,6 +289,67 @@ 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"
@@ -365,7 +431,38 @@ void MediaPlayerBridgeCapiTV::UpdateDuration() {
     LOG(INFO) << "HBBTV preload not finished, no need update duration. ";
     return;
   }
-  MediaPlayerBridgeCapi::UpdateDuration();
+  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() {
@@ -424,8 +521,8 @@ void MediaPlayerBridgeCapiTV::PlayerPreloaded() {
 
   is_preloaded_ = true;
   is_live_stream_ = CheckLiveStreaming();
-  // if (stream_type_ == DASH_STREAM)
-  //  ParseDashInfo();
+  if (stream_type_ == DASH_STREAM)
+    ParseDashInfo();
   if (is_live_stream_ && stream_type_ != OTHER_STREAM) {
     // UpdateSeekableTime();
     // StartSeekableTimeUpdateTimer();
index 228deca..702a098 100644 (file)
@@ -67,12 +67,18 @@ class MEDIA_EXPORT MediaPlayerBridgeCapiTV : public MediaPlayerBridgeCapi {
   void OnSeekableTimeUpdateTimerFired();
   void GetAdaptiveStreamingInfo();
   void UpdateSeekableTime();
+  void ParseDashInfo();
   bool GetLiveStreamingDuration(int64_t* min, int64_t* max);
+  bool GetDashLiveDuration(int64_t* duration);
+  std::string GetDashInfo();
 
   StreamType stream_type_{OTHER_STREAM};
   std::string hbbtv_url_{""};  // url_ + HIGHBITRATE(if mpd)
   std::string mime_type_ = "";
-  bool is_live_stream_ = false;
+  std::string clean_url_{""};  // Original url without suffix t/period
+  std::string mrs_url_{""};
+  std::string content_id_{""};  // clean_url + period,only report to APP
+
   bool parental_rating_pass_{false};
 
   base::TimeDelta min_seekable_time_{base::TimeDelta()};
index 95078d0..9b21291 100644 (file)
@@ -63,6 +63,7 @@ class MEDIA_EXPORT MediaPlayerTizenClient {
                                    bool* media_resource_acquired = NULL,
                                    std::string* translated_url = NULL,
                                    std::string* drm_info = NULL) = 0;
+  virtual void OnLivePlaybackComplete() = 0;
   virtual content::WebContentsDelegate* GetWebContentsDelegate() const = 0;
 #endif
 };