[M120 Migration][MM][WebRTC] Base implementation for webrtc video hole stream 17/309217/2
authorpeng.yin <peng8.yin@samsung.com>
Thu, 13 Oct 2022 05:58:23 +0000 (13:58 +0800)
committerBot Blink <blinkbot@samsung.com>
Mon, 8 Apr 2024 12:25:49 +0000 (12:25 +0000)
migrate the TizenEsPlusPlayerRenderer class from M108 who roles same
as MmVideodecoder before, but it creates a self owned EsPlusPlayer
by mojo renderer, and update the latest geometry change info to
EsPlusPlayer.

refer to:
https://review.tizen.org/gerrit/#/c/291285/

Change-Id: I0743eb1202dfa261501a9fdf12b2d5d73c34fa39
Signed-off-by: peng.yin <peng8.yin@samsung.com>
51 files changed:
cc/layers/layer_impl.h
cc/layers/video_layer.cc
cc/layers/video_layer.h
cc/layers/video_layer_impl.cc
cc/layers/video_layer_impl.h
components/cast_streaming/renderer/control/playback_command_forwarding_renderer.cc
media/base/pipeline_impl.cc
media/base/renderer.h
media/base/video_decoder_config.h
media/base/video_frame.cc
media/base/video_frame.h
media/base/video_frame_metadata.cc
media/base/video_frame_metadata.h
media/filters/chunk_demuxer.cc
media/mojo/clients/mojo_demuxer_stream_impl.cc
media/mojo/clients/mojo_renderer.cc
media/mojo/clients/mojo_renderer.h
media/mojo/clients/mojo_renderer_wrapper.cc
media/mojo/clients/mojo_renderer_wrapper.h
media/mojo/mojom/media_types.mojom
media/mojo/mojom/renderer.mojom
media/mojo/mojom/renderer_extensions.mojom
media/mojo/mojom/video_decoder_config_mojom_traits.cc
media/mojo/mojom/video_decoder_config_mojom_traits.h
media/mojo/services/mojo_renderer_service.cc
media/mojo/services/mojo_renderer_service.h
third_party/blink/public/platform/modules/mediastream/web_media_stream_sink.h
third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h
third_party/blink/renderer/modules/mediastream/local_video_capturer_source.cc
third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.h
third_party/blink/renderer/platform/media/BUILD.gn
third_party/blink/renderer/platform/media/web_media_player_impl.cc
third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc
third_party/blink/renderer/platform/video_capture/video_capture_impl.cc
third_party/blink/renderer/platform/video_capture/video_capture_impl.h
third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
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/content/renderer/media/tizen/media_player_renderer_client.cc
tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h
tizen_src/chromium_impl/media/audio/tizen/capi_usb_audio_input_stream.cc
tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.h [new file with mode: 0644]
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_tizen.cc
tizen_src/chromium_impl/media/filters/esplusplayer_util.cc
tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc
tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.h
tizen_src/chromium_impl/media/filters/media_player_tizen.h
tizen_src/chromium_impl/media/media_efl.gni

index 20c3639..ff4af40 100644 (file)
@@ -472,6 +472,10 @@ class CC_EXPORT LayerImpl {
   // TODO(sunxd): Remove this function and replace it with visitor pattern.
   virtual bool is_surface_layer() const;
 
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+  virtual absl::optional<int> GetVideoId() const { return absl::nullopt; }
+#endif
+
   int CalculateJitter();
 
   std::string DebugName() const;
index 21b6d7d..9fb763f 100644 (file)
@@ -10,13 +10,34 @@ namespace cc {
 
 scoped_refptr<VideoLayer> VideoLayer::Create(
     VideoFrameProvider* provider,
-    media::VideoTransformation transform) {
-  return base::WrapRefCounted(new VideoLayer(provider, transform));
+    media::VideoTransformation transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+    ,
+    absl::optional<int> player_id
+#endif
+) {
+  return base::WrapRefCounted(new VideoLayer(provider, transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+                                             ,
+                                             player_id
+#endif
+                                             ));
 }
 
 VideoLayer::VideoLayer(VideoFrameProvider* provider,
-                       media::VideoTransformation transform)
-    : provider_(provider), transform_(transform) {
+                       media::VideoTransformation transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+                       ,
+                       absl::optional<int> player_id
+#endif
+                       )
+    : provider_(provider),
+      transform_(transform)
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      player_id_(player_id)
+#endif
+{
   SetMayContainVideo(true);
   DCHECK(provider_.Read(*this));
 }
@@ -26,7 +47,12 @@ VideoLayer::~VideoLayer() = default;
 std::unique_ptr<LayerImpl> VideoLayer::CreateLayerImpl(
     LayerTreeImpl* tree_impl) const {
   return VideoLayerImpl::Create(tree_impl, id(), provider_.Read(*this),
-                                transform_);
+                                transform_
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+                                ,
+                                player_id_
+#endif
+  );
 }
 
 bool VideoLayer::RequiresSetNeedsDisplayOnHdrHeadroomChange() const {
index c8f3ea4..9db97e9 100644 (file)
@@ -24,7 +24,12 @@ class VideoLayerImpl;
 class CC_EXPORT VideoLayer : public Layer {
  public:
   static scoped_refptr<VideoLayer> Create(VideoFrameProvider* provider,
-                                          media::VideoTransformation transform);
+                                          media::VideoTransformation transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+                                          ,
+                                          absl::optional<int> player_id
+#endif
+  );
 
   VideoLayer(const VideoLayer&) = delete;
   VideoLayer& operator=(const VideoLayer&) = delete;
@@ -37,9 +42,17 @@ class CC_EXPORT VideoLayer : public Layer {
   // Clears |provider_| to ensure it is not used after destruction.
   void StopUsingProvider();
 
+  media::VideoTransformation GetTransform() const { return transform_; }
+
  private:
   VideoLayer(VideoFrameProvider* provider,
-             media::VideoTransformation transform);
+             media::VideoTransformation transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+             ,
+             absl::optional<int> player_id
+#endif
+  );
+
   ~VideoLayer() override;
 
   // This pointer is only for passing to VideoLayerImpl's constructor. It should
@@ -47,6 +60,10 @@ class CC_EXPORT VideoLayer : public Layer {
   ProtectedSequenceReadable<raw_ptr<VideoFrameProvider>> provider_;
 
   const media::VideoTransformation transform_;
+
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<int> player_id_;
+#endif
 };
 
 }  // namespace cc
index b51b823..01a9ade 100644 (file)
@@ -38,7 +38,12 @@ std::unique_ptr<VideoLayerImpl> VideoLayerImpl::Create(
     LayerTreeImpl* tree_impl,
     int id,
     VideoFrameProvider* provider,
-    const media::VideoTransformation& video_transform) {
+    const media::VideoTransformation& video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+    ,
+    absl::optional<int> player_id
+#endif
+) {
   DCHECK(tree_impl->task_runner_provider()->IsMainThreadBlocked() ||
          base::FeatureList::IsEnabled(features::kNonBlockingCommit));
   DCHECK(tree_impl->task_runner_provider()->IsImplThread());
@@ -48,17 +53,32 @@ std::unique_ptr<VideoLayerImpl> VideoLayerImpl::Create(
           provider, tree_impl->GetVideoFrameControllerClient());
 
   return base::WrapUnique(new VideoLayerImpl(
-      tree_impl, id, std::move(provider_client_impl), video_transform));
+      tree_impl, id, std::move(provider_client_impl), video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      player_id
+#endif
+      ));
 }
 
 VideoLayerImpl::VideoLayerImpl(
     LayerTreeImpl* tree_impl,
     int id,
     scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl,
-    const media::VideoTransformation& video_transform)
+    const media::VideoTransformation& video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+    ,
+    absl::optional<int> player_id
+#endif
+    )
     : LayerImpl(tree_impl, id),
       provider_client_impl_(std::move(provider_client_impl)),
-      video_transform_(video_transform) {
+      video_transform_(video_transform)
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      player_id_(player_id)
+#endif
+{
   set_may_contain_video(true);
 }
 
@@ -82,7 +102,12 @@ VideoLayerImpl::~VideoLayerImpl() {
 std::unique_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl(
     LayerTreeImpl* tree_impl) const {
   return base::WrapUnique(new VideoLayerImpl(
-      tree_impl, id(), provider_client_impl_, video_transform_));
+      tree_impl, id(), provider_client_impl_, video_transform_
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      player_id_
+#endif
+      ));
 }
 
 void VideoLayerImpl::DidBecomeActive() {
@@ -348,6 +373,12 @@ int VideoLayerImpl::render_target_effect_tree_index() const {
   }
   return LayerImpl::render_target_effect_tree_index();
 }
+
+#if BUILDFLAG(IS_TIZEN_TV)
+absl::optional<int> VideoLayerImpl::GetVideoId() const {
+  return player_id_;
+}
+#endif  // BUILDFLAG(IS_TIZEN_TV)
 #endif
 
 void VideoLayerImpl::SetNeedsRedraw() {
index 4aac591..237fc8c 100644 (file)
@@ -37,7 +37,12 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl {
       LayerTreeImpl* tree_impl,
       int id,
       VideoFrameProvider* provider,
-      const media::VideoTransformation& video_transform);
+      const media::VideoTransformation& video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      absl::optional<int> player_id
+#endif
+  );
   VideoLayerImpl(const VideoLayerImpl&) = delete;
   ~VideoLayerImpl() override;
 
@@ -57,6 +62,9 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl {
   gfx::ContentColorUsage GetContentColorUsage() const override;
 #if defined(TIZEN_VIDEO_HOLE)
   int render_target_effect_tree_index() const override;
+#if BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<int> GetVideoId() const override;
+#endif  //  BUILDFLAG(IS_TIZEN_TV)
 #endif
 
   void SetNeedsRedraw();
@@ -74,7 +82,12 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl {
       LayerTreeImpl* tree_impl,
       int id,
       scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl,
-      const media::VideoTransformation& video_transform);
+      const media::VideoTransformation& video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+      ,
+      absl::optional<int> player_id
+#endif
+  );
 
   const char* LayerTypeAsString() const override;
 
@@ -94,6 +107,10 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl {
 
   media::VideoTransformation video_transform_;
 
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<int> player_id_;
+#endif
+
 #if defined(TIZEN_TBM_SUPPORT)
   scoped_refptr<viz::ContextProvider> context_provider_;
   viz::ResourceId resource_id_;
index ccf00d8..4255a37 100644 (file)
@@ -43,6 +43,14 @@ class RendererCommandForwarder : public media::mojom::Renderer {
   ~RendererCommandForwarder() override = default;
 
   // media::mojom::Renderer overrides.
+#if defined(TIZEN_MULTIMEDIA)
+  void EnableLowLatencyMode() override {}
+  void SetHardwareResource(
+      media::mojom::HardwareResourceConfig config,
+      media::mojom::Renderer::SetHardwareResourceCallback) override {}
+  void RequestVideoDecodedBuffer(
+      media::mojom::Renderer::RequestVideoDecodedBufferCallback cb) override {}
+#endif
   void Initialize(
       ::mojo::PendingAssociatedRemote<media::mojom::RendererClient> client,
       absl::optional<
index b33eec4..a026dd0 100644 (file)
@@ -712,7 +712,7 @@ void PipelineImpl::RendererWrapper::SetCdm(CdmContext* cdm_context,
 void PipelineImpl::RendererWrapper::SetMediaGeometry(const gfx::RectF rect_f) {
   LOG(INFO) << __func__;
   if (shared_state_.renderer)
-    shared_state_.renderer->SetMediaGeometry(rect_f);
+    shared_state_.renderer->SetMediaGeometry(rect_f, media::VIDEO_ROTATION_0);
 }
 #endif
 
index 133476d..e32caab 100644 (file)
@@ -22,6 +22,11 @@ namespace media {
 class CdmContext;
 class MediaResource;
 class RendererClient;
+#if defined(TIZEN_MULTIMEDIA)
+namespace mojom {
+enum class HardwareResourceConfig;
+}
+#endif
 
 // Types of media::Renderer.
 // WARNING: These values are reported to metrics. Entries should not be
@@ -97,18 +102,29 @@ class MEDIA_EXPORT Renderer {
 #if defined(TIZEN_MULTIMEDIA)
   virtual void Seek(base::TimeDelta time, base::OnceClosure seek_cb) {}
   virtual void Suspend() {}
+  virtual void EnableLowLatencyMode() {}
+  using SetHardwareResourceCB = base::OnceCallback<void(bool)>;
+  virtual void SetHardwareResource(media::mojom::HardwareResourceConfig config,
+                                   SetHardwareResourceCB cb) {}
+  using RequestVideoDecodedBufferCB = base::OnceCallback<void(bool)>;
+  virtual void RequestVideoDecodedBuffer(RequestVideoDecodedBufferCB cb) {}
   using ToggledFullscreenCB = base::OnceCallback<void()>;
   virtual void ToggleFullscreenMode(bool is_fullscreen,
                                     ToggledFullscreenCB cb) {}
 #endif
 
 #if defined(TIZEN_VIDEO_HOLE)
+  virtual void SetPlayerVideoAbove(int32_t other_id) {}
+  virtual void UseSubsurfaceController() {}
   virtual void SetVideoHole(bool is_video_hole) {}
-  virtual void SetMediaGeometry(const gfx::RectF& rect) {}
+  virtual void SetMediaGeometry(const gfx::RectF& rect,
+                                media::VideoRotation rotation) {}
 #endif
 
   using StartDateCB = base::OnceCallback<void(double)>;
 #if BUILDFLAG(IS_TIZEN_TV)
+  using GetVideoIdCB = base::OnceCallback<void(int32_t)>;
+  virtual void GetVideoId(GetVideoIdCB cb) {}
   virtual void SetContentMimeType(const std::string& mime_type) {}
   virtual void SetParentalRatingResult(bool is_pass) {}
   virtual void SetActiveTextTrack(int id, bool is_in_band) {}
index 436cfc0..c288349 100644 (file)
 
 namespace media {
 
+#if defined(TIZEN_MULTIMEDIA)
+constexpr auto kFHDVideoMaxWidth = 1920;
+constexpr auto kFHDVideoMaxHeight = 1080;
+constexpr auto k8KVideoMaxWidth = 7680;
+constexpr auto k8KVideoMaxHeight = 4320;
+#endif
+
 // Describes the content of a video stream, as described by the media container
 // (or otherwise determined by the demuxer).
 class MEDIA_EXPORT VideoDecoderConfig {
@@ -167,10 +174,16 @@ class MEDIA_EXPORT VideoDecoderConfig {
 #if BUILDFLAG(IS_TIZEN_TV)
   void set_hdr_info(const std::string& hdr_info) { hdr_info_ = hdr_info; }
   const std::string& hdr_info() const { return hdr_info_; }
-  void set_framerate_num(const int num) { framerate_num_ = num; }
-  void set_framerate_den(const int den) { framerate_den_ = den; }
+  void set_framerate_num(int num) { framerate_num_ = num; }
+  void set_framerate_den(int den) { framerate_den_ = den; }
+  void set_enable_manual_copy(bool enable) { enable_manual_copy_ = enable; }
   int framerate_num() const { return framerate_num_; }
   int framerate_den() const { return framerate_den_; }
+  bool enable_manual_copy() const { return enable_manual_copy_; }
+#endif
+#if defined(TIZEN_MULTIMEDIA)
+  void set_max_coded_size(gfx::Size size) { max_coded_size_ = size; }
+  const gfx::Size& max_coded_size() const { return max_coded_size_; }
 #endif
 
  private:
@@ -201,9 +214,14 @@ class MEDIA_EXPORT VideoDecoderConfig {
   bool is_rtc_ = false;
 
 #if BUILDFLAG(IS_TIZEN_TV)
+  bool needs_raw_access_ = false;
   int framerate_num_ = 0;
   int framerate_den_ = 0;
   std::string hdr_info_;
+  bool enable_manual_copy_{false};
+#endif
+#if defined(TIZEN_MULTIMEDIA)
+  gfx::Size max_coded_size_{k8KVideoMaxWidth, k8KVideoMaxHeight};
 #endif
 
   // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
index e7f03f7..87d60b3 100644 (file)
@@ -521,8 +521,17 @@ scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
     const gfx::Size& natural_size,
     const uint8_t* data,
     size_t data_size,
-    base::TimeDelta timestamp) {
-  auto layout = GetDefaultLayout(format, coded_size);
+    base::TimeDelta timestamp,
+    size_t encoded_data_size) {
+  absl::optional<VideoFrameLayout> layout;
+  if (format == PIXEL_FORMAT_ENCODED) {
+    auto plane = media::ColorPlaneLayout(encoded_data_size, 0, data_size);
+    layout = media::VideoFrameLayout::CreateWithPlanes(format, coded_size,
+                                                       {std::move(plane)});
+  } else {
+    layout = GetDefaultLayout(format, coded_size);
+  }
+
   if (!layout)
     return nullptr;
   return WrapExternalDataWithLayout(*layout, visible_rect, natural_size, data,
index 3ed6fec..f31ae5b 100644 (file)
@@ -262,7 +262,8 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
       const gfx::Size& natural_size,
       const uint8_t* data,
       size_t data_size,
-      base::TimeDelta timestamp);
+      base::TimeDelta timestamp,
+      size_t encoded_data_size = 0);
 
   static scoped_refptr<VideoFrame> WrapExternalDataWithLayout(
       const VideoFrameLayout& layout,
index 24d2844..3a64c01 100644 (file)
@@ -70,6 +70,9 @@ void VideoFrameMetadata::MergeMetadataFrom(
   MERGE_OPTIONAL_FIELD(receive_time, metadata_source);
   MERGE_OPTIONAL_FIELD(wallclock_frame_duration, metadata_source);
   MERGE_OPTIONAL_FIELD(maximum_composition_delay_in_frames, metadata_source);
+#if BUILDFLAG(IS_TIZEN_TV)
+  MERGE_OPTIONAL_FIELD(player_id, metadata_source);
+#endif
 
 #undef MERGE_VALUE_FIELD
 #undef MERGE_OPTIONAL_FIELD
index b14a94a..82a2b0a 100644 (file)
@@ -216,6 +216,10 @@ struct MEDIA_EXPORT VideoFrameMetadata {
   // This is an experimental feature, see crbug.com/1138888 for more
   // information.
   absl::optional<int> maximum_composition_delay_in_frames;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<int> player_id;
+#endif
 };
 
 }  // namespace media
index df72051..8daa31a 100644 (file)
@@ -375,6 +375,8 @@ void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
 
   switch (state_) {
     case UNINITIALIZED:
+      if (stream_->GetCurrentVideoDecoderConfig().is_rtc())
+        return;
       NOTREACHED_NORETURN();
     case RETURNING_ABORT_FOR_READS:
       // Null buffers should be returned in this state since we are waiting
index f4b26f7..7afeabe 100644 (file)
@@ -96,8 +96,14 @@ void MojoDemuxerStreamImpl::OnBufferReady(
 
 #if defined(TIZEN_MULTIMEDIA)
   if (status == Status::kNeedBuffer) {
-    LOG(INFO) << __func__ << " (" << media::DemuxerStream::GetTypeName(stream_->type())
-              << ") kNeedBuffer!";
+    static int64_t log_second = -1;
+    int64_t log_second_now =
+        (base::TimeTicks::Now() - base::TimeTicks()).InSeconds();
+    bool log_on = (log_second != log_second_now);
+    log_second = log_second_now;
+    LOG_IF(INFO, log_on) << __func__ << " ("
+                         << media::DemuxerStream::GetTypeName(stream_->type())
+                         << ") kNeedBuffer!";
     std::move(callback).Run(Status::kNeedBuffer, {},
                             audio_config, video_config);
     return;
index 148a8b3..8c7ff42 100644 (file)
@@ -256,14 +256,27 @@ base::TimeDelta MojoRenderer::GetMediaTime() {
 }
 
 #if defined(TIZEN_VIDEO_HOLE)
+void MojoRenderer::SetPlayerVideoAbove(int32_t other_id) {
+  DVLOG(2) << __func__;
+  BindRemoteRendererIfNeeded();
+  remote_renderer_->SetPlayerVideoAbove(other_id);
+}
+
+void MojoRenderer::UseSubsurfaceController() {
+  DVLOG(2) << __func__;
+  BindRemoteRendererIfNeeded();
+  remote_renderer_->UseSubsurfaceController();
+}
+
 void MojoRenderer::SetVideoHole(bool is_video_hole) {
   BindRemoteRendererIfNeeded();
   remote_renderer_->SetVideoHole(is_video_hole);
 }
 
-void MojoRenderer::SetMediaGeometry(const gfx::RectF& rect) {
+void MojoRenderer::SetMediaGeometry(const gfx::RectF& rect,
+                                    VideoRotation rotation) {
   if (remote_renderer_.is_bound())
-    remote_renderer_->SetMediaGeometry(rect);
+    remote_renderer_->SetMediaGeometry(rect, rotation);
 }
 #endif
 
@@ -271,6 +284,26 @@ RendererType MojoRenderer::GetRendererType() {
   return RendererType::kMojo;
 }
 
+#if defined(TIZEN_MULTIMEDIA)
+void MojoRenderer::EnableLowLatencyMode() {
+  LOG(INFO) << __func__;
+  if (remote_renderer_.is_bound())
+    remote_renderer_->EnableLowLatencyMode();
+}
+
+void MojoRenderer::SetHardwareResource(mojom::HardwareResourceConfig config,
+                                       SetHardwareResourceCB cb) {
+  LOG(INFO) << __func__;
+  if (remote_renderer_.is_bound())
+    remote_renderer_->SetHardwareResource(config, std::move(cb));
+}
+
+void MojoRenderer::RequestVideoDecodedBuffer(RequestVideoDecodedBufferCB cb) {
+  if (remote_renderer_.is_bound())
+    remote_renderer_->RequestVideoDecodedBuffer(std::move(cb));
+}
+#endif
+
 void MojoRenderer::OnTimeUpdate(base::TimeDelta time,
                                 base::TimeDelta max_time,
                                 base::TimeTicks capture_time) {
@@ -344,6 +377,13 @@ void MojoRenderer::OnStatisticsUpdate(const PipelineStatistics& stats) {
 }
 
 #if BUILDFLAG(IS_TIZEN_TV)
+void MojoRenderer::GetVideoId(GetVideoIdCB cb) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  if (remote_renderer_.is_bound())
+    remote_renderer_->GetVideoId(std::move(cb));
+}
+
 void MojoRenderer::SetContentMimeType(const std::string& mime_type) {
   DVLOG(2) << __func__ << " mime_type : " << mime_type;
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
index 8ae441e..5bf3a50 100644 (file)
@@ -62,6 +62,10 @@ class MojoRenderer : public Renderer, public mojom::RendererClient {
 #if defined(TIZEN_MULTIMEDIA)
   void Seek(base::TimeDelta time, base::OnceClosure seek_cb) override;
   void Suspend() override;
+  void EnableLowLatencyMode() override;
+  void SetHardwareResource(mojom::HardwareResourceConfig config,
+                           SetHardwareResourceCB cb) override;
+  void RequestVideoDecodedBuffer(RequestVideoDecodedBufferCB cb) override;
   void ToggleFullscreenMode(bool is_fullscreen,
                             ToggledFullscreenCB cb) override;
 #endif
@@ -72,11 +76,15 @@ class MojoRenderer : public Renderer, public mojom::RendererClient {
   RendererType GetRendererType() override;
 
 #if defined(TIZEN_VIDEO_HOLE)
+  void SetPlayerVideoAbove(int32_t other_id) override;
+  void UseSubsurfaceController() override;
   void SetVideoHole(bool is_video_hole) override;
-  void SetMediaGeometry(const gfx::RectF& rect) override;
+  void SetMediaGeometry(const gfx::RectF& rect,
+                        VideoRotation rotation) override;
 #endif
 
 #if BUILDFLAG(IS_TIZEN_TV)
+  void GetVideoId(GetVideoIdCB cb) override;
   void SetContentMimeType(const std::string& mime_type) override;
   void SetParentalRatingResult(bool is_pass) override;
   void SetActiveTextTrack(int id, bool is_in_band) override;
index f5dee67..bff94b6 100644 (file)
@@ -67,8 +67,9 @@ void MojoRendererWrapper::SetVideoHole(bool is_video_hole) {
   mojo_renderer_->SetVideoHole(is_video_hole);
 }
 
-void MojoRendererWrapper::SetMediaGeometry(const gfx::RectF& rect) {
-  mojo_renderer_->SetMediaGeometry(rect);
+void MojoRendererWrapper::SetMediaGeometry(const gfx::RectF& rect,
+                                           VideoRotation rotation) {
+  mojo_renderer_->SetMediaGeometry(rect, rotation);
 }
 #endif
 
@@ -77,6 +78,11 @@ base::TimeDelta MojoRendererWrapper::GetMediaTime() {
 }
 
 #if BUILDFLAG(IS_TIZEN_TV)
+void MojoRendererWrapper::GetVideoId(GetVideoIdCB cb) {
+  if (mojo_renderer_)
+    mojo_renderer_->GetVideoId(std::move(cb));
+}
+
 void MojoRendererWrapper::SetContentMimeType(const std::string& mime_type) {
   if (mojo_renderer_)
     mojo_renderer_->SetContentMimeType(mime_type);
index 4bf07fe..1dc7c14 100644 (file)
@@ -45,11 +45,14 @@ class MojoRendererWrapper : public Renderer {
 
 #if defined(TIZEN_VIDEO_HOLE)
   void SetVideoHole(bool is_video_hole) override;
-  void SetMediaGeometry(const gfx::RectF& rect) override;
+  void SetMediaGeometry(const gfx::RectF& rect,
+                        VideoRotation rotation) override;
 #endif
 
 #if BUILDFLAG(IS_TIZEN_TV)
   using StartDateCB = base::OnceCallback<void(double)>;
+  using GetVideoIdCB = base::OnceCallback<void(int32_t)>;
+  void GetVideoId(GetVideoIdCB cb) override;
   void SetContentMimeType(const std::string& mime_type) override;
   void SetParentalRatingResult(bool is_pass) override;
   void SetActiveTextTrack(int id, bool is_in_band) override;
index d86cdf8..7ea0505 100644 (file)
@@ -196,6 +196,7 @@ struct VideoDecoderConfig {
   EncryptionScheme encryption_scheme;
   VideoColorSpace color_space_info;
   gfx.mojom.HDRMetadata? hdr_metadata;
+  bool is_rtc;
   [EnableIf=is_tizen_tv]
   string hdr_info;
   [EnableIf=is_tizen_tv]
index b6aaaf0..79a0f93 100644 (file)
@@ -23,6 +23,15 @@ struct MediaUrlParams {
   bool is_hls;
 };
 
+[EnableIf=tizen_multimedia]
+enum HardwareResourceConfig {
+  kMainDecoderMainScaler = 0,
+  kSubDecoderSubScaler,
+  kSubDecoderMainScaler,
+  kMainDecoderSubScaler,
+  kNone,
+};
+
 // A Mojo equivalent of media::Renderer. Used when audio and video decoding
 // happens outside of the sandboxed render process.
 // See media/mojo/README.md
@@ -60,10 +69,25 @@ interface Renderer {
   SetCdm(mojo_base.mojom.UnguessableToken? cdm_id) => (bool success);
 
   [EnableIf=tizen_video_hole]
+  SetPlayerVideoAbove(int32 other_id);
+
+  [EnableIf=tizen_video_hole]
+  UseSubsurfaceController();
+
+  [EnableIf=tizen_video_hole]
   SetVideoHole(bool is_video_hole);
 
   [EnableIf=tizen_video_hole]
-  SetMediaGeometry(gfx.mojom.RectF rect);
+  SetMediaGeometry(gfx.mojom.RectF rect, VideoRotation rotation);
+
+  [EnableIf=tizen_multimedia]
+  EnableLowLatencyMode();
+
+  [EnableIf=tizen_multimedia]
+  SetHardwareResource(HardwareResourceConfig config) => (bool success);
+
+  [EnableIf=tizen_multimedia]
+  RequestVideoDecodedBuffer() => (bool success);
 
   [EnableIf=tizen_multimedia]
   ToggleFullscreenMode(bool is_fullscreen) => ();
@@ -72,6 +96,9 @@ interface Renderer {
   SetContentMimeType(string mime_type);
 
   [EnableIf=is_tizen_tv]
+  GetVideoId()=> (int32 player_id);
+
+  [EnableIf=is_tizen_tv]
   SetParentalRatingResult(bool is_pass);
 
   [EnableIf=is_tizen_tv]
index 966f077..b4a775c 100644 (file)
@@ -102,6 +102,12 @@ interface MediaPlayerRendererExtension {
 
   [EnableIf=tizen_tbm_support]
   OnTbmBufferExhausted(gfx.mojom.TbmBufferHandle tbm_buffer_handle);
+
+  [EnableIf=is_tizen_tv]
+  EnableTbmBufferCallback(bool enabled);
+
+  [EnableIf=is_tizen_tv]
+  SetCallBackFrameSize(gfx.mojom.Size size);
 };
 
 // Extension of the mojo::RendererClient communication layer for media flinging,
index ed74736..dcf30fa 100644 (file)
@@ -63,6 +63,8 @@ bool StructTraits<media::mojom::VideoDecoderConfigDataView,
 
   output->set_level(input.level());
 
+  output->set_is_rtc(input.is_rtc());
+
 #if BUILDFLAG(IS_TIZEN_TV)
   std::string hdr;
   if (!input.ReadHdrInfo(&hdr))
index 5dff94d..204c0ab 100644 (file)
@@ -74,6 +74,10 @@ struct StructTraits<media::mojom::VideoDecoderConfigDataView,
     return input.level();
   }
 
+  static bool is_rtc(const media::VideoDecoderConfig& input) {
+    return input.is_rtc();
+  }
+
 #if BUILDFLAG(IS_TIZEN_TV)
   static std::string hdr_info(const media::VideoDecoderConfig& input) {
     return input.hdr_info();
index 4d011ad..091f5f7 100644 (file)
@@ -112,6 +112,23 @@ void MojoRendererService::Suspend() {
   renderer_->Suspend();
 }
 
+void MojoRendererService::EnableLowLatencyMode() {
+  DVLOG(2) << __func__;
+  renderer_->EnableLowLatencyMode();
+}
+
+void MojoRendererService::SetHardwareResource(
+    media::mojom::HardwareResourceConfig config,
+    mojom::Renderer::SetHardwareResourceCallback cb) {
+  DVLOG(2) << __func__;
+  renderer_->SetHardwareResource(config, std::move(cb));
+}
+
+void MojoRendererService::RequestVideoDecodedBuffer(
+    mojom::Renderer::RequestVideoDecodedBufferCallback cb) {
+  renderer_->RequestVideoDecodedBuffer(std::move(cb));
+}
+
 void MojoRendererService::ToggleFullscreenMode(
     bool is_fullscreen,
     ToggleFullscreenModeCallback callback) {
@@ -178,12 +195,21 @@ void MojoRendererService::SetCdm(
 }
 
 #if defined(TIZEN_VIDEO_HOLE)
+void MojoRendererService::SetPlayerVideoAbove(int32_t other_id) {
+  renderer_->SetPlayerVideoAbove(other_id);
+}
+
+void MojoRendererService::UseSubsurfaceController() {
+  renderer_->UseSubsurfaceController();
+}
+
 void MojoRendererService::SetVideoHole(bool is_video_hole) {
   renderer_->SetVideoHole(is_video_hole);
 }
 
-void MojoRendererService::SetMediaGeometry(const gfx::RectF& rect) {
-  renderer_->SetMediaGeometry(rect);
+void MojoRendererService::SetMediaGeometry(const gfx::RectF& rect,
+                                           VideoRotation rotation) {
+  renderer_->SetMediaGeometry(rect, rotation);
 }
 #endif
 
@@ -210,6 +236,12 @@ void MojoRendererService::OnStatisticsUpdate(const PipelineStatistics& stats) {
 }
 
 #if BUILDFLAG(IS_TIZEN_TV)
+void MojoRendererService::GetVideoId(GetVideoIdCallback cb) {
+  DVLOG(3) << __func__;
+  if (renderer_)
+    renderer_->GetVideoId(std::move(cb));
+}
+
 void MojoRendererService::SetContentMimeType(const std::string& mime_type) {
   DVLOG(3) << __func__ << ", mime_type: " << mime_type;
   if (renderer_)
index c4ced5f..4ddd5a5 100644 (file)
@@ -68,6 +68,12 @@ class MEDIA_MOJO_EXPORT MojoRendererService final : public mojom::Renderer,
 #if defined(TIZEN_MULTIMEDIA)
   void Seek(base::TimeDelta time, base::OnceClosure seek_cb) final;
   void Suspend() final;
+  void EnableLowLatencyMode() final;
+  void SetHardwareResource(
+      media::mojom::HardwareResourceConfig config,
+      mojom::Renderer::SetHardwareResourceCallback cb) final;
+  void RequestVideoDecodedBuffer(
+      mojom::Renderer::RequestVideoDecodedBufferCallback cb) final;
   void ToggleFullscreenMode(bool is_fullscreen,
                             ToggleFullscreenModeCallback callback) final;
 #endif
@@ -79,10 +85,13 @@ class MEDIA_MOJO_EXPORT MojoRendererService final : public mojom::Renderer,
 
 #if defined(TIZEN_VIDEO_HOLE)
   void SetVideoHole(bool is_video_hole) final;
-  void SetMediaGeometry(const gfx::RectF& rect) final;
+  void SetMediaGeometry(const gfx::RectF& rect, VideoRotation rotation) final;
+  void SetPlayerVideoAbove(int32_t other_id) final;
+  void UseSubsurfaceController() final;
 #endif
 
 #if BUILDFLAG(IS_TIZEN_TV)
+  void GetVideoId(GetVideoIdCallback cb) final;
   void SetContentMimeType(const std::string& mime_type) final;
   void SetParentalRatingResult(bool is_pass) final;
   void SetActiveTextTrack(int id, bool is_in_band) final;
index 263cbea..e30f95c 100644 (file)
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_source.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_common.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
index ac40925..36d2c4e 100644 (file)
@@ -187,6 +187,9 @@ class BLINK_MODULES_EXPORT WebMediaPlayerMS
                             bool is_opaque);
   void OnOpacityChanged(bool is_opaque);
   void OnTransformChanged(media::VideoTransformation video_transform);
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+  void SetPlayerId(int id);
+#endif  // defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
 
   // WebMediaStreamObserver implementation
   void TrackAdded(const WebString& track_id) override;
@@ -349,6 +352,10 @@ class BLINK_MODULES_EXPORT WebMediaPlayerMS
   // Whether the use of a surface layer instead of a video layer is enabled.
   bool use_surface_layer_ = false;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  int player_id_{0};
+#endif
+
   // Owns the weblayer and obtains/maintains SurfaceIds for
   // kUseSurfaceLayerForVideo feature.
   std::unique_ptr<WebSurfaceLayerBridge> bridge_;
index dfbbff4..f4796ac 100644 (file)
@@ -138,18 +138,30 @@ void LocalVideoCapturerSource::OnStateUpdate(blink::VideoCaptureState state) {
     case VIDEO_CAPTURE_STATE_ERROR:
     case VIDEO_CAPTURE_STATE_ERROR_SYSTEM_PERMISSIONS_DENIED:
     case VIDEO_CAPTURE_STATE_ERROR_CAMERA_BUSY:
-    case VIDEO_CAPTURE_STATE_ENDED:
+    case VIDEO_CAPTURE_STATE_ENDED: {
+      // |release_device_cb_| will unref device by one, and UseDevice will ref
+      // device by one, call |release_device_cb_| first will cause a unnecessary
+      // device re-creating opt. for currently camera preview playback solution,
+      // geometry info from renderer may be missed durnning device recreating.
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_MULTIMEDIA)
+      base::OnceClosure post_release_device_cb = std::move(release_device_cb_);
+#else
       std::move(release_device_cb_).Run();
+#endif
       release_device_cb_ =
           frame && frame->Client()
               ? manager_->UseDevice(session_id_,
                                     &frame->GetBrowserInterfaceBroker())
               : base::DoNothing();
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_MULTIMEDIA)
+      std::move(post_release_device_cb).Run();
+#endif
       OnLog(
           "LocalVideoCapturerSource::OnStateUpdate signaling to "
           "consumer that source is no longer running.");
       running_callback_.Run(run_state);
       break;
+    }
 
     case VIDEO_CAPTURE_STATE_STARTING:
     case VIDEO_CAPTURE_STATE_PAUSED:
index bb3277e..cd2f694 100644 (file)
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
+#if defined(TIZEN_VIDEO_HOLE)
+#include "ui/gfx/geometry/rect_f.h"
+#endif
+
 #if BUILDFLAG(IS_TIZEN)
 #include "tizen/system_info.h"
 #endif
@@ -189,6 +193,63 @@ class WebMediaPlayerMS::FrameDeliverer {
 
   void OnVideoFrame(scoped_refptr<media::VideoFrame> frame) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(video_sequence_checker_);
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+    if (frame->metadata().player_id.has_value()) {
+      const int player_id = frame->metadata().player_id.value_or(0);
+      if (player_id != player_id_) {
+        player_id_ = player_id;
+        main_task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&WebMediaPlayerMS::SetPlayerId, player_,
+                                      player_id_));
+      }
+      if (frame->format() == media::PIXEL_FORMAT_ENCODED) {
+        auto orig_frame = frame;
+        frame = media::VideoFrame::CreateHoleFrame(orig_frame->natural_size());
+        frame->set_timestamp(orig_frame->timestamp());
+        frame->metadata().player_id = player_id_;
+      }
+    }
+
+    // Make new frame in case there is any TBM buffer attached to prevent
+    // keeping the buffer in case when rendering stalls
+    if (frame->storage_type() == media::VideoFrame::STORAGE_HOLE) {
+#if defined(TIZEN_TBM_SUPPORT)
+      if (frame->format() == media::PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER &&
+          request_frame_in_progress_) {
+        base::AutoLock auto_lock(request_buffer_lock_);
+        scoped_refptr<media::VideoFrame> decoded_video_frame =
+            media::VideoFrame::WrapExternalYuvData(
+                media::VideoPixelFormat::PIXEL_FORMAT_NV12, frame->coded_size(),
+                frame->visible_rect(), frame->natural_size(),
+                frame->stride(media::VideoFrame::kYPlane),
+                frame->stride(media::VideoFrame::kUVPlane),
+                frame->GetWritableVisibleData(media::VideoFrame::kYPlane),
+                frame->GetWritableVisibleData(media::VideoFrame::kUVPlane),
+                frame->timestamp());
+        request_frame_in_progress_ = false;
+        if (decoded_video_frame) {
+          decoded_video_frame->metadata().MergeMetadataFrom(frame->metadata());
+          decoded_frame_cb_.Run(std::move(decoded_video_frame));
+        }
+      }
+#endif  // defined(TIZEN_TBM_SUPPORT)
+      auto orig_frame = frame;
+      frame = media::VideoFrame::CreateHoleFrame(orig_frame->natural_size());
+      frame->set_timestamp(orig_frame->timestamp());
+      frame->metadata().MergeMetadataFrom(orig_frame->metadata());
+      EnqueueFrame(orig_frame->unique_id(), std::move(frame));
+      return;
+    } else if (frame->metadata().end_of_stream) {
+      // create a hole frame to prevent renderer from reading the dirty data in
+      // eos frame
+      auto orig_frame = frame;
+      frame = media::VideoFrame::CreateHoleFrame(orig_frame->natural_size());
+      frame->set_timestamp(orig_frame->timestamp());
+      frame->metadata().MergeMetadataFrom(orig_frame->metadata());
+      EnqueueFrame(orig_frame->unique_id(), std::move(frame));
+      return;
+    }
+#endif  // defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
 
 // On Android, stop passing frames.
 #if BUILDFLAG(IS_ANDROID)
@@ -330,6 +391,20 @@ class WebMediaPlayerMS::FrameDeliverer {
     weak_factory_for_pool_.InvalidateWeakPtrs();
   }
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_TBM_SUPPORT)
+  void RequestVideoDecodedBuffer() {
+    base::AutoLock auto_lock(request_buffer_lock_);
+    request_frame_in_progress_ = true;
+  }
+
+  RequestCB decoded_frame_cb_;
+  base::Lock request_buffer_lock_;
+  bool request_frame_in_progress_ = false;
+#endif
+
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+  int player_id_{0};
+#endif  // defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
   bool render_frame_suspended_ = false;
 
   const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
@@ -1313,12 +1388,31 @@ void WebMediaPlayerMS::OnTransformChanged(
     // Keep the old |video_layer_| alive until SetCcLayer() is called with a new
     // pointer, as it may use the pointer from the last call.
     auto new_video_layer =
-        cc::VideoLayer::Create(compositor_.get(), video_transform);
+        cc::VideoLayer::Create(compositor_.get(), video_transform
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+                               ,
+                               absl::optional<int>(player_id_)
+#endif
+        );
+
     get_client()->SetCcLayer(new_video_layer.get());
     video_layer_ = std::move(new_video_layer);
   }
 }
 
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+void WebMediaPlayerMS::SetPlayerId(int player_id) {
+  LOG(INFO) << __func__ << " player_id: " << player_id;
+  player_id_ = player_id;
+
+  if (video_layer_) {
+    // If we have a video_layer_ already, then we need to call
+    // OnTransformChanged to reset it with new player_id_.
+    OnTransformChanged(video_layer_->GetTransform());
+  }
+}
+#endif  // defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+
 bool WebMediaPlayerMS::IsInPictureInPicture() const {
   DCHECK(client_);
   return (!client_->IsInAutoPIP() &&
index 745f009..337cf39 100644 (file)
 #include "third_party/libyuv/include/libyuv/video_common.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+#include "media/blink/renderer/tizen_esplusplayer_renderer.h"
+#endif
+
 namespace WTF {
 
 template <typename T>
@@ -299,6 +304,37 @@ WebMediaPlayerMSCompositor::Metadata WebMediaPlayerMSCompositor::GetMetadata() {
   return current_metadata_;
 }
 
+#if defined(TIZEN_VIDEO_HOLE)
+void WebMediaPlayerMSCompositor::OnDrawableContentRectChanged(
+    const gfx::Rect& rect) {
+#if BUILDFLAG(IS_TIZEN_TV)
+  media::VideoRotation rotation = media::VIDEO_ROTATION_0;
+  if (current_frame_ && current_frame_->metadata().transformation.has_value())
+    rotation = current_frame_->metadata().transformation.value().rotation;
+
+  std::int32_t player_id{0};
+  {
+    base::AutoLock auto_lock(current_frame_lock_);
+    if (current_frame_) {
+      player_id = current_frame_->metadata().player_id.value_or(0);
+    }
+  }
+
+  LOG(INFO) << "WebMediaPlayerMSCompositor::OnDrawableContentRectChanged: "
+            << ", player id:" << player_id << ", rect:" << rect.ToString()
+            << ", rotation " << media::VideoRotationToString(rotation);
+
+  if (player_id != 0) {
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+    media::TizenEsPlusPlayerRendererManager::GetInstance()
+        .OnVideoSinkGeometryChange(player_id, gfx::RectF{rect}, rotation);
+#endif
+  }
+#endif  // BUILDFLAG(IS_TIZEN_TV)
+}
+#endif  // defined(TIZEN_VIDEO_HOLE)
+
 void WebMediaPlayerMSCompositor::SetForceSubmit(bool force_submit) {
   DCHECK(video_frame_compositor_task_runner_->BelongsToCurrentThread());
   submitter_->SetForceSubmit(force_submit);
index c6d73a6..9b81ccb 100644 (file)
@@ -153,7 +153,7 @@ class MODULES_EXPORT WebMediaPlayerMSCompositor
       DrawableContentRectChangedCallback cb) override {}
 
   // Notifies the client of video plane geometry to be use.
-  void OnDrawableContentRectChanged(const gfx::Rect&) override {}
+  void OnDrawableContentRectChanged(const gfx::Rect& rect) override;
 #endif
 
  private:
index 7062313..4a6bb85 100644 (file)
@@ -4,6 +4,10 @@
 
 import("//media/media_options.gni")
 
+if (tizen_multimedia) {
+  import("//tizen_src/chromium_impl/media/media_efl.gni")
+}
+
 # TODO(https://crbug.com/1198341): use blink_platform_sources once the code is
 # ported to Blink code conventions.
 component("media") {
@@ -108,6 +112,11 @@ component("media") {
       "hls_data_source_provider_impl.h",
     ]
   }
+
+  if (tizen_multimedia) {
+    configs += external_media_efl_blink_config
+    sources += external_media_efl_blink_sources
+  }
 }
 
 source_set("unit_tests") {
index 1542bc1..f11a400 100644 (file)
@@ -2104,7 +2104,12 @@ void WebMediaPlayerImpl::OnMetadata(const media::PipelineMetadata& metadata) {
       DCHECK(!video_layer_);
       video_layer_ = cc::VideoLayer::Create(
           compositor_.get(),
-          pipeline_metadata_.video_decoder_config.video_transformation());
+          pipeline_metadata_.video_decoder_config.video_transformation()
+#if defined(TIZEN_VIDEO_HOLE) && BUILDFLAG(IS_TIZEN_TV)
+              ,
+          absl::nullopt
+#endif
+      );
       video_layer_->SetContentsOpaque(opaque_);
       client_->SetCcLayer(video_layer_.get());
     }
index d3218bc..ef39ad3 100644 (file)
@@ -10,6 +10,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/types/optional_util.h"
 #include "media/base/media_switches.h"
+#include "media/base/video_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h"
 #include "third_party/blink/renderer/platform/webrtc/webrtc_video_utils.h"
@@ -223,6 +224,26 @@ void WebRtcVideoTrackSource::OnFrameCaptured(
         webrtc::Timestamp::Micros(frame->timestamp().InMicroseconds());
   }
 
+#if BUILDFLAG(IS_TIZEN)
+  if (frame->format() == media::PIXEL_FORMAT_ENCODED
+#if defined(TIZEN_VIDEO_HOLE)
+      || frame->storage_type() == media::VideoFrame::STORAGE_HOLE
+#endif
+  ) {
+    // The webrtc::VideoFrame::UpdateRect expected by WebRTC must
+    // be relative to the |visible_rect()|. We need to translate.
+    absl::optional<gfx::Rect> cropped_rect;
+    if (accumulated_update_rect_) {
+      cropped_rect =
+          CropRectangle(*accumulated_update_rect_, frame->visible_rect());
+    }
+
+    DeliverFrame(std::move(frame), base::OptionalToPtr(cropped_rect),
+                 translated_camera_time_us, capture_time_identifier);
+    return;
+  }
+#endif
+
   // Translate the |crop_*| values output by AdaptFrame() from natural size to
   // visible size. This is needed to apply the new cropping on top of any
   // existing soft-applied cropping and scaling when using
index 68bbcb5..0fcf81e 100644 (file)
@@ -854,7 +854,14 @@ void VideoCaptureImpl::StopCapture(int client_id) {
   DVLOG(1) << "StopCapture: No more client, stopping ...";
   StopDevice();
   client_buffers_.clear();
+#if !BUILDFLAG(IS_TIZEN_TV) && !defined(TIZEN_MULTIMEDIA)
+  // the current weak ptr is owned by capture impl manager, and this capture
+  // impl instance shall not be able to re-start again by invalidate weak ptr.
+  // it is supposed to re-creating capture impl instead, but re-creating is
+  // skipped on tizen tv product, so remove this line to make it available
+  // to respond to the follow-up callings.
   weak_factory_.InvalidateWeakPtrs();
+#endif
 }
 
 void VideoCaptureImpl::RequestRefreshFrame() {
@@ -940,7 +947,10 @@ void VideoCaptureImpl::OnStateChanged(
       OnLog("VideoCaptureImpl changing state to VIDEO_CAPTURE_STATE_STOPPED");
       state_ = VIDEO_CAPTURE_STATE_STOPPED;
       client_buffers_.clear();
+#if !BUILDFLAG(IS_TIZEN_TV) && !defined(TIZEN_MULTIMEDIA)
+      // see comments in StopCapture
       weak_factory_.InvalidateWeakPtrs();
+#endif
       if (!clients_.empty() || !clients_pending_on_restart_.empty()) {
         OnLog("VideoCaptureImpl restarting capture");
         RestartCapture();
@@ -1026,6 +1036,28 @@ void VideoCaptureImpl::OnBufferReady(
                        (reference_time - base::TimeTicks()).InMicroseconds(),
                        "time_delta", buffer->info->timestamp.InMicroseconds());
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+  // push shared buffer handle to tizen esplusplayer renderer
+  const auto& iter = client_buffers_.find(buffer->buffer_id);
+  DCHECK(iter != client_buffers_.end());
+  scoped_refptr<BufferContext> buffer_context = iter->second;
+  if (buffer_context->buffer_type() ==
+          VideoFrameBufferHandleType::kUnsafeShmemRegion &&
+      buffer->info->pixel_format == media::PIXEL_FORMAT_ENCODED) {
+    if (renderer_ && renderer_->IsReady()) {
+      renderer_->QueueBuffer(buffer->info->metadata, buffer->info->coded_size,
+                             buffer_context->data(),
+                             buffer->info->encoded_data_size,
+                             true /* key frame */);
+      GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer->buffer_id,
+                                           media::VideoCaptureFeedback());
+      return;
+    }
+    LOG(INFO) << "renderer is not exists, or not prepared.";
+  }
+#endif
+
   // Create and initialize frame preparers for the non-scaled and the scaled
   // frames.
   auto frame_preparer =
@@ -1076,6 +1108,18 @@ void VideoCaptureImpl::BindVideoFrameOnMediaThread(
   std::move(on_frame_ready_callback).Run(std::move(frame_preparer));
 }
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+void VideoCaptureImpl::OnVideoFrameReadyFromRenderer(
+    scoped_refptr<media::VideoFrame> frame,
+    base::TimeTicks reference_time) {
+  DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
+  for (const auto& client : clients_) {
+    client.second.deliver_frame_cb.Run(frame, reference_time);
+  }
+}
+#endif
+
 void VideoCaptureImpl::OnVideoFrameReady(
     base::TimeTicks reference_time,
     std::unique_ptr<VideoFrameBufferPreparer> frame_preparer) {
@@ -1217,6 +1261,27 @@ void VideoCaptureImpl::StartCaptureInternal() {
   }
   start_outcome_reported_ = false;
   base::UmaHistogramBoolean("Media.VideoCapture.Start", true);
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+  const media::VideoCodec codec =
+      (params_.requested_format.pixel_format == media::PIXEL_FORMAT_ENCODED
+           ? media::VideoCodec::kH264
+           : media::VideoCodec::kMJPEG);
+  if (!renderer_)
+    renderer_ = media::TizenEsPlusPlayerRendererManager::GetInstance()
+                    .CreateTizenEsPlusPlayerRenderer(
+                        codec, gfx::Size(1920, 1080),
+                        media::HardwareResouceType::kSub,  /* sub-scaler */
+                        media::HardwareResouceType::kMain, /* main-decoder */
+                        media::CanDropFrames::kYes, media::Mode::kVideoHole,
+                        base::DoNothing(),
+                        base::BindPostTaskToCurrentDefault(base::BindRepeating(
+                            &VideoCaptureImpl::OnVideoFrameReadyFromRenderer,
+                            base::Unretained(this))),
+                        true /* do not handle sink changes by listener */,
+                        true /* enable espp manual copy */);
+#endif  // BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) &&
+        // defined(TIZEN_MULTIMEDIA)
 
   GetVideoCaptureHost()->Start(device_id_, session_id_, params_,
                                observer_receiver_.BindNewPipeAndPassRemote());
index b0d9da0..bf4fb98 100644 (file)
 #include "third_party/blink/renderer/platform/allow_discouraged_type.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+#include "media/blink/renderer/tizen_esplusplayer_renderer.h"
+#endif
+
 namespace base {
 class SequencedTaskRunner;
 }  // namespace base
@@ -201,6 +206,11 @@ class PLATFORM_EXPORT VideoCaptureImpl
       base::OnceCallback<void(std::unique_ptr<VideoFrameBufferPreparer>)>
           on_frame_ready_callback,
       base::OnceCallback<void()> on_gpu_context_lost);
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+  void OnVideoFrameReadyFromRenderer(scoped_refptr<media::VideoFrame> frame,
+                                     base::TimeTicks reference_time);
+#endif
   void OnVideoFrameReady(
       base::TimeTicks reference_time,
       std::unique_ptr<VideoFrameBufferPreparer> frame_preparer);
@@ -309,6 +319,11 @@ class PLATFORM_EXPORT VideoCaptureImpl
   THREAD_CHECKER(io_thread_checker_);
 
   base::OneShotTimer startup_timeout_;
+#if BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) && \
+    defined(TIZEN_MULTIMEDIA)
+  std::unique_ptr<media::TizenEsPlusPlayerRenderer> renderer_;
+#endif  // BUILDFLAG(IS_TIZEN_TV) && defined(TIZEN_VIDEO_HOLE) &&
+        // defined(TIZEN_MULTIMEDIA)
 
   base::WeakPtr<VideoCaptureImpl> weak_this_;
   // WeakPtrFactory pointing back to |this| object, for use with
index fa8aaca..770f54b 100644 (file)
@@ -285,7 +285,10 @@ bool CanConvertToWebRtcVideoFrameBuffer(const media::VideoFrame* frame) {
                          frame->format())) ||
          frame->storage_type() ==
              media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER ||
-         frame->HasTextures();
+#if defined(TIZEN_VIDEO_HOLE)
+         frame->storage_type() == media::VideoFrame::STORAGE_HOLE ||
+#endif
+         frame->format() == media::PIXEL_FORMAT_ENCODED || frame->HasTextures();
 }
 
 // static
index 7278fa9..51abec6 100644 (file)
@@ -62,6 +62,8 @@ TizenRendererImpl::TizenRendererImpl(
       video_rect_(gfx::RectF()),
 #endif
 #if BUILDFLAG(IS_TIZEN_TV)
+      is_low_latency_(false),
+      hw_res_cfg_(-1),
       notify_playback_state_(media::kPlaybackStop),
 #endif
       renderer_extension_receiver_(this,
@@ -232,11 +234,20 @@ void TizenRendererImpl::SetPlayerInitialize() {
 #if defined(TIZEN_VIDEO_HOLE)
   if (is_video_hole_) {
     SetPlayerVideoHole();
-    SetPlayerMediaGeometry();
+    SetPlayerMediaGeometry(media::VIDEO_ROTATION_0);
   }
 #endif
 
   media_player_->Initialize(sink_);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (is_low_latency_) {
+    EnablePlayerLowLatencyMode();
+  }
+  if (hw_res_cfg_ != -1) {
+    SetPlayerHardwareResource(hw_res_cfg_);
+  }
+#endif
 }
 
 void TizenRendererImpl::SetPlayerPrepare() {
@@ -294,6 +305,24 @@ void TizenRendererImpl::Seek(base::TimeDelta time, base::OnceClosure seek_cb) {
 }
 
 #if defined(TIZEN_VIDEO_HOLE)
+void TizenRendererImpl::SetPlayerVideoAbove(int32_t other_id) {
+  if (media_player_) {
+    media_player_->SetPlayerVideoAbove(other_id);
+  }
+}
+
+void TizenRendererImpl::UseSubsurfaceController() {
+  if (use_subsurface_controller_ == true) {
+    LOG(INFO) << "Skip same setting.";
+    return;
+  }
+
+  if (media_player_) {
+    media_player_->UseSubsurfaceController();
+    use_subsurface_controller_ = true;
+  }
+}
+
 void TizenRendererImpl::SetVideoHole(bool is_video_hole) {
   if (is_video_hole_ == is_video_hole)
     return;
@@ -307,15 +336,17 @@ void TizenRendererImpl::SetPlayerVideoHole() {
     media_player_->SetVideoHole(is_video_hole_);
 }
 
-void TizenRendererImpl::SetMediaGeometry(const gfx::RectF& rect) {
+void TizenRendererImpl::SetMediaGeometry(const gfx::RectF& rect,
+                                         media::VideoRotation rotation) {
   if (video_rect_ == rect)
     return;
 
   video_rect_ = rect;
-  SetPlayerMediaGeometry();
+  video_rotation_ = rotation;
+  SetPlayerMediaGeometry(rotation);
 }
 
-void TizenRendererImpl::SetPlayerMediaGeometry() {
+void TizenRendererImpl::SetPlayerMediaGeometry(media::VideoRotation rotation) {
   if (media_player_) {
     // Always get the latest viewport rect.
     media_player_->SetMediaGeometry(GetViewportRect(), video_rect_);
@@ -343,7 +374,7 @@ gfx::Rect TizenRendererImpl::GetViewportRect() const {
 void TizenRendererImpl::OnWebViewMoved() {
   if (media_player_) {
     LOG_ID(INFO, player_id_) << __func__ << " Reset WebView-Position.";
-    SetPlayerMediaGeometry();
+    SetPlayerMediaGeometry(video_rotation_);
   }
 }
 #endif
@@ -370,7 +401,72 @@ void TizenRendererImpl::Suspend() {
   is_suspended_ = true;
 }
 
+void TizenRendererImpl::EnableLowLatencyMode() {
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (is_low_latency_)
+    return;
+
+  is_low_latency_ = true;
+  EnablePlayerLowLatencyMode();
+#endif
+}
+
+int ToPlayerHardwareResourceConfig(
+    media::mojom::HardwareResourceConfig config) {
+  switch (config) {
+    case media::mojom::HardwareResourceConfig::kMainDecoderMainScaler:
+      return 0;
+    case media::mojom::HardwareResourceConfig::kSubDecoderSubScaler:
+      return 1;
+    case media::mojom::HardwareResourceConfig::kSubDecoderMainScaler:
+      return 2;
+    case media::mojom::HardwareResourceConfig::kMainDecoderSubScaler:
+      return 3;
+    default:
+      return -1;
+  }
+}
+
+void TizenRendererImpl::SetHardwareResource(
+    media::mojom::HardwareResourceConfig config,
+    SetHardwareResourceCB cb) {
+#if BUILDFLAG(IS_TIZEN_TV)
+  int player_cfg = ToPlayerHardwareResourceConfig(config);
+  if (player_cfg == -1) {
+    LOG(ERROR) << "Invalid config.";
+    std::move(cb).Run(false);
+    return;
+  }
+
+  if (hw_res_cfg_ == player_cfg) {
+    std::move(cb).Run(true);
+    return;
+  }
+  hw_res_cfg_ = player_cfg;
+  SetPlayerHardwareResource(player_cfg);
+#endif
+  std::move(cb).Run(true);
+}
+
 #if BUILDFLAG(IS_TIZEN_TV)
+void TizenRendererImpl::EnablePlayerLowLatencyMode() {
+  if (!media_player_) {
+    LOG_ID(ERROR, player_id_) << "media_player_ is not created yet";
+    return;
+  }
+
+  media_player_->EnableLowLatencyMode();
+}
+
+void TizenRendererImpl::SetPlayerHardwareResource(int config) {
+  if (!media_player_) {
+    LOG_ID(ERROR, player_id_) << "media_player_ is not created yet";
+    return;
+  }
+
+  media_player_->SetHardwareResource(config);
+}
+
 void TizenRendererImpl::SetActiveTextTrack(int id, bool is_in_band) {
   LOG_ID(INFO, player_id_) << "(" << static_cast<void*>(this) << ") "
                            << __func__ << " " << id << "/ " << is_in_band;
@@ -438,6 +534,18 @@ content::WebContentsDelegate* TizenRendererImpl::GetWebContentsDelegate()
 }
 #endif
 
+void TizenRendererImpl::RequestVideoDecodedBuffer(
+    RequestVideoDecodedBufferCB cb) {
+  if (!media_player_) {
+    LOG_ID(ERROR, player_id_) << "media_player_ is not created yet";
+    std::move(cb).Run(false);
+    return;
+  }
+
+  media_player_->RequestVideoDecodedBuffer();
+  std::move(cb).Run(true);
+}
+
 void TizenRendererImpl::ToggleFullscreenMode(bool is_fullscreen,
                                              ToggledFullscreenCB cb) {
   if (media_player_)
@@ -539,6 +647,7 @@ void TizenRendererImpl::OnStatisticsUpdate(
     const media::PipelineStatistics& stats) {
   NOTIMPLEMENTED();
 }
+
 #if BUILDFLAG(IS_TIZEN_TV)
 void TizenRendererImpl::NotifyTrackInfoToBrowser(int active_track_id) {
   LOG_ID(INFO, player_id_)
@@ -657,7 +766,27 @@ void TizenRendererImpl::OnLivePlaybackComplete() {
   }
   client_->OnLivePlaybackComplete();
 }
-#endif
+
+void TizenRendererImpl::GetVideoId(GetVideoIdCB cb) {
+  std::move(cb).Run(player_id_);
+}
+
+void TizenRendererImpl::EnableTbmBufferCallback(bool enable) {
+  if (!media_player_) {
+    LOG_ID(ERROR, player_id_) << "media_player_ is not created yet";
+    return;
+  }
+  media_player_->EnableTbmBufferCallback(enable);
+}
+
+void TizenRendererImpl::SetCallBackFrameSize(const gfx::Size& size) {
+  if (!media_player_) {
+    LOG_ID(ERROR, player_id_) << "media_player_ is not created yet";
+    return;
+  }
+  media_player_->SetCallBackFrameSize(size);
+}
+#endif  // BUILDFLAG(IS_TIZEN_TV)
 
 void TizenRendererImpl::OnSeekableTimeChange(base::TimeDelta min_time,
                                              base::TimeDelta max_time,
index e45d45a..6753740 100644 (file)
@@ -17,6 +17,7 @@
 #include "media/base/renderer_client.h"
 #include "media/base/video_renderer_sink.h"
 #include "media/base/waiting.h"
+#include "media/mojo/mojom/renderer.mojom.h"
 #include "media/mojo/mojom/renderer_extensions.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -87,11 +88,19 @@ class CONTENT_EXPORT TizenRendererImpl
   void OnUpdateAudioMutingState(bool muted) {}
   void OnWebContentsDestroyed();
 
-  void Seek(base::TimeDelta time, base::OnceClosure seek_cb);
+  void Seek(base::TimeDelta time, base::OnceClosure seek_cb) override;
 
   void Suspend() override;
 
+  void EnableLowLatencyMode() override;
+  void SetHardwareResource(media::mojom::HardwareResourceConfig config,
+                           SetHardwareResourceCB cb) override;
+  void RequestVideoDecodedBuffer(RequestVideoDecodedBufferCB cb) override;
   void ToggleFullscreenMode(bool is_fullscreen, ToggledFullscreenCB cb) final;
+#if defined(TIZEN_VIDEO_HOLE)
+  void SetPlayerVideoAbove(int32_t other_id) override;
+  void UseSubsurfaceController() override;
+#endif
 
   // MediaPlayerTizenClient implementation.
   void OnError(media::PipelineStatus error) override;
@@ -130,6 +139,9 @@ class CONTENT_EXPORT TizenRendererImpl
                            std::string* drm_info = NULL) override;
   void UpdateCurrentTime(base::TimeDelta current_time) override;
   void OnLivePlaybackComplete() override;
+  void GetVideoId(GetVideoIdCB cb) override;
+  void SetCallBackFrameSize(const gfx::Size& size) override;
+  void EnableTbmBufferCallback(bool enable) override;
 #endif
 
 #if defined(TIZEN_TBM_SUPPORT)
@@ -152,7 +164,8 @@ class CONTENT_EXPORT TizenRendererImpl
 
 #if defined(TIZEN_VIDEO_HOLE)
   void SetVideoHole(bool is_video_hole) final;
-  void SetMediaGeometry(const gfx::RectF& rect) final;
+  void SetMediaGeometry(const gfx::RectF& rect,
+                        media::VideoRotation rotation) final;
   gfx::Rect GetViewportRect() const;
 #endif
 
@@ -182,10 +195,12 @@ class CONTENT_EXPORT TizenRendererImpl
   void SetPlayerPrepare();
 #if defined(TIZEN_VIDEO_HOLE)
   void SetPlayerVideoHole();
-  void SetPlayerMediaGeometry();
+  void SetPlayerMediaGeometry(media::VideoRotation rotation);
   void OnWebViewMoved();
 #endif
 #if BUILDFLAG(IS_TIZEN_TV)
+  void EnablePlayerLowLatencyMode();
+  void SetPlayerHardwareResource(int config);
   void SetActiveTextTrack(int id, bool is_in_band) override;
   void SetActiveAudioTrack(int index) override;
   void SetActiveVideoTrack(int index) override;
@@ -242,11 +257,16 @@ class CONTENT_EXPORT TizenRendererImpl
   bool is_suspended_ = false;
 
 #if defined(TIZEN_VIDEO_HOLE)
+  bool use_subsurface_controller_ = false;
   bool is_video_hole_ = false;
   gfx::RectF video_rect_;
+  media::VideoRotation video_rotation_ = media::VIDEO_ROTATION_0;
 #endif
 
 #if BUILDFLAG(IS_TIZEN_TV)
+  bool is_low_latency_;
+  int hw_res_cfg_;
+
   int notify_playback_state_;
   // Stores the mime type.  Required for URL streams which are DASH
   // content, so that we can set it to the media_player_ before calling
index caf7284..7c8729e 100644 (file)
@@ -75,8 +75,10 @@ void MediaPlayerRendererClient::SetVideoHole(bool is_video_hole) {
   MojoRendererWrapper::SetVideoHole(is_video_hole);
 }
 
-void MediaPlayerRendererClient::SetMediaGeometry(const gfx::RectF& rect) {
-  MojoRendererWrapper::SetMediaGeometry(rect);
+void MediaPlayerRendererClient::SetMediaGeometry(
+    const gfx::RectF& rect,
+    media::VideoRotation rotation) {
+  MojoRendererWrapper::SetMediaGeometry(rect, rotation);
 }
 #endif
 
index 5109bf4..884695e 100644 (file)
@@ -67,7 +67,8 @@ class CONTENT_EXPORT MediaPlayerRendererClient
 
 #if defined(TIZEN_VIDEO_HOLE)
   void SetVideoHole(bool is_video_hole) override;
-  void SetMediaGeometry(const gfx::RectF& rect) override;
+  void SetMediaGeometry(const gfx::RectF& rect,
+                        media::VideoRotation rotation) override;
 #endif
 
   // media::mojom::MediaPlayerRendererClientExtension implementation
index da4df17..a5653b0 100644 (file)
@@ -60,7 +60,7 @@ bool CapiUsbAudioInputStream::OpenMic() {
     return false;
   }
 
-  int device_id = std::stoi(device_name_, nullptr, 0);
+  int device_id = std::atoi(device_name_.c_str());
   sound_device_h device;
   sound_device_type_e device_type;
   sound_stream_type_e stream_type = SOUND_STREAM_TYPE_MEDIA_EXTERNAL_ONLY;
diff --git a/tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.cc b/tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.cc
new file mode 100644 (file)
index 0000000..306588d
--- /dev/null
@@ -0,0 +1,883 @@
+// Copyright 2022 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/blink/renderer/tizen_esplusplayer_renderer.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_frame_visitor.h"
+#include "content/renderer/media/media_interface_factory.h"
+#include "media/base/video_codecs.h"
+#include "media/renderers/video_overlay_factory.h"
+// #include "ui/gfx/tbm_buffer_handle_inter_process.h"
+
+#if defined(ENABLE_AUTO_ZOOM)
+#include "media/mojo/mojom/ai_zoom_settings.mojom.h"
+#endif  // defined(ENABLE_AUTO_ZOOM)
+
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+#include "content/renderer/render_thread_impl.h"
+#endif
+
+#define LOG_ID(severity) LOG(severity) << "[" << id_ << "]: "
+
+namespace {
+class MainFrameFinder : public content::RenderFrameVisitor {
+ public:
+  content::RenderFrame* result{nullptr};
+  bool Visit(content::RenderFrame* render_frame) final {
+    if (render_frame && render_frame->IsMainFrame()) {
+      result = render_frame;
+      return false;
+    }
+    return true;
+  }
+};
+}  // namespace
+
+namespace media {
+
+TizenEsPlusPlayerRendererManager::TizenEsPlusPlayerRendererManager() = default;
+
+TizenEsPlusPlayerRendererManager::~TizenEsPlusPlayerRendererManager() {
+  NOTREACHED();
+}
+
+// static
+TizenEsPlusPlayerRendererManager&
+TizenEsPlusPlayerRendererManager::GetInstance() {
+  static base::NoDestructor<TizenEsPlusPlayerRendererManager> instance;
+  return *instance;
+}
+
+std::unique_ptr<TizenEsPlusPlayerRenderer>
+TizenEsPlusPlayerRendererManager::CreateTizenEsPlusPlayerRenderer(
+    media::VideoCodec codec,
+    gfx::Size init_size,
+    media::HardwareResouceType scaler_type,
+    media::HardwareResouceType decoder_type,
+    media::CanDropFrames can_drop_frames,
+    media::Mode mode,
+    media::InitCb init_cb,
+    media::FrameCb frame_cb,
+    bool has_id,
+    bool could_enable_user_data) {
+  base::AutoLock lk(lock_);
+
+  // An instance with |renderer_id| equal to 0 means that it will update the
+  // geometry to ESPP by registering and responding to listener events.
+  auto selected_id = 0;
+  if (has_id)
+    selected_id = ++index_;
+
+  auto id_iter = id_to_renderers_.find(selected_id);
+  if (id_iter != id_to_renderers_.end()) {
+    LOG(ERROR) << "Renderer already exists.";
+    return nullptr;
+  }
+
+  if (!SelectHWResource(scaler_type, decoder_type, mode)) {
+    LOG(ERROR) << "Resource conflicts or invalid combination.";
+    return nullptr;
+  }
+
+  auto renderer = base::WrapUnique(new TizenEsPlusPlayerRenderer(
+      codec, init_size, scaler_type, decoder_type, can_drop_frames, mode,
+      std::move(init_cb), std::move(frame_cb), selected_id,
+      could_enable_user_data));
+  id_to_renderers_[selected_id] = renderer->GetWeakPtr();
+  return renderer;
+}
+
+base::WeakPtr<TizenEsPlusPlayerRenderer>
+TizenEsPlusPlayerRendererManager::GetTizenEsPlusPlayerRenderer(int id) {
+  auto id_iter = id_to_renderers_.find(id);
+  if (id_iter == id_to_renderers_.end()) {
+    LOG(ERROR) << "Renderer does not exist:" << id;
+    return nullptr;
+  }
+
+  auto renderer = id_to_renderers_[id];
+  if (!renderer) {
+    LOG(ERROR) << "Renderer is invalid:" << id;
+    return nullptr;
+  }
+
+  return renderer;
+}
+
+void TizenEsPlusPlayerRendererManager::OnVideoSinkGeometryChange(
+    int id,
+    const gfx::RectF& rect,
+    media::VideoRotation rotation) {
+  base::AutoLock lk(lock_);
+  auto renderer = GetTizenEsPlusPlayerRenderer(id);
+  if (renderer)
+    renderer->OnVideoSinkGeometryChange(rect, rotation);
+}
+
+void TizenEsPlusPlayerRendererManager::OnZOrderChanged(
+    int broadcast_id,
+    const std::vector<int>& video_ids) {
+  base::AutoLock lk(lock_);
+
+  size_t size = video_ids.size();
+  if (size < 2) {
+    return;
+  }
+
+  // Skip same z-order
+  if (video_ids_order_ == video_ids) {
+    return;
+  }
+
+  for (size_t i = 0; i < size - 1; i++) {
+    auto renderer = GetTizenEsPlusPlayerRenderer(video_ids[i]);
+    if (!renderer)
+      continue;
+
+    for (size_t j = i + 1; j < size; j++) {
+      auto renderer_other = GetTizenEsPlusPlayerRenderer(video_ids[j]);
+      if (!renderer_other)
+        continue;
+      renderer->SetPlayerVideoAbove(renderer_other->GetVideoId());
+      break;
+    }
+  }
+  video_ids_order_ = video_ids;
+}
+
+void TizenEsPlusPlayerRendererManager::RemoveTizenEsPlusPlayerRenderer(int id) {
+  base::AutoLock lk(lock_);
+  auto it = id_to_renderers_.find(id);
+  video_ids_order_.clear();
+  if (it != id_to_renderers_.end())
+    id_to_renderers_.erase(it);
+}
+
+bool TizenEsPlusPlayerRendererManager::HasResourceConflict(
+    const media::SelectedResource& type) {
+  for (const auto& it : id_to_renderers_) {
+    if (it.second->selected_resource().first == type.first ||
+        it.second->selected_resource().second == type.second) {
+      return true;
+    }
+  }
+  return false;
+};
+
+bool TizenEsPlusPlayerRendererManager::HasResourceConflictScaler(
+    const media::HardwareResouceType& type) {
+  for (const auto& it : id_to_renderers_) {
+    if (it.second->selected_resource().first == type) {
+      return true;
+    }
+  }
+  return false;
+};
+
+bool TizenEsPlusPlayerRendererManager::HasResourceConflictDecoder(
+    const media::HardwareResouceType& type) {
+  for (const auto& it : id_to_renderers_) {
+    if (it.second->selected_resource().second == type) {
+      return true;
+    }
+  }
+  return false;
+};
+
+bool TizenEsPlusPlayerRendererManager::SelectHWResource(
+    media::HardwareResouceType& scaler_type,
+    media::HardwareResouceType& decoder_type,
+    const media::Mode& mode) {
+  if (mode == media::Mode::kVideoHoleWithMappingBuffer &&
+      scaler_type == media::HardwareResouceType::kSub) {
+    LOG(ERROR) << "Invalid combination, mode:" << static_cast<int>(mode)
+               << " scaler_type:" << static_cast<int>(scaler_type)
+               << " mode:" << static_cast<int>(mode);
+    return false;
+  }
+
+  if (HasResourceConflict({scaler_type, decoder_type})) {
+    return false;
+  }
+
+  // select for scaler kAny case
+  if (scaler_type == media::HardwareResouceType::kAny) {
+    while (scaler_type != media::HardwareResouceType::kEnd) {
+      scaler_type = static_cast<media::HardwareResouceType>(
+          static_cast<int>(scaler_type) + 1);
+      if (!HasResourceConflictScaler(scaler_type)) {
+        break;
+      }
+    }
+  }
+  if (scaler_type == media::HardwareResouceType::kEnd) {
+    return false;
+  }
+
+  // select for decoder kAny case
+  if (decoder_type == media::HardwareResouceType::kAny) {
+    while (decoder_type != media::HardwareResouceType::kEnd) {
+      decoder_type = static_cast<media::HardwareResouceType>(
+          static_cast<int>(decoder_type) + 1);
+      if (!HasResourceConflictDecoder(decoder_type)) {
+        break;
+      }
+    }
+  }
+  if (decoder_type == media::HardwareResouceType::kEnd) {
+    return false;
+  }
+
+  return true;
+}
+
+TizenEsPlusPlayerRenderer::TizenEsPlusPlayerRenderer(
+    media::VideoCodec codec,
+    gfx::Size init_size,
+    HardwareResouceType scaler_type,
+    HardwareResouceType decoder_type,
+    CanDropFrames can_drop_frames,
+    Mode mode,
+    InitCb init_cb,
+    FrameCb frame_cb,
+    int renderer_id,
+    bool could_enable_user_data)
+    : player_state_(Status::NONE),
+      codec_(codec),
+      current_frame_size_(init_size),
+      init_cb_(std::move(init_cb)),
+      frame_cb_(std::move(frame_cb)),
+      mode_(mode),
+      tbm_frame_in_use_(0),
+      pending_disable_tbm_(false),
+      id_(renderer_id),
+      could_enable_user_data_(could_enable_user_data) {
+  LOG_ID(INFO) << "Create renderer id:" << id_
+               << " mode:" << static_cast<int>(mode)
+               << " codec:" << static_cast<int>(codec)
+               << " hardware scaler:" << static_cast<int>(scaler_type)
+               << " hardware decoder:" << static_cast<int>(decoder_type)
+               << " can_drop_frames:" << static_cast<int>(can_drop_frames)
+               << " init size:" << init_size.ToString()
+               << " could_enable_user_data:" << could_enable_user_data;
+  log_ = std::make_unique<media::NullMediaLog>();
+  selected_resource_ = std::make_pair(scaler_type, decoder_type);
+
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+  GetScalerUsageManager();
+#endif
+
+  // Unretained is safe because |this| will outlive |worker_task_runner_|.
+  worker_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TizenEsPlusPlayerRenderer::Init, base::Unretained(this)));
+}
+
+TizenEsPlusPlayerRenderer::~TizenEsPlusPlayerRenderer() {
+  // Make sure |mojo_renderer_| is deleted before |worker_task_runner_| on which
+  // all |mojo_renderer_| related task is running
+  base::WaitableEvent sync_with_worker_task_runner{
+      base::WaitableEvent::ResetPolicy::MANUAL};
+  worker_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TizenEsPlusPlayerRenderer::ReleaseRendererOnWorkThread,
+                     base::Unretained(this), &sync_with_worker_task_runner));
+  sync_with_worker_task_runner.Wait();
+  LOG_ID(INFO) << " Mojo renderer released.";
+}
+
+void TizenEsPlusPlayerRenderer::DidTakeResources() {
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+  if (scaler_manager_ && uses_subresource_.has_value() &&
+      uses_subresource_.value())
+    scaler_manager_->SetUsed(true);
+#endif
+}
+
+void TizenEsPlusPlayerRenderer::DidFreeResources() {
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+  if (scaler_manager_ && uses_subresource_.has_value() &&
+      uses_subresource_.value())
+    scaler_manager_->SetUsed(false);
+#endif
+}
+
+void TizenEsPlusPlayerRenderer::SetGeometryOnWorkerThread(
+    const gfx::RectF& rect,
+    media::VideoRotation rotation) {
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  mojo_renderer_->SetMediaGeometry(rect, rotation);
+}
+
+void TizenEsPlusPlayerRenderer::StartPlayingOnWorkerThread(
+    base::TimeDelta time) {
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  mojo_renderer_->StartPlayingFrom(time);
+  player_state_ = Status::PREPARED;
+}
+
+void TizenEsPlusPlayerRenderer::ReleaseRendererOnWorkThread(
+    base::WaitableEvent* event) {
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  media::TizenEsPlusPlayerRendererManager::GetInstance()
+      .RemoveTizenEsPlusPlayerRenderer(id_);
+  // invalidate all weak ptrs at once to prevent any task from running again.
+  weak_factory_.InvalidateWeakPtrs();
+  mojo_renderer_.reset();
+  renderer_extension_remote_.reset();
+  client_extension_receiver_.reset();
+  event->Signal();
+}
+
+void TizenEsPlusPlayerRenderer::OnHardwareResourcesComplete(bool success) {
+  LOG_ID(INFO) << " success:" << success;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  if (success && selected_resource_.first == HardwareResouceType::kSub) {
+    uses_subresource_ = true;
+    DidTakeResources();
+  }
+}
+
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+void TizenEsPlusPlayerRenderer::GetScalerUsageManager() {
+  LOG_ID(INFO) << __func__;
+  base::WaitableEvent event{base::WaitableEvent::ResetPolicy::MANUAL};
+  content::RenderThreadImpl::DeprecatedGetMainTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WaitableEvent* event, media::ScalerUsageManager** manager) {
+            *manager =
+                content::RenderThreadImpl::current()->GetScalerUsageManager();
+            event->Signal();
+          },
+          &event, &scaler_manager_));
+
+  event.Wait();
+}
+#endif
+
+mojom::HardwareResourceConfig
+TizenEsPlusPlayerRenderer::ToMojomHardwareResourceConfig(
+    SelectedResource types) {
+  if (types.first == HardwareResouceType::kMain) {
+    if (types.second == HardwareResouceType::kMain) {
+      return mojom::HardwareResourceConfig::kMainDecoderMainScaler;
+    } else if (types.second == HardwareResouceType::kSub) {
+      return mojom::HardwareResourceConfig::kSubDecoderMainScaler;
+    }
+  } else if (types.first == HardwareResouceType::kSub) {
+    if (types.second == HardwareResouceType::kMain) {
+      return mojom::HardwareResourceConfig::kMainDecoderSubScaler;
+    } else if (types.second == HardwareResouceType::kSub) {
+      return mojom::HardwareResourceConfig::kSubDecoderSubScaler;
+    }
+  }
+  return mojom::HardwareResourceConfig::kNone;
+}
+
+void TizenEsPlusPlayerRenderer::SelectHardwareResources() {
+  LOG_ID(INFO) << __func__;
+  if (mode_ != Mode::kVideoHole && mode_ != Mode::kVideoHoleWithMappingBuffer) {
+    LOG_ID(INFO) << "Skip hardware resource selecting.";
+    return;
+  }
+
+  mojom::HardwareResourceConfig cfg =
+      ToMojomHardwareResourceConfig(selected_resource_);
+  if (cfg == mojom::HardwareResourceConfig::kNone) {
+    LOG_ID(INFO) << "Invalid hardware resource selecting.";
+    return;
+  }
+
+  mojo_renderer_->SetHardwareResource(
+      std::move(cfg),
+      base::BindOnce(&TizenEsPlusPlayerRenderer::OnHardwareResourcesComplete,
+                     GetWeakPtr()));
+}
+
+void TizenEsPlusPlayerRenderer::SetupBeforePlaying() {
+  LOG_ID(INFO) << __func__;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+
+  if (player_state_ != Status::INITIALISING) {
+    LOG_ID(ERROR) << "Wrong status.";
+    return;
+  }
+
+  bool OffscreenRenderingEnabled =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableOffscreenRendering);
+  if (mode_ == Mode::kVideoHole || mode_ == Mode::kVideoHoleWithMappingBuffer) {
+    mojo_renderer_->SetVideoHole(true);
+    if (!OffscreenRenderingEnabled) {
+      LOG_ID(INFO)
+          << __func__
+          << ", offscreen rendering is not enabled, use subsurface controller";
+      mojo_renderer_->UseSubsurfaceController();
+    } else {
+      // fixme: in this case (web browser), there will be z-order issues in dual
+      // decoder
+      LOG_ID(INFO) << __func__
+                   << ", offscreen rendering is enabled, can't use subsurface "
+                      "controller";
+    }
+  }
+
+  mojo_renderer_->EnableLowLatencyMode();
+  mojo_renderer_->SetPlaybackRate(1.0);
+  SelectHardwareResources();
+  mojo_renderer_->GetVideoId(base::BindOnce(
+      &TizenEsPlusPlayerRenderer::OnGetVideoId, weak_factory_.GetWeakPtr()));
+}
+
+VideoCodecProfile TizenEsPlusPlayerRenderer::GetCodecProfile(VideoCodec codec) {
+  LOG_ID(INFO) << __func__;
+  VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
+  if (codec_ == VideoCodec::kMJPEG) {
+    profile = VIDEO_CODEC_PROFILE_UNKNOWN;
+  } else if (codec == VideoCodec::kH264) {
+    profile = H264PROFILE_BASELINE;
+  } else if (codec == VideoCodec::kVP8) {
+    profile = VP8PROFILE_MIN;
+  } else {
+    LOG_ID(ERROR) << "Unknown codec:" << codec;
+  }
+
+  return profile;
+}
+
+bool TizenEsPlusPlayerRenderer::UpdateVideoConfig() {
+  LOG_ID(INFO) << __func__;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+
+  if (!media_resource_) {
+    LOG_ID(INFO) << "No source created yet, create new one.";
+    media_resource_ = std::make_unique<StreamResource>();
+    media_resource_->ResetStream();
+  }
+
+  ChunkDemuxerStream* stream = media_resource_->GetVideoStream();
+  if (!stream) {
+    LOG_ID(ERROR) << "No video stream.";
+    return false;
+  }
+
+  VideoDecoderConfig cfg = VideoDecoderConfig(
+      codec_, GetCodecProfile(codec_), VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(), VideoTransformation(), current_frame_size_,
+      gfx::Rect{0, 0, current_frame_size_.width(),
+                current_frame_size_.height()},
+      current_frame_size_, std::vector<uint8_t>{},
+      EncryptionScheme::kUnencrypted);
+  // it is needed to set espp to manual copy mode now if we want to enable user
+  // data later.
+  cfg.set_enable_manual_copy(could_enable_user_data_);
+
+  // Fix the video max resolution to FHD before setting it to ESPP to ensure
+  // that our hardware decoder selection logic is not disrupted. That is to say
+  // we hope the allocated decoder is 'just enough' rather than 'more than
+  // enough'.
+  cfg.set_max_coded_size({kFHDVideoMaxWidth, kFHDVideoMaxHeight});
+  cfg.set_is_rtc(true);
+  return stream->UpdateVideoConfig(cfg, false, log_.get());
+}
+
+void TizenEsPlusPlayerRenderer::Init() {
+  TRACE_EVENT0("media", "TizenEsPlusPlayerRenderer::Init");
+  LOG_ID(INFO) << __func__;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+
+  MainFrameFinder find_main_frame;
+  content::RenderFrame::ForEach(&find_main_frame);
+  if (!find_main_frame.result) {
+    LOG_ID(ERROR)
+        << "Couldn't find main render frame, EsPlusPlayer Renderer will "
+           "be disabled";
+    return;
+  }
+
+  player_state_ = Status::INITIALISING;
+
+  // init mojo
+  media_interface_factory_ = std::make_unique<content::MediaInterfaceFactory>(
+      find_main_frame.result->GetBrowserInterfaceBroker());
+
+  mojo::PendingRemote<mojom::Renderer> renderer_remote;
+  media_interface_factory_->CreateMediaPlayerRenderer(
+      client_extension_receiver_.BindNewPipeAndPassRemote(),
+      renderer_remote.InitWithNewPipeAndPassReceiver(),
+      renderer_extension_remote_.BindNewPipeAndPassReceiver());
+
+  mojo_renderer_ = std::make_unique<MojoRenderer>(
+      worker_task_runner_, nullptr, /* VideoOverlayFactory */
+      nullptr,                      /* VideoRendererSink */
+      std::move(renderer_remote));
+
+  // init source and esplusplayer settings
+  UpdateVideoConfig();
+
+  // init renderer
+  mojo_renderer_->Initialize(
+      media_resource_.get(), this,
+      base::BindOnce(&TizenEsPlusPlayerRenderer::OnRendererInitResult,
+                     GetWeakPtr()));
+
+#if defined(ENABLE_AUTO_ZOOM)
+  if (pending_ai_zoom_settings_) {
+    SetAiZoomSettings(*pending_ai_zoom_settings_);
+    pending_ai_zoom_settings_.reset();
+  }
+#endif  // defined(ENABLE_AUTO_ZOOM)
+}
+
+void TizenEsPlusPlayerRenderer::OnGetVideoId(int32_t player_id) {
+  LOG_ID(INFO) << " player_id:" << player_id;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  video_id_ = player_id;
+}
+
+void TizenEsPlusPlayerRenderer::OnRendererInitResult(PipelineStatus status) {
+  LOG_ID(INFO) << " status:" << status;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+
+  if (status != PIPELINE_OK) {
+    player_state_ = Status::ERROR;
+    if (init_cb_)
+      std::move(init_cb_).Run(false);
+    return;
+  }
+
+  SetupBeforePlaying();
+  player_state_ = Status::INITIALISED;
+  if (init_cb_)
+    std::move(init_cb_).Run(true);
+}
+
+bool TizenEsPlusPlayerRenderer::IsReady() {
+  if (player_state_ == Status::INITIALISED ||
+      player_state_ == Status::PREPARED) {
+    return true;
+  }
+  return false;
+}
+
+VideoFrameMetadata TizenEsPlusPlayerRenderer::GetMetaData(
+    base::TimeDelta timestamp) {
+  PendingFrame pending_frame;
+  {
+    base::AutoLock lk(pending_frames_lock_);
+    while (!pending_frames_.empty()) {
+      pending_frame = pending_frames_.front();
+      if (pending_frame.timestamp.InMilliseconds() >=
+          timestamp.InMilliseconds()) {
+        if (pending_frame.timestamp.InMilliseconds() ==
+            timestamp.InMilliseconds()) {
+          pending_frames_.pop_front();
+        }
+        break;
+      } else {
+        pending_frames_.pop_front();
+      }
+    }
+  }
+  if (pending_frame.timestamp.InMilliseconds() != timestamp.InMilliseconds()) {
+    LOG_ID(ERROR) << "Could not find pending frame by timestamp: "
+                  << timestamp.InMilliseconds();
+    return lastVideoFrameMeta_;
+  }
+  return pending_frame.metadata.value();
+}
+
+bool TizenEsPlusPlayerRenderer::QueueBuffer(const VideoFrameMetadata& meta,
+                                            const gfx::Size& coded_size,
+                                            const uint8_t* data,
+                                            std::size_t size,
+                                            bool key_frame) {
+  UpdateLastCaptureResolution(meta);
+
+  ChunkDemuxerStream* stream = media_resource_->GetVideoStream();
+  if (!stream) {
+    LOG_ID(ERROR) << "No video stream.";
+    return true;
+  }
+
+  base::TimeDelta ts =
+      meta.reference_time.value_or(base::TimeTicks()) - base::TimeTicks();
+  TRACE_EVENT2("media", "TizenEsPlusPlayerRenderer::QueueBuffer", "key_frame",
+               key_frame, "timestamp", ts.InMilliseconds());
+  bool request_key_frame = false;
+  {
+    base::AutoLock lk(append_frame_lock_);
+    StreamParser::BufferQueue buffer_queue;
+    auto buffer = StreamParserBuffer::CopyFrom(data, size, key_frame,
+                                               DemuxerStream::Type::VIDEO, 0);
+    buffer->set_timestamp(ts);
+    buffer->set_duration(base::Milliseconds(33));
+    buffer->set_is_duration_estimated(true);
+    buffer_queue.push_back(std::move(buffer));
+    stream->Append(buffer_queue);
+
+    // we could not clean buffer until key frame received.
+    double queue_buffer_size = stream->GetBufferedSize();
+    if (queue_buffer_size >= kMaxBufferSize) {
+      LOG_ID(INFO) << "Max buffer size(" << kMaxBufferSize << ") reached.";
+      request_key_frame = true;
+      if (key_frame) {
+        LOG_ID(INFO) << "Clean bufer";
+        stream->Remove(first_buffer_ts.value_or(base::Seconds(0)), ts, ts);
+        stream->Seek(ts);
+      }
+    }
+  }
+
+  if (!first_buffer_ts.has_value()) {
+    LOG_ID(INFO) << "Seek to the stream beginning timestamp:"
+                 << ts.InMilliseconds() << "(" << ts << ")"
+                 << " from where the esplusplayer shall read.";
+    stream->Seek(ts);
+    first_buffer_ts = ts;
+
+    // prepare and start player from now
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TizenEsPlusPlayerRenderer::StartPlayingOnWorkerThread,
+                       GetWeakPtr(), ts));
+  }
+
+  if (mode_ == Mode::kVideoHole) {
+    auto frame = media::VideoFrame::CreateHoleFrame(coded_size);
+    if (!frame) {
+      LOG_ID(ERROR) << "create hole frame failed!";
+      return request_key_frame;
+    }
+    frame->set_timestamp(ts);
+    frame->metadata().MergeMetadataFrom(meta);
+    frame->metadata().player_id = id_;
+    if (frame_cb_)
+      frame_cb_.Run(std::move(frame), *meta.reference_time);
+  } else if (mode_ == Mode::kVideoHoleWithMappingBuffer) {
+    base::AutoLock lk(pending_frames_lock_);
+    pending_frames_.push_back(PendingFrame{ts, meta});
+  }
+  lastVideoFrameMeta_ = meta;
+
+  return request_key_frame;
+}
+
+void TizenEsPlusPlayerRenderer::OnRequestSeek(base::TimeDelta time) {
+  mojo_renderer_->Seek(time, base::DoNothing());
+  ChunkDemuxerStream* stream = media_resource_->GetVideoStream();
+  if (!stream) {
+    LOG_ID(ERROR) << "No video stream.";
+    return;
+  }
+
+  // ESPP shall flush all frames cached after seek, so it should not feed any
+  // frame before seek.
+  stream->StartReturningData();
+}
+
+void TizenEsPlusPlayerRenderer::OnError(PipelineStatus status) {
+  LOG_ID(INFO) << " status:" << status;
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  player_state_ = Status::ERROR;
+}
+
+#if defined(TIZEN_TBM_SUPPORT)
+void TizenEsPlusPlayerRenderer::OnNewTbmFrameAvailable(
+    uint32_t player_id,
+    gfx::TbmBufferHandleInterProcess tbm_buffer_handle_inter_process,
+    base::TimeDelta timestamp) {
+  TRACE_EVENT1("media", "TizenEsPlusPlayerRenderer::OnNewTbmFrameAvailable",
+               "timestamp", timestamp.InMilliseconds());
+  DCHECK(worker_task_runner_->BelongsToCurrentThread());
+  auto [tbm_buffer_handle, tbm_surface_release_cb_runner] =
+      gfx::ToTbmBufferHandle(tbm_buffer_handle_inter_process);
+
+  auto frame = media::VideoFrame::WrapTBMInterProcessBuffer(
+      gfx::Size(tbm_buffer_handle.width, tbm_buffer_handle.height), timestamp,
+      tbm_buffer_handle);
+  if (!frame) {
+    LOG_ID(ERROR) << "Failed to wrap TBM IPC buffer.";
+    return;
+  }
+
+  frame->AddDestructionObserver(tbm_surface_release_cb_runner.Release());
+  frame->metadata().player_id = id_;
+
+  // Webrtc TBM from esplayer will be destroyed from
+  // video frame DestructionObserver
+  frame->SetTbmDestroyType(gfx::kDestroyTbmByVideoFrame);
+  frame->AddDestructionObserver(BindToCurrentLoop(base::BindOnce(
+      &TizenEsPlusPlayerRenderer::ReleaseMediaPacket, GetWeakPtr(),
+      reinterpret_cast<uintptr_t>(tbm_buffer_handle.media_packet))));
+  frame->metadata().MergeMetadataFrom(GetMetaData(timestamp));
+  if (frame_cb_) {
+    frame_cb_.Run(std::move(frame), timestamp + base::TimeTicks());
+  }
+  tbm_frame_in_use_++;
+}
+
+void TizenEsPlusPlayerRenderer::ReleaseMediaPacket(uintptr_t media_packet) {
+  if (!worker_task_runner_->BelongsToCurrentThread()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TizenEsPlusPlayerRenderer::ReleaseMediaPacket,
+                       GetWeakPtr(), media_packet));
+    return;
+  }
+
+  if (!renderer_extension_remote_.is_bound()) {
+    LOG_ID(INFO) << "Extension mojo is unbound.";
+    return;
+  }
+
+  renderer_extension_remote_->OnMediaPacketExhausted(media_packet);
+  tbm_frame_in_use_--;
+  if (tbm_frame_in_use_ == 0 && pending_disable_tbm_)
+    EnableTbmBufferCallback(false);
+}
+#endif
+
+void TizenEsPlusPlayerRenderer::EnableTbmBufferCallback(bool enable) {
+  if (!worker_task_runner_->BelongsToCurrentThread()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TizenEsPlusPlayerRenderer::EnableTbmBufferCallback,
+                       GetWeakPtr(), enable));
+    return;
+  }
+
+  if (!could_enable_user_data_) {
+    LOG_ID(INFO)
+        << " Current instance is initialized without user data support.";
+    return;
+  }
+
+  // ESPP will delete all TBM buffers immediately when disable TBM,
+  // so we need to make sure there is no TBM buffer in use currently,
+  // or disable TBM later.
+  if (!enable && tbm_frame_in_use_ > 0) {
+    LOG_ID(INFO) << " There are still " << tbm_frame_in_use_
+                 << " TBM buffer(s) in use, pending disable TBM.";
+    pending_disable_tbm_ = true;
+    return;
+  }
+  pending_disable_tbm_ = false;
+
+  // TODO(vd.wasm) This assumes that initial mode will be `kVideoHole`.
+  auto new_mode =
+      (enable ? Mode::kVideoHoleWithMappingBuffer : Mode::kVideoHole);
+  if (mode_ == new_mode) {
+    return;
+  }
+
+  mode_ = new_mode;
+  renderer_extension_remote_->EnableTbmBufferCallback(enable);
+  LOG_ID(INFO) << " new mode:" << static_cast<int>(mode_);
+}
+
+void TizenEsPlusPlayerRenderer::SetCallBackFrameSize(const gfx::Size& size) {
+  if (!worker_task_runner_->BelongsToCurrentThread()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TizenEsPlusPlayerRenderer::SetCallBackFrameSize,
+                       GetWeakPtr(), size));
+    return;
+  }
+
+  LOG_ID(INFO) << " new size:" << size.ToString();
+  renderer_extension_remote_->SetCallBackFrameSize(size);
+}
+
+void TizenEsPlusPlayerRenderer::UpdateLastCaptureResolution(
+    const VideoFrameMetadata& meta) {
+  if (!meta.source_size.has_value())
+    return;
+
+  gfx::Size source_size = *meta.source_size;
+  if (last_capture_size_ != source_size) {
+    LOG_ID(INFO) << ", capture source size changed from "
+                 << last_capture_size_.ToString() << " to "
+                 << source_size.ToString();
+    SetCallBackFrameSize(source_size);
+    last_capture_size_ = source_size;
+  }
+}
+
+#if defined(ENABLE_AUTO_ZOOM)
+void TizenEsPlusPlayerRenderer::SetAiZoomSettings(
+    const media::TizenAiZoomSettings& settings) {
+  if (!worker_task_runner_->BelongsToCurrentThread()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&TizenEsPlusPlayerRenderer::SetAiZoomSettings,
+                                  GetWeakPtr(), settings));
+    return;
+  }
+
+  LOG_ID(INFO) << " renderer extensions bound = "
+               << renderer_extension_remote_.is_bound()
+               << ", ai zoom settings = " << settings.AsHumanReadableString();
+
+  if (!renderer_extension_remote_.is_bound()) {
+    pending_ai_zoom_settings_ = settings;
+    return;
+  }
+
+  renderer_extension_remote_->SetAiZoomSettings(settings);
+}
+#endif  // defined(ENABLE_AUTO_ZOOM)
+
+void TizenEsPlusPlayerRenderer::SetPlayerVideoAbove(int32_t other_id) {
+  if (!worker_task_runner_->BelongsToCurrentThread()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TizenEsPlusPlayerRenderer::SetPlayerVideoAbove,
+                       base::Unretained(this), other_id));
+    return;
+  }
+
+  LOG_ID(INFO) << __func__ << " other_id:" << other_id;
+  mojo_renderer_->SetPlayerVideoAbove(other_id);
+}
+
+void TizenEsPlusPlayerRenderer::OnVideoSinkGeometryChange(
+    const gfx::RectF& rect,
+    media::VideoRotation rotation) {
+  LOG_ID(INFO) << " rect:" << rect.ToString() << " rotation:" << rotation;
+  worker_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TizenEsPlusPlayerRenderer::SetGeometryOnWorkerThread,
+                     GetWeakPtr(), rect, rotation));
+}
+
+base::WeakPtr<TizenEsPlusPlayerRenderer>
+TizenEsPlusPlayerRenderer::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+void TizenEsPlusPlayerRenderer::StreamResource::ResetStream() {
+  LOG(INFO) << __func__;
+  demuxer_streamer_ = std::make_unique<ChunkDemuxerStream>(
+      DemuxerStream::VIDEO, MediaTrack::Id("tizen renderer video stream"));
+}
+
+std::vector<DemuxerStream*>
+TizenEsPlusPlayerRenderer::StreamResource::GetAllStreams() {
+  LOG(INFO) << __func__;
+  std::vector<DemuxerStream*> v;
+  if (demuxer_streamer_)
+    v.push_back(demuxer_streamer_.get());
+  return v;
+}
+
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.h b/tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.h
new file mode 100644 (file)
index 0000000..8addc02
--- /dev/null
@@ -0,0 +1,304 @@
+// Copyright 2022 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_RENDERER_TIZEN_ESPLUSPLAYER_RENDERER
+#define MEDIA_RENDERER_TIZEN_ESPLUSPLAYER_RENDERER
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "content/renderer/media/media_interface_factory.h"
+#include "media/base/media_util.h"
+#include "media/base/renderer_client.h"
+#include "media/filters/chunk_demuxer.h"
+#include "media/mojo/clients/mojo_renderer.h"
+#include "media/mojo/mojom/interface_factory.mojom.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/platform/modules/mediastream/web_media_stream_sink.h"
+
+#if defined(TIZEN_TBM_SUPPORT)
+#include "tizen_src/chromium_impl/ui/gfx/tbm_buffer_handle_inter_process.h"
+#endif
+
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+#include "tizen_src/chromium_impl/media/base/tizen/scaler_usage_manager.h"
+#endif
+
+namespace media {
+
+enum class CanDropFrames { kNo, kYes };
+enum class HardwareResouceCategory { kScaler, kDeocder };
+enum class HardwareResouceType { kAny, kMain, kSub, kEnd };
+enum class Mode { kVideoHole, kTexture, kVideoHoleWithMappingBuffer };
+
+using FrameCb = base::RepeatingCallback<void(scoped_refptr<media::VideoFrame>,
+                                             base::TimeTicks)>;
+using InitCb = base::OnceCallback<void(bool)>;
+using SelectedResource = std::pair<HardwareResouceType /* scaler */,
+                                   HardwareResouceType /* decoder */>;
+
+class TizenEsPlusPlayerRenderer;
+
+class TizenEsPlusPlayerRendererManager {
+ public:
+  static TizenEsPlusPlayerRendererManager& GetInstance();
+  std::unique_ptr<TizenEsPlusPlayerRenderer> CreateTizenEsPlusPlayerRenderer(
+      media::VideoCodec codec,
+      gfx::Size init_size,
+      media::HardwareResouceType scaler_type,
+      media::HardwareResouceType decoder_type,
+      media::CanDropFrames can_drop_frames,
+      media::Mode mode,
+      media::InitCb init_cb,
+      media::FrameCb frame_cb,
+      bool has_id,
+      bool could_enable_user_data);
+
+  void RemoveTizenEsPlusPlayerRenderer(int id);
+  void OnVideoSinkGeometryChange(int id,
+                                 const gfx::RectF& rect,
+                                 media::VideoRotation rotation);
+  void OnZOrderChanged(int broadcast_id, const std::vector<int>& video_ids);
+
+ private:
+  friend class base::NoDestructor<TizenEsPlusPlayerRendererManager>;
+
+  TizenEsPlusPlayerRendererManager();
+  ~TizenEsPlusPlayerRendererManager();
+  // this function should be carefully used, as the got weakptr could
+  // be invalid anytime.
+  base::WeakPtr<TizenEsPlusPlayerRenderer> GetTizenEsPlusPlayerRenderer(int id);
+  bool HasResourceConflict(const media::SelectedResource& type);
+  bool HasResourceConflictScaler(const media::HardwareResouceType& type);
+  bool HasResourceConflictDecoder(const media::HardwareResouceType& type);
+  bool SelectHWResource(media::HardwareResouceType& scaler_type,
+                        media::HardwareResouceType& decoder_type,
+                        const media::Mode& mode);
+
+  int index_{0};
+  std::unordered_map<int, base::WeakPtr<TizenEsPlusPlayerRenderer>>
+      id_to_renderers_;
+  std::vector<int> video_ids_order_;
+  base::Lock lock_;
+};
+
+// This class is used for rendering camera video stream by upstream
+// mojo renderer interface on Tizen TV products.
+// all mojo related process should be handled on |worker_task_runner_|.
+class TizenEsPlusPlayerRenderer final
+    : public RendererClient,
+      public media::mojom::MediaPlayerRendererClientExtension {
+ public:
+  // max buffer queue size, clear the whole buffer queue once it is exceeded.
+  static constexpr double kMaxBufferSize = 8 * 1024 * 1024;
+
+  // We need a way to get the meta data in TBM callback to constructe video
+  // frame: store meta data in pending frame, and get it by timestamp in TBM
+  // callback.
+  struct PendingFrame {
+    base::TimeDelta timestamp;
+    absl::optional<media::VideoFrameMetadata> metadata{};
+  };
+  VideoFrameMetadata GetMetaData(base::TimeDelta timestamp);
+
+  ~TizenEsPlusPlayerRenderer() override;
+
+  int32_t GetVideoId() { return video_id_; }
+  void OnGetVideoId(int32_t player_id);
+  void OnRendererInitResult(PipelineStatus status);
+  bool IsReady();
+  // QueueBuffer should be called on IO thread
+  bool QueueBuffer(const VideoFrameMetadata& meta,
+                   const gfx::Size& coded_size,
+                   const uint8_t* data,
+                   std::size_t size,
+                   bool key_frame);
+
+  // RendererClient implementation
+  void OnError(PipelineStatus status) override;
+  void OnFallback(PipelineStatus status) override {}
+  void OnEnded() override {}
+  void OnStatisticsUpdate(const PipelineStatistics&) override {}
+  // void OnPlayerStarted(bool) override {}
+  void OnBufferingStateChange(BufferingState,
+                              BufferingStateChangeReason) override {}
+  void OnWaiting(WaitingReason) override {}
+  void OnAudioConfigChange(const AudioDecoderConfig&) override {}
+  void OnVideoConfigChange(const VideoDecoderConfig&) override {}
+  void OnVideoNaturalSizeChange(const gfx::Size&) override {}
+  void OnVideoOpacityChange(bool) override {}
+  bool IsVideoStreamAvailable() override { return IsReady(); }
+  void OnVideoFrameRateChange(absl::optional<int>) override {}
+
+  // media::mojom::MediaPlayerRendererClientExtension implementation
+  void OnDurationChange(base::TimeDelta duration) override {}
+  void OnVideoSizeChange(const gfx::Size& size) override {}
+#if defined(TIZEN_MULTIMEDIA)
+  void OnBufferUpdate(base::TimeDelta time) override {}
+  void OnRequestSeek(base::TimeDelta time) override;
+#endif
+  void OnNewFrameAvailable(uint32_t playerId,
+                           base::UnsafeSharedMemoryRegion frame,
+                           uint32_t size,
+                           base::TimeDelta timestamp,
+                           uint32_t width,
+                           uint32_t height) override {}
+#if defined(TIZEN_TBM_SUPPORT)
+  void OnNewTbmFrameAvailable(
+      uint32_t playerId,
+      gfx::TbmBufferHandleInterProcess tbm_buffer_handle,
+      base::TimeDelta timestamp) override;
+  void ReleaseMediaPacket(uintptr_t media_packet);
+#endif
+  void EnableTbmBufferCallback(bool enable);
+  void SetCallBackFrameSize(const gfx::Size& size);
+  void UpdateLastCaptureResolution(const VideoFrameMetadata& meta);
+  void NotifyTrackInfoToBrowser(int) override {}
+  using RendererClient::AddTrackInfo;
+  void AddTrackInfo(media::mojom::MediaTrackInfoPtr trackinfo) override {}
+  // using RendererClient::OnRegisterTimelineCbInfo;
+  // void OnRegisterTimelineCbInfo(
+  //    media::mojom::register_timeline_cb_info_sPtr info) override {}
+  // void OnSyncTimelineCbInfo(const std::string& timeline_selector,
+  //                           int sync) override {}
+  // void OnMrsUrlChange(const std::string& url) override {}
+  // void OnContentIdChange(const std::string& id) override {}
+#if defined(ENABLE_AUTO_ZOOM)
+  void SetAiZoomSettings(const media::TizenAiZoomSettings& settings);
+#endif  // defined(ENABLE_AUTO_ZOOM)
+  void SetPlayerVideoAbove(int32_t other_id);
+  void OnVideoSinkGeometryChange(const gfx::RectF& rect,
+                                 media::VideoRotation rotation);
+
+  const SelectedResource& selected_resource() const {
+    return selected_resource_;
+  }
+
+  base::WeakPtr<TizenEsPlusPlayerRenderer> GetWeakPtr();
+
+ private:
+  friend class TizenEsPlusPlayerRendererManager;
+  TizenEsPlusPlayerRenderer(media::VideoCodec codec,
+                            gfx::Size init_size,
+                            HardwareResouceType scaler_type,
+                            HardwareResouceType decoder_type,
+                            CanDropFrames can_drop_frames,
+                            Mode mode,
+                            InitCb init_cb,
+                            FrameCb frame_cb,
+                            int renderer_id,
+                            bool could_enable_user_data);
+
+  void Init();
+  bool UpdateVideoConfig();
+  VideoCodecProfile GetCodecProfile(VideoCodec codec);
+  // something must be done before esplusplayer prepared
+  void SetupBeforePlaying();
+
+  // HW resources
+  void OnHardwareResourcesComplete(bool success);
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+  void GetScalerUsageManager();
+#endif
+  void SelectHardwareResources();
+  void DidTakeResources();
+  void DidFreeResources();
+
+  void ReleaseRendererOnWorkThread(base::WaitableEvent* event);
+  void StartPlayingOnWorkerThread(base::TimeDelta time);
+  void SetGeometryOnWorkerThread(const gfx::RectF& rect,
+                                 media::VideoRotation rotation);
+  mojom::HardwareResourceConfig ToMojomHardwareResourceConfig(
+      SelectedResource types);
+
+  mojo::Remote<media::mojom::MediaPlayerRendererExtension>
+      renderer_extension_remote_;
+  mojo::Receiver<media::mojom::MediaPlayerRendererClientExtension>
+      client_extension_receiver_{this};
+  std::unique_ptr<content::MediaInterfaceFactory> media_interface_factory_;
+  std::unique_ptr<MojoRenderer> mojo_renderer_;
+
+  // all mojo IPC should be handled and posted to this dedicated runner.
+  scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_{
+      base::ThreadPool::CreateSingleThreadTaskRunner(
+          {base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})};
+
+  class StreamResource : public MediaResource {
+   public:
+    ~StreamResource() override = default;
+    std::vector<DemuxerStream*> GetAllStreams() override;
+    ChunkDemuxerStream* GetVideoStream() { return demuxer_streamer_.get(); }
+    void ResetStream();
+
+   private:
+    std::unique_ptr<ChunkDemuxerStream> demuxer_streamer_;
+  };
+
+  enum class Status {
+    NONE,
+    ERROR,
+    SUSPENDED,
+    INITIALISING,
+    INITIALISED,
+    PREPARED
+  };
+
+#if defined(TIZEN_HW_ENCODER) && !defined(TIZEN_CAPI_ENCODER_TV_API)
+  ScalerUsageManager* scaler_manager_;
+#endif
+  SelectedResource selected_resource_;
+  absl::optional<bool> uses_subresource_;
+
+  std::unique_ptr<StreamResource> media_resource_;
+  std::atomic<Status> player_state_;
+
+  VideoCodec codec_;
+  gfx::Size current_frame_size_;
+  std::unique_ptr<NullMediaLog> log_;
+
+  InitCb init_cb_;
+  FrameCb frame_cb_;
+  Mode mode_;
+
+  absl::optional<base::TimeDelta> first_buffer_ts;
+
+  int tbm_frame_in_use_;
+  bool pending_disable_tbm_;
+  int id_;
+  int video_id_;
+  // it indicates whether the current player could be running in TBM mode.
+  bool could_enable_user_data_;
+
+#if defined(ENABLE_AUTO_ZOOM)
+  std::optional<media::TizenAiZoomSettings> pending_ai_zoom_settings_;
+#endif  // defined(ENABLE_AUTO_ZOOM)
+
+  base::Lock pending_frames_lock_{};
+  base::Lock append_frame_lock_{};
+  std::deque<PendingFrame> pending_frames_{};
+
+  // In case get none from pending_frames_, use latest meta data
+  VideoFrameMetadata lastVideoFrameMeta_;
+
+  // Save the latest capture resolution, when it's changed, send
+  // SetCallBackFrameSize to UI process
+  gfx::Size last_capture_size_;
+
+  base::WeakPtrFactory<TizenEsPlusPlayerRenderer> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_RENDERER_TIZEN_ESPLUSPLAYER_RENDERER
index 729eff0..86ea118 100644 (file)
@@ -591,9 +591,10 @@ void VideoCaptureDeviceTizen::OnCameraCaptured(camera_preview_data_s* frame,
   if (self->first_ref_time_.is_null())
     self->first_ref_time_ = now;
 
-  self->client_->OnIncomingCapturedBuffer(std::move(self->buffer_),
-                                          videocaptureformat, now,
-                                          now - self->first_ref_time_);
+  self->client_->OnIncomingCapturedBufferExt(
+      std::move(self->buffer_), videocaptureformat, gfx::ColorSpace(), now,
+      now - self->first_ref_time_, gfx::Rect(videocaptureformat.frame_size),
+      VideoFrameMetadata(), frame->data.encoded_plane.size);
 
   // statistics & log every seconds
   ++(self->frame_total_);
index 1e42c3d..a4efe57 100644 (file)
@@ -142,6 +142,11 @@ esplusplayer_video_mime_type ConvertToESPlusVideoMimeType(
     case media::VideoCodec::kAV1:
       videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_AV1;
       break;
+#if defined(TIZEN_MULTIMEDIA_MJPEG_SUPPORT)
+    case media::VideoCodec::kMJPEG:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_MJPEG;
+      break;
+#endif
     default: {
       LOG(WARNING) << "Unknown codec :" << codec << ". Returning H264.";
       videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_H264;
index c781940..a56e135 100644 (file)
@@ -156,8 +156,8 @@ bool MediaPlayerESPlusPlayerTV::ReadFromBufferQueue(DemuxerStream::Type type) {
 }
 
 bool MediaPlayerESPlusPlayerTV::SetSubmitDataType(
-    const bool is_drm_eme,
-    const bool single_process_mode) {
+    const bool& is_drm_eme,
+    const bool& single_process_mode) {
   int error = ESPLUSPLAYER_ERROR_TYPE_NONE;
   is_drm_eme_ = is_drm_eme;
   if (is_drm_eme) {
@@ -176,8 +176,8 @@ bool MediaPlayerESPlusPlayerTV::SetSubmitDataType(
         esplayer_, ESPLUSPLAYER_SUBMIT_DATA_TYPE_CLEAN_DATA);
   }
   if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
-    LOG(ERROR) << "esplusplayer_set_submit_data_type failed. error code "
-               << error;
+    LOG_ID(ERROR, player_id_)
+        << "esplusplayer_set_submit_data_type failed. error code " << error;
     return false;
   }
   return true;
@@ -314,14 +314,154 @@ esplusplayer_submit_status MediaPlayerESPlusPlayerTV::SubmitEsPacket(
 
 
 void MediaPlayerESPlusPlayerTV::EnableLowLatencyMode() {
-  NOTIMPLEMENTED();
+  if (!esplayer_) {
+    LOG(ERROR) << "Invalid player handle.";
+    return;
+  }
+
+  if (GetPlayerState() != ESPLUSPLAYER_STATE_IDLE) {
+    LOG(ERROR) << "Invalid player state.";
+    return;
+  }
+
+  auto error = esplusplayer_set_low_latency_mode(
+      esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_set_low_latency_mode failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+  }
+  error = esplusplayer_set_low_latency_mode(
+      esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_set_low_latency_mode failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+  }
+  is_video_low_latency_ = true;
 }
 
 void MediaPlayerESPlusPlayerTV::SetHardwareResource(int config) {
-  NOTIMPLEMENTED();
+  LOG(INFO) << __func__ << " config:" << config;
+  if (!esplayer_) {
+    LOG(ERROR) << "Invalid player handle.";
+    return;
+  }
+
+  if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
+    LOG(ERROR) << "Invalid player state.";
+    return;
+  }
+
+  auto error = esplusplayer_set_alternative_video_resource(esplayer_, config);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_set_alternative_video_resource failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return;
+  }
+
+  return;
 }
 
 void MediaPlayerESPlusPlayerTV::EnableTbmBufferCallback(bool enable) {
+  LOG_ID(INFO, player_id_) << __func__ << " enable: " << enable;
+#if TIZEN_VERSION_AT_LEAST(6, 0, 0)
+  if (decoded_frame_buffer_type_ ==
+      ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE) {
+    LOG_ID(INFO, player_id_) << "tbm buffer callback had enabled, skip it";
+    return;
+  }
+
+  enable_tbm_buffer_callback_ = enable;
+  if (enable && GetPlayerState() == ESPLUSPLAYER_STATE_NONE) {
+    LOG_ID(INFO, player_id_)
+        << "Invalid player state. To enable Tbm buffer callback the "
+           "player must not be NONE.";
+    return;
+  }
+
+  // fixme: ESPP crash if set type to none, so skip it.
+  if (!enable)
+    return;
+
+  esplusplayer_decoded_video_frame_buffer_type type =
+      (enable ? ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE
+              : ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE);
+
+  int error = esplusplayer_set_video_frame_buffer_type(esplayer_, type);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG_ID(ERROR, player_id_)
+        << "esplusplayer_set_video_frame_buffer_type failed. error #"
+        << esplusplayer_get_error_string(
+               static_cast<esplusplayer_error_type>(error));
+    return;
+  }
+  decoded_frame_buffer_type_ = type;
+
+  // TODO(vd.wasm) Add method for setting buffer resolution.
+  if (decoded_frame_buffer_type_ ==
+      ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE) {
+    int width =
+        pending_cb_frame_width_ > 0 ? pending_cb_frame_width_ : cb_frame_width_;
+    width = width > 0 ? width : kDefaultCallBackFrameWidth;
+    int height = pending_cb_frame_height_ > 0 ? pending_cb_frame_height_
+                                              : cb_frame_height_;
+    height = height > 0 ? height : kDefaultCallBackFrameHeight;
+    SetCallBackFrameSize(gfx::Size(width, height));
+  }
+
+#else
+  LOG_ID(INFO, player_id_)
+      << "EnableTbmBufferCallback not support in this version";
+#endif
+}
+
+void MediaPlayerESPlusPlayerTV::SetCallBackFrameSize(const gfx::Size& size) {
+  LOG_ID(INFO, player_id_) << "Going to set callback frame size: "
+                           << size.width() << "x" << size.height();
+#if TIZEN_VERSION_AT_LEAST(6, 0, 0)
+  if (size.width() <= 0 || size.height() <= 0 ||
+      size.width() > kMaxCallBackFrameWidth ||
+      size.height() > kMaxCallBackFrameHeight) {
+    LOG(WARNING) << "invalid callback frame size:" << size.width() << "x"
+                 << size.height();
+    return;
+  }
+  if (cb_frame_width_ == size.width() && cb_frame_height_ == size.height())
+    return;
+  if (decoded_frame_buffer_type_ ==
+          ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE &&
+      GetPlayerState() != ESPLUSPLAYER_STATE_NONE) {
+    int player_error = esplusplayer_set_video_frame_buffer_scale_resolution(
+        esplayer_, size.width(), size.height());
+    if (player_error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+      LOG_ID(ERROR, player_id_)
+          << "esplusplayer_set_video_frame_buffer_scale_resolution "
+             "failed, error code "
+          << player_error;
+      pending_cb_frame_width_ = size.width();
+      pending_cb_frame_height_ = size.height();
+      return;
+    }
+    LOG_ID(INFO, player_id_)
+        << "callback frame size successfully set to:" << size.width() << "x"
+        << size.height();
+    cb_frame_width_ = size.width();
+    cb_frame_height_ = size.height();
+    pending_cb_frame_width_ = 0;
+    pending_cb_frame_height_ = 0;
+  } else {
+    LOG_ID(INFO, player_id_)
+        << "callback frame size setting will handled later: " << size.width()
+        << "x" << size.height();
+    pending_cb_frame_width_ = size.width();
+    pending_cb_frame_height_ = size.height();
+  }
+#endif
+}
+
+bool MediaPlayerESPlusPlayerTV::RequestVideoDecodedBuffer() {
   NOTIMPLEMENTED();
 }
 
@@ -334,6 +474,12 @@ void MediaPlayerESPlusPlayerTV::OnVideoFrameDropped(
   NOTIMPLEMENTED();
 }
 
+void MediaPlayerESPlusPlayerTV::OnEos() {
+  MediaPlayerESPlusPlayer::OnEos();
+  if (GetMediaPlayerClient())
+    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
+}
+
 void MediaPlayerESPlusPlayerTV::OnPrepareComplete(bool result) {
   if (!task_runner_->BelongsToCurrentThread()) {
     task_runner_->PostTask(
@@ -342,13 +488,13 @@ void MediaPlayerESPlusPlayerTV::OnPrepareComplete(bool result) {
     return;
   }
 
-  return MediaPlayerESPlusPlayer::OnPrepareComplete(result);
-}
+  if (result && is_video_low_latency_ && IsValid(DemuxerStream::VIDEO) &&
+      !ReadRequested(DemuxerStream::VIDEO)) {
+    SetShouldFeed(DemuxerStream::VIDEO, true);
+    ReadBuffer(DemuxerStream::VIDEO);
+  }
 
-void MediaPlayerESPlusPlayerTV::OnEos() {
-  MediaPlayerESPlusPlayer::OnEos();
-  if (GetMediaPlayerClient())
-    GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
+  return MediaPlayerESPlusPlayer::OnPrepareComplete(result);
 }
 
 void ReleaseTzHandle(int tz_handle, int size) {
index 4a9d6c6..0a23727 100644 (file)
@@ -24,6 +24,13 @@ void ReleaseTzHandle(int tz_handle, int size);
 
 class MEDIA_EXPORT MediaPlayerESPlusPlayerTV : public MediaPlayerESPlusPlayer {
  public:
+  enum {
+    kDefaultCallBackFrameHeight = 540,
+    kDefaultCallBackFrameWidth = 960,
+    kMaxCallBackFrameHeight = 720,
+    kMaxCallBackFrameWidth = 1280,
+  };
+
   MediaPlayerESPlusPlayerTV();
   ~MediaPlayerESPlusPlayerTV() override;
 
@@ -35,12 +42,13 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayerTV : public MediaPlayerESPlusPlayer {
   void EnableLowLatencyMode() override;
   void SetHardwareResource(int config) override;
   void EnableTbmBufferCallback(bool enable) override;
+  void SetCallBackFrameSize(const gfx::Size& size) override;
+  bool RequestVideoDecodedBuffer() override;
   void SetAppInfo() override;
 
   void OnVideoFrameDropped(const uint64_t dropped_count);
   void OnPrepareComplete(bool result) override;
   void OnEos() override;
-
 #if defined(TIZEN_VIDEO_HOLE)
   void ToggleFullscreenMode(bool is_fullscreen) override {}
 #endif
@@ -91,7 +99,8 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayerTV : public MediaPlayerESPlusPlayer {
 #endif
   bool SetVideoSubmitDataType(const media::VideoDecoderConfig& video_config);
   void SetAudioSubmitDataType();
-  bool SetSubmitDataType(const bool is_drm_eme, const bool single_process_mode);
+  bool SetSubmitDataType(const bool& is_drm_eme,
+                         const bool& single_process_mode);
   void SetAudioDecoderTypeIfNeeded(const media::AudioCodec& codec);
 
   bool is_drm_eme_{false};
@@ -109,6 +118,15 @@ class MEDIA_EXPORT MediaPlayerESPlusPlayerTV : public MediaPlayerESPlusPlayer {
   bool is_hdr_changed_{false};
   esplusplayer_matroska_color matroska_color_;
 
+  bool is_video_low_latency_{false};
+  esplusplayer_decoded_video_frame_buffer_type decoded_frame_buffer_type_{
+      ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE};
+  bool enable_tbm_buffer_callback_{false};
+  int cb_frame_width_{0};
+  int cb_frame_height_{0};
+  int pending_cb_frame_width_{0};
+  int pending_cb_frame_height_{0};
+
   base::WeakPtrFactory<MediaPlayerESPlusPlayerTV> weak_factory_{this};
 };
 }  // namespace media
index 3caf276..63194a9 100644 (file)
@@ -8,6 +8,7 @@
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/task/single_thread_task_runner.h"
 #include "media/base/demuxer_stream.h"
+#include "media/base/renderer.h"
 #include "media/filters/flags.h"
 #include "third_party/blink/public/platform/web_application_type.h"
 #include "third_party/blink/public/platform/web_media_player.h"
@@ -97,6 +98,8 @@ class MEDIA_EXPORT MediaPlayerTizen {
   virtual void SetMediaGeometry(const gfx::Rect& viewport_rect,
                                 const gfx::RectF& rect) = 0;
   virtual void PrepareVideoHole() {}
+  virtual void SetPlayerVideoAbove(int32_t other_id) {}
+  virtual void UseSubsurfaceController() {}
 #endif
   virtual void RequestSuspend(bool resource_conflicted = false) = 0;
 
@@ -107,6 +110,8 @@ class MEDIA_EXPORT MediaPlayerTizen {
   virtual void EnableLowLatencyMode() {}
   virtual void SetHardwareResource(int config) {}
   virtual void EnableTbmBufferCallback(bool enable) {}
+  virtual void SetCallBackFrameSize(const gfx::Size& size) {}
+  virtual bool RequestVideoDecodedBuffer() { return false; }
   virtual void SetAppInfo() {}
   virtual int GetPlayerId() { return -1; }
   virtual void SetContentMimeType(const std::string& mime_type) {}
index 7b41f67..5abc8b7 100644 (file)
@@ -19,6 +19,9 @@ if (use_ozone) {
   external_media_efl_deps += [ "//tizen_src/chromium_impl/ui/ozone:ozone_efl" ]
 }
 
+external_media_efl_blink_config = []
+external_media_efl_blink_sources = []
+
 if (tizen_multimedia) {
   external_media_video_decode_config += [
     "//tizen_src/build:esplusplayer",
@@ -127,6 +130,15 @@ if (tizen_multimedia) {
     ]
   }
 
+  if(tizen_product_tv) {
+    if(tizen_video_hole) {
+      external_media_efl_blink_sources += [
+        "//tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.cc",
+        "//tizen_src/chromium_impl/media/blink/renderer/tizen_esplusplayer_renderer.h",
+      ]
+    }
+  }
+
   external_media_capture_config += [
     "//tizen_src/build:capi-media-camera",
     "//tizen_src/build:libcapi-media-camera",