[M108 Migration][MM] Manage multi-players using resource conflict callback. 85/289885/2
authorSuhaspoornachandra <s.poornachan@samsung.com>
Wed, 15 Mar 2023 06:04:12 +0000 (11:34 +0530)
committerDae-Hyun Ko <dhyuna.ko@samsung.com>
Thu, 16 Mar 2023 00:17:59 +0000 (00:17 +0000)
Because of hw decoder capacity, only one player can be played on the web page
including multi-players.
It is not known which player will play after web page loading.
Thereafter, the player requested by the user plays, and the other player
is suspended.

- Players who receive the ResourceConflict callback from Resource Manager
  should be suspended.
- Seek is pended while reconfiguring pipeline during resume.
  After prepare, request demuxer seek to operate the seek normally.

Reference:
https://review.tizen.org/gerrit/283009

Change-Id: I4686502216cc1ba266f0f95d01baf7a9a5abc757
Signed-off-by: Sun-woo Nam <sunny.nam@samsung.com>
Signed-off-by: Suhaspoornachandra <s.poornachan@samsung.com>
18 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/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_esplusplayer.cc
tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h
tizen_src/chromium_impl/media/filters/media_player_registry.cc
tizen_src/chromium_impl/media/filters/media_player_registry.h
tizen_src/chromium_impl/media/filters/media_player_tizen.h

index 83a965e..34602a6 100644 (file)
@@ -95,6 +95,10 @@ class MEDIA_EXPORT Pipeline {
     // the frame rate is unstable.  The duration used for the frame rate is
     // based on wall clock time, not media time.
     virtual void OnVideoFrameRateChange(absl::optional<int> fps) = 0;
+
+#if defined(TIZEN_MULTIMEDIA)
+    virtual void OnResourceConflict() = 0;
+#endif
   };
 
   virtual ~Pipeline() {}
index f60b744..7bb45f9 100644 (file)
@@ -170,6 +170,10 @@ class PipelineImpl::RendererWrapper final : public DemuxerHost,
   void OnVideoNaturalSizeChange(const gfx::Size& size) final;
   void OnVideoOpacityChange(bool opaque) final;
   void OnVideoFrameRateChange(absl::optional<int> fps) final;
+#if defined(TIZEN_MULTIMEDIA)
+  void OnResourceConflict() final;
+  void OnRequestSeek(base::TimeDelta time) final;
+#endif
 
   // Common handlers for notifications from renderers and demuxer.
   void OnPipelineError(PipelineStatus error);
@@ -962,6 +966,36 @@ void PipelineImpl::RendererWrapper::OnVideoConfigChange(
                                              weak_pipeline_, config));
 }
 
+#if defined(TIZEN_MULTIMEDIA)
+void PipelineImpl::RendererWrapper::OnResourceConflict() {
+  LOG(INFO) << __func__;
+  main_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PipelineImpl::OnResourceConflict, weak_pipeline_));
+}
+
+void PipelineImpl::RendererWrapper::OnRequestSeek(base::TimeDelta time) {
+  LOG(INFO) << __func__ << " time:" << time;
+
+  if (!media_task_runner_->BelongsToCurrentThread()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&media::PipelineImpl::RendererWrapper::OnRequestSeek,
+                       weak_factory_.GetWeakPtr(), time));
+  }
+
+  SetState(kSeeking);
+
+  SerialRunner::Queue bound_fns;
+  demuxer_->AbortPendingReads();
+  bound_fns.Push(
+      base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_), time));
+  pending_callbacks_ = SerialRunner::Run(
+      std::move(bound_fns), base::BindOnce(&RendererWrapper::CompleteSeek,
+                                           weak_factory_.GetWeakPtr(), time));
+}
+#endif
+
 void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
   DCHECK(!error.is_ok()) << "PIPELINE_OK isn't an error!";
@@ -1768,6 +1802,13 @@ void PipelineImpl::OnVideoPipelineInfoChange(const VideoPipelineInfo& info) {
   client_->OnVideoPipelineInfoChange(info);
 }
 
+#if defined(TIZEN_MULTIMEDIA)
+void PipelineImpl::OnResourceConflict() {
+  DVLOG(3) << __func__;
+  client_->OnResourceConflict();
+}
+#endif
+
 void PipelineImpl::OnSeekDone(bool is_suspended) {
   DVLOG(3) << __func__ << ": is_suspended=" << is_suspended;
   DCHECK(thread_checker_.CalledOnValidThread());
index d6bfd5d..831e739 100644 (file)
@@ -182,6 +182,9 @@ class MEDIA_EXPORT PipelineImpl : public Pipeline {
   void OnVideoPipelineInfoChange(const VideoPipelineInfo& info);
   void OnRemotePlayStateChange(MediaStatus::State state);
   void OnVideoFrameRateChange(absl::optional<int> fps);
+#if defined(TIZEN_MULTIMEDIA)
+  void OnResourceConflict();
+#endif
 
   // Task completion callbacks from RendererWrapper.
   void OnSeekDone(bool is_suspended);
index ad3ffec..c5b7fcb 100644 (file)
@@ -64,6 +64,12 @@ class MEDIA_EXPORT RendererClient {
   // unset if the frame rate is unstable.  The duration used for the frame rate
   // is based on the wall clock time, not the media time.
   virtual void OnVideoFrameRateChange(absl::optional<int> fps) = 0;
+
+#if defined(TIZEN_MULTIMEDIA)
+  virtual void OnResourceConflict() {}
+
+  virtual void OnRequestSeek(base::TimeDelta time) {}
+#endif
 };
 
 }  // namespace media
index bb2e8b0..68c2283 100644 (file)
@@ -409,6 +409,16 @@ void MojoRenderer::OnFlushed() {
 }
 
 #if defined(TIZEN_MULTIMEDIA)
+void MojoRenderer::OnResourceConflict() {
+  DVLOG(2) << __func__;
+  client_->OnResourceConflict();
+}
+
+void MojoRenderer::OnRequestSeek(base::TimeDelta time) {
+  DVLOG(2) << __func__ << "(" << time << ")";
+  client_->OnRequestSeek(time);
+}
+
 void MojoRenderer::OnSeekCompleted() {
   DVLOG(1) << __func__;
   DCHECK(task_runner_->BelongsToCurrentThread());
index 9eabf6d..3750ca9 100644 (file)
@@ -90,6 +90,10 @@ class MojoRenderer : public Renderer, public mojom::RendererClient {
   void OnVideoOpacityChange(bool opaque) override;
   void OnWaiting(WaitingReason reason) override;
   void OnStatisticsUpdate(const PipelineStatistics& stats) override;
+#if defined(TIZEN_MULTIMEDIA)
+  void OnResourceConflict() override;
+  void OnRequestSeek(base::TimeDelta time) override;
+#endif
 
   // Binds |remote_renderer_| to the mojo message pipe. Can be called multiple
   // times. If an error occurs during connection, OnConnectionError will be
index 756ee5e..6e998bc 100644 (file)
@@ -105,4 +105,10 @@ interface RendererClient {
   // Called when the remote rendering service is waiting for `reason`,
   // e.g. waiting for decryption key.
   OnWaiting(WaitingReason reason);
+
+  [EnableIf=tizen_multimedia]
+  OnResourceConflict();
+
+  [EnableIf=tizen_multimedia]
+  OnRequestSeek(mojo_base.mojom.TimeDelta time);
 };
index de301ba..5f8924e 100644 (file)
@@ -224,6 +224,18 @@ void MojoRendererService::OnVideoOpacityChange(bool opaque) {
   client_->OnVideoOpacityChange(opaque);
 }
 
+#if defined(TIZEN_MULTIMEDIA)
+void MojoRendererService::OnResourceConflict() {
+  DVLOG(2) << __func__;
+  client_->OnResourceConflict();
+}
+
+void MojoRendererService::OnRequestSeek(base::TimeDelta time) {
+  DVLOG(2) << __func__ << "(" << time << ")";
+  client_->OnRequestSeek(time);
+}
+#endif
+
 void MojoRendererService::OnVideoFrameRateChange(absl::optional<int> fps) {
   DVLOG(2) << __func__ << "(" << (fps ? *fps : -1) << ")";
   // TODO(liberato): plumb to |client_|.
index 8031edf..9a39a1e 100644 (file)
@@ -102,6 +102,10 @@ class MEDIA_MOJO_EXPORT MojoRendererService final : public mojom::Renderer,
   void OnVideoNaturalSizeChange(const gfx::Size& size) final;
   void OnVideoOpacityChange(bool opaque) final;
   void OnVideoFrameRateChange(absl::optional<int> fps) final;
+#if defined(TIZEN_MULTIMEDIA)
+  void OnResourceConflict() final;
+  void OnRequestSeek(base::TimeDelta time) final;
+#endif
 
   // Called when the MediaResourceShim is ready to go (has a config,
   // pipe handle, etc) and can be handed off to a renderer for use.
index 1358144..89084df 100644 (file)
@@ -2645,6 +2645,14 @@ void WebMediaPlayerImpl::OnVideoPipelineInfoChange(
   UpdateSecondaryProperties();
 }
 
+#if defined(TIZEN_MULTIMEDIA)
+void WebMediaPlayerImpl::OnResourceConflict() {
+  DVLOG(2) << __func__;
+  client_->PausePlayback(WebMediaPlayerClient::PauseReason::kUnknown);
+  SetSuspendState(true);
+}
+#endif
+
 void WebMediaPlayerImpl::OnFrameHidden() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
index 218cb25..8f99c21 100644 (file)
@@ -379,6 +379,9 @@ class PLATFORM_EXPORT WebMediaPlayerImpl
   void OnVideoAverageKeyframeDistanceUpdate() override;
   void OnAudioPipelineInfoChange(const media::AudioPipelineInfo& info) override;
   void OnVideoPipelineInfoChange(const media::VideoPipelineInfo& info) override;
+#if defined(TIZEN_MULTIMEDIA)
+  void OnResourceConflict() override;
+#endif
 
   // Simplified watch time reporting.
   void OnSimpleWatchTimerTick();
index c718835..943ec52 100644 (file)
@@ -280,6 +280,12 @@ void TizenRendererImpl::SetStreamInfo() {
         &TizenRendererImpl::OnNewFrameAvailable, base::Unretained(this)));
 #endif
   }
+
+  media_player_->SetResourceConflictCallback(base::BindRepeating(
+      &TizenRendererImpl::OnResourceConflict, base::Unretained(this)));
+
+  media_player_->SetRequestSeekCallback(base::BindRepeating(
+      &TizenRendererImpl::OnRequestSeek, base::Unretained(this)));
 }
 
 void TizenRendererImpl::SetCdm(media::CdmContext* cdm_context,
@@ -543,4 +549,12 @@ void TizenRendererImpl::OnTbmBufferExhausted(
 }
 #endif
 
+void TizenRendererImpl::OnResourceConflict() {
+  client_->OnResourceConflict();
+}
+
+void TizenRendererImpl::OnRequestSeek(base::TimeDelta time) {
+  client_->OnRequestSeek(time);
+}
+
 }  // namespace content
index 1450951..1dcfe0f 100644 (file)
@@ -144,6 +144,9 @@ class CONTENT_EXPORT TizenRendererImpl
   void OnVideoOpacityChange(bool opaque);
   void OnVideoFrameRateChange(absl::optional<int> fps);
 
+  void OnResourceConflict();
+  void OnRequestSeek(base::TimeDelta time);
+
   media::RendererClient* client_;
 
   State state_;
index 9947b35..ec4ad4c 100644 (file)
@@ -266,6 +266,14 @@ void MediaPlayerESPlusPlayer::Prepare() {
     return;
   }
 
+  if (MediaPlayerRegistry::GetInstance()->IsMediaPlayerPreparing()) {
+    LOG(WARNING) << "(" << static_cast<void*>(this) << ") " << __func__
+                 << " Only one media player can be prepared."
+                 << " Call ResourceConflict Callback.";
+    conflict_cb_.Run();
+    return;
+  }
+
 #if defined(TIZEN_VIDEO_HOLE)
   if (is_video_hole_ && video_plane_controller_) {
     if (video_plane_controller_->Initialize() != TIZEN_ERROR_NONE) {
@@ -316,6 +324,7 @@ void MediaPlayerESPlusPlayer::Prepare() {
     return;
   }
   is_preparing_ = true;
+  MediaPlayerRegistry::GetInstance()->SetIsPreparing(true);
 }
 
 bool MediaPlayerESPlusPlayer::IsPrepared() {
@@ -367,6 +376,8 @@ void MediaPlayerESPlusPlayer::Release() {
   GetBufferQueue(DemuxerStream::VIDEO).clear();
   GetBufferQueue(DemuxerStream::AUDIO).clear();
   data_cb_.Reset();
+  conflict_cb_.Reset();
+  request_seek_cb_.Reset();
   sink_ = nullptr;
 
   if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
@@ -476,6 +487,7 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time,
                                    base::OnceClosure seek_cb) {
   // Ignore seek in release state. Will catch-up later.
   if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
+    LOG(INFO) << __func__ << " Ignore seek in release state.";
     if (seek_cb)
       std::move(seek_cb).Run();
     return;
@@ -483,13 +495,7 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time,
 
   // Ignore seek to 0 during initialization.
   if (GetPlayerState() < ESPLUSPLAYER_STATE_READY && time.InSecondsF() == 00) {
-    if (seek_cb)
-      std::move(seek_cb).Run();
-    return;
-  }
-
-  if (time.FloorToMultiple(base::Seconds(0.1)) ==
-      GetCurrentTime().FloorToMultiple(base::Seconds(0.1))) {
+    LOG(INFO) << __func__ << " Ignore seek to 0 during initialization.";
     if (seek_cb)
       std::move(seek_cb).Run();
     return;
@@ -498,7 +504,6 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time,
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " :"
             << time;
 
-  seek_cb_ = std::move(seek_cb);
   if (GetPlayerState() < ESPLUSPLAYER_STATE_READY || !is_prepared_) {
     LOG(INFO) << "Add to pending seek ("
               << ") state: " << GetString(GetPlayerState())
@@ -510,6 +515,8 @@ void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time,
       std::move(seek_cb).Run();
     return;
   }
+  seek_cb_ = std::move(seek_cb);
+
   SeekInternal(time);
 }
 
@@ -840,6 +847,12 @@ esplusplayer_submit_status MediaPlayerESPlusPlayer::SubmitEsPacket(
   packet.pts = buffer->timestamp().InMilliseconds();
   packet.duration = buffer->duration().InMilliseconds();
 
+  LOG(INFO) << __func__ << " " << DemuxerStream::GetTypeName(type) << " : "
+            << "Pushing esplus timestamp: " << packet.pts
+            << ", duration: " << packet.duration
+            << ", size: " << packet.buffer_size
+            << ", is_key_frame: " << buffer->is_key_frame();
+
   // This filed only set when PushMediaPacket
   packet.matroska_color_info = nullptr;
   esplusplayer_submit_status status =
@@ -951,6 +964,8 @@ void MediaPlayerESPlusPlayer::OnReadyToPrepare(
 }
 
 void MediaPlayerESPlusPlayer::OnPrepareComplete(bool result) {
+   MediaPlayerRegistry::GetInstance()->SetIsPreparing(false);
+
   if (!task_runner_->BelongsToCurrentThread()) {
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnPrepareComplete,
@@ -988,7 +1003,7 @@ void MediaPlayerESPlusPlayer::OnPrepareComplete(bool result) {
 
   is_prepared_ = true;
   if (pending_seek_) {
-    SeekInternal(pending_seek_position_);
+    request_seek_cb_.Run(pending_seek_position_);
   } else if (!is_paused_) {
     Play();
   }
@@ -1186,7 +1201,7 @@ void MediaPlayerESPlusPlayer::OnResourceConflict() {
   }
 
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
-  NOTIMPLEMENTED();
+  conflict_cb_.Run();
 }
 
 void MediaPlayerESPlusPlayer::OnError(const esplusplayer_error_type error) {
@@ -1291,6 +1306,16 @@ void MediaPlayerESPlusPlayer::SetFrameAvailableCallback(
   data_cb_ = datacb;
 }
 
+void MediaPlayerESPlusPlayer::SetResourceConflictCallback(
+    const ResourceConflictCB& conflict_cb) {
+  conflict_cb_ = conflict_cb;
+}
+
+void MediaPlayerESPlusPlayer::SetRequestSeekCallback(
+    const RequestSeekCB& request_seek_cb) {
+  request_seek_cb_ = request_seek_cb;
+}
+
 #if defined(TIZEN_VIDEO_HOLE)
 void MediaPlayerESPlusPlayer::SetVideoHole(bool is_video_hole) {
   LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
index 4247fde..03788cd 100644 (file)
@@ -49,6 +49,8 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer : public MediaPlayerTizen {
                                    uint32_t,
                                    uint32_t)>;
 #endif
+  using ResourceConflictCB = base::RepeatingCallback<void()>;
+  using RequestSeekCB = base::RepeatingCallback<void(base::TimeDelta)>;
 
   bool CreatePlayer() override;
   void Initialize(VideoRendererSink* sink) override;
@@ -76,6 +78,11 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer : public MediaPlayerTizen {
   base::TimeDelta GetCurrentTime() override;
 
   void SetFrameAvailableCallback(const DataRequestCB& datacb) override;
+
+  void SetResourceConflictCallback(
+      const ResourceConflictCB& conflict_cb) override;
+  void SetRequestSeekCallback(const RequestSeekCB& request_seek_cb) override;
+
 #if defined(TIZEN_VIDEO_HOLE)
   void SetVideoHole(bool is_video_hole) override;
   void SetMediaGeometry(const gfx::Rect& viewport_rect,
@@ -187,6 +194,8 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayer : public MediaPlayerTizen {
   int player_id_ = 0;
 
   DataRequestCB data_cb_;
+  ResourceConflictCB conflict_cb_;
+  RequestSeekCB request_seek_cb_;
 
   VideoRendererSink* sink_;
 
index 0f21b49..de5d9f8 100644 (file)
@@ -38,4 +38,12 @@ MediaPlayerTizen* MediaPlayerRegistry::GetMediaPlayer(int player_id) {
   return nullptr;
 }
 
+bool MediaPlayerRegistry::IsMediaPlayerPreparing() {
+  return is_preparing_;
+}
+
+void MediaPlayerRegistry::SetIsPreparing(bool is_preparing) {
+  is_preparing_ = is_preparing;
+}
+
 }  // namespace media
index 0576fba..57e6de6 100644 (file)
@@ -23,6 +23,8 @@ class MEDIA_EXPORT MediaPlayerRegistry {
 
   MediaPlayerTizen* GetMediaPlayer(int player_id);
 
+  bool IsMediaPlayerPreparing();
+  void SetIsPreparing(bool is_preparing);
  private:
   MediaPlayerRegistry();
   ~MediaPlayerRegistry();
@@ -34,6 +36,8 @@ class MEDIA_EXPORT MediaPlayerRegistry {
 
   std::map<int, MediaPlayerTizen*> media_players_;
 
+  bool is_preparing_ = false;
+
   friend struct base::DefaultSingletonTraits<MediaPlayerRegistry>;
 };
 
index 1a05578..cedffa1 100644 (file)
@@ -54,6 +54,9 @@ class MEDIA_EXPORT MediaPlayerTizen {
                                    uint32_t)>;
 #endif
 
+  using ResourceConflictCB = base::RepeatingCallback<void()>;
+  using RequestSeekCB = base::RepeatingCallback<void(base::TimeDelta)>;
+
   MediaPlayerTizen() {}
   virtual ~MediaPlayerTizen() {}
 
@@ -82,6 +85,10 @@ class MEDIA_EXPORT MediaPlayerTizen {
   virtual void SetVolume(double volume) = 0;
   virtual base::TimeDelta GetCurrentTime() = 0;
 
+  virtual void SetResourceConflictCallback(
+      const ResourceConflictCB& conflict_cb) {}
+  virtual void SetRequestSeekCallback(const RequestSeekCB& pending_seek_cb) {}
+
   // Will be replaced by |ClientExtention| interface.
   virtual void SetFrameAvailableCallback(const DataRequestCB& datacb) = 0;