[MM] Use ESPP for media rendering 99/285499/6
authorVenugopal S M <sm.venugopal@samsung.com>
Mon, 21 Mar 2022 07:45:30 +0000 (13:15 +0530)
committerBot Blink <blinkbot@samsung.com>
Mon, 2 Jan 2023 14:29:16 +0000 (14:29 +0000)
- Use ESPP for media rendering.
- Handling of playback from browser process.
- Added support for software rendering.

- Also, changes are made to suit 108 upversion.
- Alignment is corrected.

Reference:
- https://review.tizen.org/gerrit/271043
- https://review.tizen.org/gerrit/272693
- https://review.tizen.org/gerrit/271947
- https://review.tizen.org/gerrit/273637
- https://review.tizen.org/gerrit/279293

Change-Id: Ic9b9aa396834431b75deee2b41921f261eff2581
Signed-off-by: Venugopal S M <sm.venugopal@samsung.com>
55 files changed:
content/browser/media/frameless_media_interface_proxy.cc
content/browser/media/frameless_media_interface_proxy.h
content/browser/media/media_interface_proxy.cc
content/browser/media/media_interface_proxy.h
content/renderer/media/media_factory.cc
content/renderer/media/media_interface_factory.cc
content/renderer/media/media_interface_factory.h
media/BUILD.gn
media/base/BUILD.gn
media/base/mime_util_internal.cc
media/base/pipeline_impl.cc
media/ffmpeg/ffmpeg_common.cc
media/filters/BUILD.gn
media/media_options.gni
media/mojo/clients/mojo_renderer_factory.cc
media/mojo/clients/mojo_renderer_factory.h
media/mojo/mojom/BUILD.gn
media/mojo/mojom/interface_factory.mojom
media/mojo/mojom/renderer_extensions.mojom
media/mojo/services/interface_factory_impl.cc
media/mojo/services/interface_factory_impl.h
packaging/chromium-efl.spec
third_party/ffmpeg/ffmpeg_generated.gni
third_party/ffmpeg/ffmpeg_options.gni
tizen_src/build/BUILD.gn
tizen_src/build/config/tizen_features.gni
tizen_src/build/gn_chromiumefl.sh
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/media/media_web_contents_observer_efl.h
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h [new file with mode: 0644]
tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h [new file with mode: 0644]
tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h [new file with mode: 0644]
tizen_src/chromium_impl/content/renderer/renderer_efl.gni
tizen_src/chromium_impl/media/BUILD.gn
tizen_src/chromium_impl/media/audio/tizen/audio_manager_capi.h
tizen_src/chromium_impl/media/audio/tizen/capi_audio_input.h
tizen_src/chromium_impl/media/audio/tizen/capi_audio_output.h
tizen_src/chromium_impl/media/base/efl/media_player_efl.h
tizen_src/chromium_impl/media/base/tizen/media_player_bridge_capi.h
tizen_src/chromium_impl/media/base/tizen/media_source_player_capi.h
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_factory_tizen.cc
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_factory_tizen.h
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.h [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/esplusplayer_util.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/esplusplayer_util.h [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h [new file with mode: 0644]
tizen_src/chromium_impl/media/media_efl.gni

index e6c05fe..757ce3d 100644 (file)
@@ -116,14 +116,16 @@ void FramelessMediaInterfaceProxy::CreateFlingingRenderer(
     mojo::PendingRemote<media::mojom::FlingingRendererClientExtension>
         client_extenion,
     mojo::PendingReceiver<media::mojom::Renderer> receiver) {}
+#endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 void FramelessMediaInterfaceProxy::CreateMediaPlayerRenderer(
     mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
         client_extension_remote,
     mojo::PendingReceiver<media::mojom::Renderer> receiver,
     mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
         renderer_extension_receiver) {}
-#endif  // BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 
 #if BUILDFLAG(IS_WIN)
 // Unimplemented method as this requires CDM and media::Renderer services with
index 2e77a79..75b4756 100644 (file)
@@ -64,13 +64,15 @@ class FramelessMediaInterfaceProxy final
       const base::UnguessableToken& overlay_plane_id,
       mojo::PendingReceiver<media::mojom::Renderer> receiver) final;
 #endif
-#if BUILDFLAG(IS_ANDROID)
+#if defined(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
   void CreateMediaPlayerRenderer(
       mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
           client_extension_remote,
       mojo::PendingReceiver<media::mojom::Renderer> receiver,
       mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
           renderer_extension_receiver) final;
+#endif  // defined(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
+#if BUILDFLAG(IS_ANDROID)
   void CreateFlingingRenderer(
       const std::string& presentation_id,
       mojo::PendingRemote<media::mojom::FlingingRendererClientExtension>
index 0299bd8..04c0711 100644 (file)
 #include "media/base/media_switches.h"
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
+#if defined(TIZEN_MULTIMEDIA)
+#include "media/mojo/services/mojo_renderer_service.h"  // nogncheck
+#include "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -355,7 +360,6 @@ void MediaInterfaceProxy::CreateMediaPlayerRenderer(
     mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
         renderer_extension_receiver) {
   DCHECK(thread_checker_.CalledOnValidThread());
-
   media::MojoRendererService::Create(
       nullptr,
       std::make_unique<MediaPlayerRenderer>(
@@ -368,6 +372,26 @@ void MediaInterfaceProxy::CreateMediaPlayerRenderer(
 }
 #endif
 
+#if defined(TIZEN_MULTIMEDIA)
+void MediaInterfaceProxy::CreateMediaPlayerRenderer(
+    mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
+        client_extension_remote,
+    mojo::PendingReceiver<media::mojom::Renderer> receiver,
+    mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
+        renderer_extension_receiver) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  media::MojoRendererService::Create(
+      nullptr,
+      std::make_unique<content::TizenRendererImpl>(
+          render_frame_host().GetProcess()->GetID(),
+          render_frame_host().GetRoutingID(),
+          WebContents::FromRenderFrameHost(&render_frame_host()),
+          std::move(renderer_extension_receiver),
+          std::move(client_extension_remote)),
+      std::move(receiver));
+}
+#endif
+
 #if BUILDFLAG(IS_WIN)
 void MediaInterfaceProxy::CreateMediaFoundationRenderer(
     mojo::PendingRemote<media::mojom::MediaLog> media_log_remote,
index c3b0195..19acc58 100644 (file)
@@ -74,13 +74,15 @@ class MediaInterfaceProxy final : public DocumentUserData<MediaInterfaceProxy>,
       mojo::PendingRemote<media::mojom::FlingingRendererClientExtension>
           client_extension,
       mojo::PendingReceiver<media::mojom::Renderer> receiver) final;
+#endif  // BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
   void CreateMediaPlayerRenderer(
       mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
           client_extension_remote,
       mojo::PendingReceiver<media::mojom::Renderer> receiver,
       mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
           renderer_extension_request) final;
-#endif  // BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(TIZEN_MULTIMEDIA)
 #if BUILDFLAG(IS_WIN)
   void CreateMediaFoundationRenderer(
       mojo::PendingRemote<media::mojom::MediaLog> media_log_remote,
index fc6f9b8..7848a79 100644 (file)
 #include "url/gurl.h"
 #endif
 
+#if defined(TIZEN_MULTIMEDIA)
+#include "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h"
+#endif
+
 #if BUILDFLAG(ENABLE_CAST_RENDERER)
 #include "content/renderer/media/cast_renderer_client_factory.h"
 #endif
@@ -578,6 +582,15 @@ MediaFactory::CreateRendererFactorySelector(
                                      std::move(factory));
   }
 
+#if defined(TIZEN_MULTIMEDIA)
+  auto media_player_factory =
+      std::make_unique<MediaPlayerRendererClientFactory>(
+          render_thread->compositor_task_runner(), CreateMojoRendererFactory());
+  factory_selector->AddBaseFactory(RendererType::kMediaPlayer,
+                                   std::move(media_player_factory));
+  is_base_renderer_factory_set = true;
+#endif
+
 #if BUILDFLAG(IS_ANDROID)
   DCHECK(interface_broker_);
 
index 938f049..632948d 100644 (file)
@@ -119,7 +119,7 @@ void MediaInterfaceFactory::CreateCastRenderer(
 }
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
+#if defined(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 void MediaInterfaceFactory::CreateMediaPlayerRenderer(
     mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
         client_extension_remote,
@@ -141,7 +141,9 @@ void MediaInterfaceFactory::CreateMediaPlayerRenderer(
       std::move(client_extension_remote), std::move(receiver),
       std::move(renderer_extension_receiver));
 }
+#endif  // defined(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 
+#if BUILDFLAG(IS_ANDROID)
 void MediaInterfaceFactory::CreateFlingingRenderer(
     const std::string& presentation_id,
     mojo::PendingRemote<media::mojom::FlingingRendererClientExtension>
index c017285..47adb9b 100644 (file)
@@ -67,13 +67,15 @@ class MediaInterfaceFactory final : public media::mojom::InterfaceFactory {
       mojo::PendingRemote<media::mojom::FlingingRendererClientExtension>
           client_extension,
       mojo::PendingReceiver<media::mojom::Renderer> receiver) final;
+#endif  // BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
   void CreateMediaPlayerRenderer(
       mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
           client_extension_remote,
       mojo::PendingReceiver<media::mojom::Renderer> receiver,
       mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
           renderer_extension_receiver) final;
-#endif  // BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 #if BUILDFLAG(IS_WIN)
   void CreateMediaFoundationRenderer(
       mojo::PendingRemote<media::mojom::MediaLog> media_log_remote,
index 77fa873..548201d 100644 (file)
@@ -65,7 +65,7 @@ buildflag_header("media_buildflags") {
   }
 }
 
-if (proprietary_codecs && media_use_ffmpeg) {
+if (proprietary_codecs && media_use_ffmpeg && !tizen_multimedia) {
   assert(
       ffmpeg_branding != "Chromium",
       "proprietary codecs and ffmpeg_branding set to Chromium are incompatible")
index 6660d88..42dfa5d 100644 (file)
@@ -454,7 +454,7 @@ source_set("base") {
     sources += [ "demuxer_memory_limit_android.cc" ]
   } else if (is_castos) {
     sources += [ "demuxer_memory_limit_cast.cc" ]
-  } else if (is_fuchsia) {
+  } else if (is_fuchsia || tizen_multimedia) {
     sources += [ "demuxer_memory_limit_low.cc" ]
   } else {
     sources += [ "demuxer_memory_limit_default.cc" ]
index 5629c05..879acf4 100644 (file)
@@ -417,7 +417,7 @@ void MimeUtil::AddSupportedMediaFormats() {
 #endif  // BUILDFLAG(IS_ANDROID)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
-#if defined(TIZEN_MULTIMEDIA) // Tizen supports HLS & m3u8.
+#if defined(TIZEN_MULTIMEDIA)  // Tizen supports HLS & m3u8.
   AddContainerWithCodecs("video/3gpp", mp4_codecs);
   AddContainerWithCodecs("application/vnd.apple.mpegurl", mp4_codecs);
   AddContainerWithCodecs("application/x-mpegURL", mp4_codecs);
index 4f79aa7..30159d7 100644 (file)
@@ -380,11 +380,13 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
   // Abort any reads the renderer may be blocked on.
   demuxer_->AbortPendingReads();
 
-
+  // Flush is not a pre-requisite for ESPP seek.
+#if !defined(TIZEN_MULTIMEDIA)
   // Flush.
   DCHECK(shared_state_.renderer);
   bound_fns.Push(base::BindOnce(
       &Renderer::Flush, base::Unretained(shared_state_.renderer.get())));
+#endif
 
   // Seek demuxer.
   bound_fns.Push(base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_),
index 899d752..57c52e3 100644 (file)
@@ -405,6 +405,11 @@ bool AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context,
                       codec_context->extradata + codec_context->extradata_size);
   }
 
+#if defined(EWK_BRINGUP) && defined(TIZEN_MULTIMEDIA)
+  // TODO: Address the hardcoding.
+  sample_format = kSampleFormatS16;
+#endif
+
   config->Initialize(codec, sample_format, channel_layout, codec_context->sample_rate,
                      extra_data, encryption_scheme, seek_preroll,
                      codec_context->delay);
index 129afcc..f556dec 100644 (file)
@@ -5,6 +5,10 @@
 import("//media/gpu/args.gni")
 import("//media/media_options.gni")
 
+if (tizen_multimedia) {
+  import("//tizen_src/chromium_impl/media/media_efl.gni")
+}
+
 source_set("filters") {
   # Do not expand the visibility here without double-checking with OWNERS, this
   # is a roll-up target which is part of the //media component. Most other DEPs
@@ -250,6 +254,11 @@ source_set("filters") {
       "h264_bitstream_buffer.h",
     ]
   }
+
+  if (tizen_multimedia) {
+    configs += external_media_video_decode_config
+    sources += external_media_video_decode_sources
+  }
 }
 
 source_set("perftests") {
index 26f6604..7ff34f1 100644 (file)
@@ -25,7 +25,7 @@ declare_args() {
   # Enable usage of FFmpeg within the media library. Used for most software
   # based decoding, demuxing, and sometimes optimized FFTs. If disabled,
   # implementors must provide their own demuxers and decoders.
-  media_use_ffmpeg = true && !is_tizen
+  media_use_ffmpeg = true && tizen_multimedia
 
   # Enable usage of libvpx within the media library. Used for software based
   # decoding of VP9 and VP8A type content.
index 443b787..487930d 100644 (file)
@@ -105,7 +105,8 @@ std::unique_ptr<MojoRenderer> MojoRendererFactory::CreateFlingingRenderer(
                                         video_renderer_sink,
                                         std::move(renderer_remote));
 }
-
+#endif
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 std::unique_ptr<MojoRenderer> MojoRendererFactory::CreateMediaPlayerRenderer(
     mojo::PendingReceiver<mojom::MediaPlayerRendererExtension>
         renderer_extension_receiver,
@@ -125,6 +126,6 @@ std::unique_ptr<MojoRenderer> MojoRendererFactory::CreateMediaPlayerRenderer(
                                         video_renderer_sink,
                                         std::move(renderer_remote));
 }
-#endif  // BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 
 }  // namespace media
index e36799e..37bcbf6 100644 (file)
@@ -72,7 +72,8 @@ class MojoRendererFactory final : public RendererFactory {
           client_extenion_ptr,
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
       VideoRendererSink* video_renderer_sink);
-
+#endif  // BUILDFLAG (IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
   std::unique_ptr<MojoRenderer> CreateMediaPlayerRenderer(
       mojo::PendingReceiver<mojom::MediaPlayerRendererExtension>
           renderer_extension_receiver,
@@ -80,7 +81,7 @@ class MojoRendererFactory final : public RendererFactory {
           client_extension_remote,
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
       VideoRendererSink* video_renderer_sink);
-#endif  // defined (OS_ANDROID)
+#endif  // BUILDFLAG (IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 
  private:
   // InterfaceFactory or InterfaceProvider used to create or connect to remote
index 988ee25..0783af8 100644 (file)
@@ -120,6 +120,10 @@ mojom("mojom") {
     enabled_features += [ "enable_cast_renderer" ]
   }
 
+  if (tizen_multimedia) {
+    enabled_features += [ "tizen_multimedia" ]
+  }
+
   shared_typemaps = [
     {
       types = [
index bc72da0..5aa4358 100644 (file)
@@ -97,4 +97,15 @@ interface InterfaceFactory {
             (pending_remote<ContentDecryptionModule>? cdm,
              CdmContext? cdm_context,
              string error_message);
+
+  [EnableIf=tizen_multimedia]
+  // Creates a MediaPlayerRenderer (MediaPlayerRendererClientFactory).
+  // - |renderer_extension| is bound in MediaPlayerRenderer, and receives calls
+  //   from MediaPlayerRendererClient.
+  // - |client_extension| is bound in MediaPlayerRendererClient, and receives
+  //   calls from the MediaPlayerRenderer.
+  CreateMediaPlayerRenderer(
+      pending_remote<MediaPlayerRendererClientExtension> client_extension,
+      pending_receiver<Renderer> renderer,
+      pending_receiver<MediaPlayerRendererExtension> renderer_extension);
 };
index c326d52..e750c7d 100644 (file)
@@ -6,6 +6,7 @@ module media.mojom;
 
 import "media/mojo/mojom/media_types.mojom";
 import "mojo/public/mojom/base/time.mojom";
+import "mojo/public/mojom/base/shared_memory.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
@@ -21,6 +22,14 @@ interface MediaPlayerRendererClientExtension {
   // metadata changes.
   OnVideoSizeChange(gfx.mojom.Size size);
   OnDurationChange(mojo_base.mojom.TimeDelta duration);
+
+  [EnableIf=tizen_multimedia]
+  OnNewFrameAvailable(uint32 playerId,
+                      mojo_base.mojom.UnsafeSharedMemoryRegion frame,
+                      uint32 size,
+                      mojo_base.mojom.TimeDelta timestamp,
+                      uint32 width,
+                      uint32 height);
 };
 
 // Extension of the mojo::Renderer communication layer for HLS and Android
index 4cff23f..8aaff39 100644 (file)
@@ -144,7 +144,7 @@ void InterfaceFactoryImpl::CreateCastRenderer(
 }
 #endif
 
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 void InterfaceFactoryImpl::CreateMediaPlayerRenderer(
     mojo::PendingRemote<mojom::MediaPlayerRendererClientExtension>
         client_extension_ptr,
@@ -153,7 +153,9 @@ void InterfaceFactoryImpl::CreateMediaPlayerRenderer(
         renderer_extension_receiver) {
   NOTREACHED();
 }
+#endif  // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
 
+#if defined(IS_ANDROID)
 void InterfaceFactoryImpl::CreateFlingingRenderer(
     const std::string& audio_device_id,
     mojo::PendingRemote<mojom::FlingingRendererClientExtension>
index dec82db..9aa22c8 100644 (file)
@@ -68,13 +68,15 @@ class InterfaceFactoryImpl final
       const base::UnguessableToken& overlay_plane_id,
       mojo::PendingReceiver<mojom::Renderer> receiver) final;
 #endif
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
   void CreateMediaPlayerRenderer(
       mojo::PendingRemote<mojom::MediaPlayerRendererClientExtension>
           client_extension_remote,
       mojo::PendingReceiver<mojom::Renderer> receiver,
       mojo::PendingReceiver<mojom::MediaPlayerRendererExtension>
           renderer_extension_receiver) final;
+#endif  // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
+#if defined(IS_ANDROID)
   void CreateFlingingRenderer(
       const std::string& presentation_id,
       mojo::PendingRemote<mojom::FlingingRendererClientExtension>
index acb8062..f44962f 100644 (file)
@@ -69,6 +69,7 @@ BuildRequires: pkgconfig(ecore-imf-evas)
 BuildRequires: pkgconfig(ecore-input)
 BuildRequires: pkgconfig(efl-extension)
 BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(esplusplayer)
 BuildRequires: pkgconfig(evas)
 BuildRequires: pkgconfig(feedback)
 BuildRequires: pkgconfig(fontconfig)
index 91f4681..d2acd7d 100644 (file)
@@ -12,7 +12,7 @@ ffmpeg_c_sources = []
 ffmpeg_gas_sources = []
 ffmpeg_asm_sources = []
 
-use_linux_config = is_linux || is_chromeos || is_fuchsia
+use_linux_config = is_linux || is_chromeos || is_fuchsia || tizen_multimedia
 
 if ((is_android && current_cpu == "arm" && arm_use_neon) || (is_android && current_cpu == "arm64") || (is_android && current_cpu == "x64") || (is_android && current_cpu == "x86") || (is_mac) || (is_win) || (use_linux_config)) {
   ffmpeg_c_sources += [
index 1db5f38..8aee1b8 100644 (file)
@@ -6,6 +6,7 @@ import("//build/config/arm.gni")
 import("//build/config/chrome_build.gni")
 import("//build/config/chromecast_build.gni")
 import("//build/config/sanitizers/sanitizers.gni")
+import("//tizen_src/build/config/tizen_features.gni")
 
 if (is_chrome_branded) {
   _default_ffmpeg_branding = "Chrome"
@@ -67,7 +68,7 @@ if (current_cpu == "x86") {
 os_config = current_os
 if ((is_linux || is_chromeos) && is_msan) {
   os_config = "linux-noasm"
-} else if (is_chromeos || is_fuchsia) {
+} else if (is_chromeos || is_fuchsia || tizen_multimedia) {
   os_config = "linux"
 } else if (is_win && !is_clang) {
   os_config = "win-msvc"
index 14ac742..3fbf271 100644 (file)
@@ -505,6 +505,16 @@ config("mm-player") {
   }
 }
 
+if (tizen_multimedia) {
+  config("esplusplayer") {
+    ldflags = [ "-lesplusplayer" ]
+  }
+
+  tizen_pkg_config("libesplusplayer") {
+    packages = [ "esplusplayer" ]
+  }
+}
+
 tizen_pkg_config("libmm-player") {
   packages = []
   if (is_tizen) {
index 64a4947..650fb25 100644 (file)
@@ -22,10 +22,7 @@ declare_args() {
   use_cairo = false
   use_pango = false
 
-  tizen_multimedia_eme_support = false
-  tizen_multimedia_support = false
   werror = false
-  tizen_multimedia = false
 
   tizen_clang_base_path = "//tizen_src/buildtools/llvm"
 
@@ -41,6 +38,11 @@ declare_args() {
   tizen_version_major = 0
   tizen_version_minor = 0
   tizen_version_patch = 0
+
+  # Tizen multimedia related
+  tizen_multimedia_eme_support = false
+  tizen_multimedia_support = false
+  tizen_multimedia = false
   tizen_tbm_support = false
 }
 
index c994ffd..d11a662 100755 (executable)
@@ -167,7 +167,6 @@ add_tizen_flags() {
                              enable_nacl=false
                              enable_basic_printing=true
                              enable_print_preview=true
-                             tizen_multimedia_eme_support=false
                              use_xkbcommon=true
                              target_os="\"tizen\""
                              target_sysroot=\"/\"
@@ -213,8 +212,8 @@ add_tizen_flags() {
 
   # [M49_2623] Temporary disabling the flag.
   #            FIXME: http://165.213.149.170/jira/browse/TWF-610
-  ADDITIONAL_GN_PARAMETERS+="tizen_multimedia_support=false
-                             tizen_multimedia=true
+  ADDITIONAL_GN_PARAMETERS+="tizen_multimedia=true
+                             proprietary_codecs=true
                              tizen_tbm_support=false
                             "
 }
index 1ae5fbf..668d5d0 100644 (file)
@@ -123,6 +123,21 @@ if (tizen_multimedia_support) {
     "//tizen_src/chromium_impl/content/browser/media/media_web_contents_observer_efl.h",
   ]
 }
+
+if (tizen_multimedia) {
+  external_content_browser_efl_configs += [
+    "//tizen_src/build:esplusplayer",
+    "//tizen_src/build:libesplusplayer",
+  ]
+
+  external_content_browser_efl_sources += [
+    "//tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.cc",
+    "//tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h",
+    "//tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc",
+    "//tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h",
+  ]
+}
+
 if (tizen_tbm_support) {
   external_content_browser_efl_sources += [
     "//tizen_src/chromium_impl/content/browser/media/browser_mediapacket_manager.cc",
diff --git a/tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.cc b/tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.cc
new file mode 100644 (file)
index 0000000..2fdfa87
--- /dev/null
@@ -0,0 +1,46 @@
+// 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 "tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h"
+
+#include "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h"
+
+namespace content {
+
+MediaPlayerRendererWebContentsObserver::MediaPlayerRendererWebContentsObserver(
+    WebContents* contents)
+    : WebContentsUserData<MediaPlayerRendererWebContentsObserver>(*contents) {}
+
+MediaPlayerRendererWebContentsObserver::
+    ~MediaPlayerRendererWebContentsObserver() = default;
+
+void MediaPlayerRendererWebContentsObserver::AddMediaPlayerRenderer(
+    content::TizenRendererImpl* player) {
+  DCHECK(player);
+  DCHECK(players_.find(player) == players_.end());
+  players_.insert(player);
+}
+
+void MediaPlayerRendererWebContentsObserver::RemoveMediaPlayerRenderer(
+    content::TizenRendererImpl* player) {
+  DCHECK(player);
+  auto erase_result = players_.erase(player);
+  DCHECK_EQ(1u, erase_result);
+}
+
+void MediaPlayerRendererWebContentsObserver::DidUpdateAudioMutingState(
+    bool muted) {
+  for (content::TizenRendererImpl* player : players_)
+    player->OnUpdateAudioMutingState(muted);
+}
+
+void MediaPlayerRendererWebContentsObserver::WebContentsDestroyed() {
+  for (content::TizenRendererImpl* player : players_)
+    player->OnWebContentsDestroyed();
+  players_.clear();
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(MediaPlayerRendererWebContentsObserver);
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h b/tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h
new file mode 100644 (file)
index 0000000..3b04c9e
--- /dev/null
@@ -0,0 +1,50 @@
+// 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 CONTENT_BROWSER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_WEB_CONTENTS_OBSERVER_H_
+#define CONTENT_BROWSER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_WEB_CONTENTS_OBSERVER_H_
+
+#include "base/containers/flat_set.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+
+class TizenRendererImpl;
+
+// This class propagates WebContents muting updates to MediaPlayerRenderers.
+// This allows us to avoid adding N WebContentsObservers for N
+// MediaPlayerRenderers on a page. Essentially, this is a call-stack filter to
+// prevent uninteresting observer methods from calling into the
+// MediaPlayerRenderers.
+class MediaPlayerRendererWebContentsObserver
+    : public WebContentsObserver,
+      public WebContentsUserData<MediaPlayerRendererWebContentsObserver> {
+ public:
+  MediaPlayerRendererWebContentsObserver(
+      const MediaPlayerRendererWebContentsObserver&) = delete;
+  MediaPlayerRendererWebContentsObserver& operator=(
+      const MediaPlayerRendererWebContentsObserver&) = delete;
+
+  ~MediaPlayerRendererWebContentsObserver() override;
+
+  void AddMediaPlayerRenderer(content::TizenRendererImpl* player);
+  void RemoveMediaPlayerRenderer(content::TizenRendererImpl* player);
+
+  // WebContentsObserver implementation.
+  void DidUpdateAudioMutingState(bool muted) override;
+  void WebContentsDestroyed() override;
+
+ private:
+  explicit MediaPlayerRendererWebContentsObserver(WebContents* web_contents);
+  friend class WebContentsUserData<MediaPlayerRendererWebContentsObserver>;
+
+  base::flat_set<content::TizenRendererImpl*> players_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_WEB_CONTENTS_OBSERVER_H_
index 716d66b..9e31a76 100644 (file)
@@ -18,10 +18,11 @@ class BrowserMediaPlayerManagerEfl;
 // browser side. It receives IPC messages from media RenderFrameObservers and
 // forwards them to the corresponding managers. The managers are responsible
 // for sending IPCs back to the RenderFrameObservers at the render side.
-class CONTENT_EXPORT MediaWebContentsObserver
-    : public WebContentsObserver {
+class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
  public:
   explicit MediaWebContentsObserver(WebContents* web_contents);
+  MediaWebContentsObserver(const MediaWebContentsObserver&) = delete;
+  MediaWebContentsObserver& operator=(const MediaWebContentsObserver&) = delete;
   virtual ~MediaWebContentsObserver();
 
   MediaWebContentsObserver(const MediaWebContentsObserver&) = delete;
diff --git a/tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc b/tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc
new file mode 100644 (file)
index 0000000..5ff233d
--- /dev/null
@@ -0,0 +1,385 @@
+// 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 "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/trace_event/trace_event.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/media_resource.h"
+#include "media/base/media_switches.h"
+#include "media/base/renderer_client.h"
+#include "media/base/video_decoder_config.h"
+#include "tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h"
+#include "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h"
+
+namespace content {
+namespace {
+const float kDefaultVolume = 1.0;
+}
+
+class TizenRendererImpl::RendererClientInternal final
+    : public media::RendererClient {
+ public:
+  RendererClientInternal(media::DemuxerStream::Type type,
+                         TizenRendererImpl* renderer)
+      : type_(type), renderer_(renderer) {
+    DCHECK((type_ == media::DemuxerStream::AUDIO) ||
+           (type_ == media::DemuxerStream::VIDEO));
+  }
+
+  void OnError(media::PipelineStatus error) override {
+    renderer_->OnError(error);
+  }
+
+  // Callback executed when there is a fallback somewhere in the pipeline which
+  // should be recorded for metrics analysis.
+  void OnFallback(media::PipelineStatus fallback) override {}
+
+  void OnEnded() override { renderer_->OnRendererEnded(); }
+
+  void OnStatisticsUpdate(const media::PipelineStatistics& stats) override {
+    renderer_->OnStatisticsUpdate(stats);
+  }
+
+  void OnBufferingStateChange(
+      media::BufferingState state,
+      media::BufferingStateChangeReason reason) override {
+    renderer_->OnBufferingStateChange(state, reason);
+  }
+
+  void OnWaiting(media::WaitingReason reason) override {
+    renderer_->OnWaiting(reason);
+  }
+
+  void OnAudioConfigChange(const media::AudioDecoderConfig& config) override {
+    renderer_->OnAudioConfigChange(config);
+  }
+
+  void OnVideoConfigChange(const media::VideoDecoderConfig& config) override {
+    renderer_->OnVideoConfigChange(config);
+  }
+
+  void OnVideoNaturalSizeChange(const gfx::Size& size) override {
+    DCHECK(type_ == media::DemuxerStream::VIDEO);
+    renderer_->OnVideoNaturalSizeChange(size);
+  }
+
+  void OnVideoOpacityChange(bool opaque) override {
+    DCHECK(type_ == media::DemuxerStream::VIDEO);
+    renderer_->OnVideoOpacityChange(opaque);
+  }
+
+  void OnVideoFrameRateChange(absl::optional<int> fps) override {
+    DCHECK(type_ == media::DemuxerStream::VIDEO);
+    renderer_->OnVideoFrameRateChange(fps);
+  }
+
+ private:
+  media::DemuxerStream::Type type_;
+  TizenRendererImpl* renderer_;
+};
+
+TizenRendererImpl::TizenRendererImpl(
+    int process_id,
+    int routing_id,
+    WebContents* web_contents,
+    mojo::PendingReceiver<RendererExtension> renderer_extension_receiver,
+    mojo::PendingRemote<ClientExtension> client_extension_remote)
+    : client_extension_(std::move(client_extension_remote)),
+      render_process_id_(process_id),
+      routing_id_(routing_id),
+      volume_(kDefaultVolume),
+      renderer_extension_receiver_(this,
+                                   std::move(renderer_extension_receiver)) {
+  DCHECK_EQ(WebContents::FromRenderFrameHost(
+                RenderFrameHost::FromID(process_id, routing_id)),
+            web_contents);
+
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(web_contents);
+  web_contents_muted_ = web_contents_impl && web_contents_impl->IsAudioMuted();
+  task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+  if (web_contents) {
+    MediaPlayerRendererWebContentsObserver::CreateForWebContents(web_contents);
+    web_contents_observer_ =
+        MediaPlayerRendererWebContentsObserver::FromWebContents(web_contents);
+    if (web_contents_observer_)
+      web_contents_observer_->AddMediaPlayerRenderer(this);
+  }
+}
+
+TizenRendererImpl::TizenRendererImpl(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+    media::VideoRendererSink* sink)
+    : state_(STATE_UNINITIALIZED),
+      task_runner_(task_runner),
+      sink_(sink),
+      volume_(1.0),
+      renderer_extension_receiver_(this),
+      playback_rate_(0.0) {
+  // TODO(dalecurtis): Remove once experiments for http://crbug.com/470940 are
+  // complete.
+  int threshold_ms = 0;
+  std::string threshold_ms_str(
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kVideoUnderflowThresholdMs));
+  if (base::StringToInt(threshold_ms_str, &threshold_ms) && threshold_ms > 0) {
+    NOTIMPLEMENTED();
+  }
+}
+
+TizenRendererImpl::~TizenRendererImpl() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  weak_factory_.InvalidateWeakPtrs();
+
+  // TODO
+  if (init_cb_)
+    ;
+  else if (flush_cb_)
+    ;
+  if (web_contents_observer_)
+    web_contents_observer_->RemoveMediaPlayerRenderer(this);
+}
+
+void TizenRendererImpl::Initialize(media::MediaResource* media_resource,
+                                   media::RendererClient* client,
+                                   media::PipelineStatusCallback init_cb) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK_EQ(state_, STATE_UNINITIALIZED);
+  DCHECK(init_cb);
+  DCHECK(client);
+  TRACE_EVENT_ASYNC_BEGIN0("media", "TizenRendererImpl::Initialize", this);
+
+  client_ = client;
+  init_cb_ = std::move(init_cb);
+  media_resource_ = media_resource;
+
+  if (!media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Initialize(sink_)) {
+    std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
+  std::move(init_cb_).Run(media::PIPELINE_OK);
+
+  // TODO: return unsupported error for CDM.
+  state_ = STATE_INITIALIZING;
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetTaskRunner(
+      task_runner_.get());
+  SetStreamInfo();
+}
+
+void TizenRendererImpl::SetStreamInfo() {
+  audio_stream_ = media_resource_->GetFirstStream(media::DemuxerStream::AUDIO);
+  if (audio_stream_) {
+    audio_renderer_client_ = std::make_unique<RendererClientInternal>(
+        media::DemuxerStream::AUDIO, this);
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetStreamInfo(
+        media::DemuxerStream::AUDIO, audio_stream_,
+        audio_renderer_client_.get());
+  }
+
+  video_stream_ = media_resource_->GetFirstStream(media::DemuxerStream::VIDEO);
+  if (video_stream_) {
+    video_renderer_client_ = std::make_unique<RendererClientInternal>(
+        media::DemuxerStream::VIDEO, this);
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetStreamInfo(
+        media::DemuxerStream::VIDEO, video_stream_,
+        video_renderer_client_.get());
+#if defined(TIZEN_TBM_SUPPORT)
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetFrameAvailableCallback(
+        base::BindRepeating(&TizenRendererImpl::OnNewTbmFrameAvailable,
+                            base::Unretained(this)));
+#else
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetFrameAvailableCallback(
+        base::BindRepeating(&TizenRendererImpl::OnNewFrameAvailable,
+                            base::Unretained(this)));
+#endif
+  }
+}
+
+void TizenRendererImpl::SetCdm(media::CdmContext* cdm_context,
+                               CdmAttachedCB cdm_attached_cb) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(cdm_context);
+  TRACE_EVENT0("media", "TizenRendererImpl::SetCdm");
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::SetLatencyHint(
+    absl::optional<base::TimeDelta> latency_hint) {
+  DCHECK(!latency_hint || (*latency_hint >= base::TimeDelta()));
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::SetPreservesPitch(bool preserves_pitch) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnNewFrameAvailable(
+    uint32_t playerId,
+    base::UnsafeSharedMemoryRegion frame,
+    uint32_t size,
+    base::TimeDelta timestamp,
+    uint32_t width,
+    uint32_t height) {
+  client_extension_->OnNewFrameAvailable(playerId, std::move(frame), size,
+                                         timestamp, width, height);
+}
+
+void TizenRendererImpl::Flush(base::OnceClosure flush_cb) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!flush_cb_);
+  TRACE_EVENT_ASYNC_BEGIN0("media", "TizenRendererImpl::Flush", this);
+
+  if (state_ == STATE_FLUSHED) {
+    flush_cb_ = media::BindToCurrentLoop(std::move(flush_cb));
+    // FinishFlush();
+    return;
+  }
+
+  if (state_ != STATE_PLAYING) {
+    DCHECK_EQ(state_, STATE_ERROR);
+    return;
+  }
+
+  flush_cb_ = std::move(flush_cb);
+  state_ = STATE_FLUSHING;
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Flush(std::move(flush_cb));
+}
+
+void TizenRendererImpl::Seek(base::TimeDelta time) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " time : " << time.InMicroseconds();
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Seek(time);
+}
+
+void TizenRendererImpl::StartPlayingFrom(base::TimeDelta time) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " time : " << time.InMicroseconds();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT1("media", "TizenRendererImpl::StartPlayingFrom", "time_us",
+               time.InMicroseconds());
+
+  state_ = STATE_PLAYING;
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Prepare();
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Seek(time);
+}
+
+void TizenRendererImpl::SetPlaybackRate(double playback_rate) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << "("
+            << playback_rate << ")";
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT1("media", "TizenRendererImpl::SetPlaybackRate", "rate",
+               playback_rate);
+
+  if (playback_rate_ == playback_rate)
+    return;
+
+  playback_rate_ = playback_rate;
+
+  // TODO: Random error is observed on TM1.
+  // MediaPlayerESPlusPlayer::GetEsppPlayer()->SetRate(playback_rate_);
+
+  if (playback_rate > 0)
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Play();
+  else
+    media::MediaPlayerESPlusPlayer::GetEsppPlayer()->Pause();
+}
+
+void TizenRendererImpl::SetVolume(float volume) {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  if (volume_ == volume)
+    return;
+
+  volume_ = volume;
+  media::MediaPlayerESPlusPlayer::GetEsppPlayer()->SetVolume(volume);
+}
+
+base::TimeDelta TizenRendererImpl::GetMediaTime() {
+  return media::MediaPlayerESPlusPlayer::GetEsppPlayer()->GetCurrentTime();
+}
+
+void TizenRendererImpl::OnSelectedVideoTracksChanged(
+    const std::vector<media::DemuxerStream*>& enabled_tracks,
+    base::OnceClosure change_completed_cb) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnEnabledAudioTracksChanged(
+    const std::vector<media::DemuxerStream*>& enabled_tracks,
+    base::OnceClosure change_completed_cb) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnRendererEnded() {
+  client_->OnEnded();
+}
+
+void TizenRendererImpl::OnError(media::PipelineStatus error) {
+  state_ = STATE_ERROR;
+  client_->OnError(error);
+}
+
+void TizenRendererImpl::OnStatisticsUpdate(
+    const media::PipelineStatistics& stats) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnBufferingStateChange(
+    media::BufferingState new_buffering_state,
+    media::BufferingStateChangeReason reason) {
+  client_->OnBufferingStateChange(new_buffering_state, reason);
+}
+
+void TizenRendererImpl::OnWaiting(media::WaitingReason reason) {
+  client_->OnWaiting(reason);
+}
+
+void TizenRendererImpl::OnAudioConfigChange(
+    const media::AudioDecoderConfig& config) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  client_->OnAudioConfigChange(config);
+}
+
+void TizenRendererImpl::OnVideoConfigChange(
+    const media::VideoDecoderConfig& config) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  client_->OnVideoConfigChange(config);
+}
+
+void TizenRendererImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnVideoOpacityChange(bool opaque) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::OnVideoFrameRateChange(absl::optional<int> fps) {
+  NOTIMPLEMENTED();
+}
+
+void TizenRendererImpl::InitiateScopedSurfaceRequest(
+    InitiateScopedSurfaceRequestCallback callback) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h b/tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h
new file mode 100644 (file)
index 0000000..4c4078f
--- /dev/null
@@ -0,0 +1,172 @@
+// 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_RENDERERS_TIZEN_RENDERER_IMPL_H_
+#define MEDIA_RENDERERS_TIZEN_RENDERER_IMPL_H_
+
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "content/common/content_export.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_export.h"
+#include "media/base/media_resource.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/renderer.h"
+#include "media/base/renderer_client.h"
+#include "media/base/video_renderer_sink.h"
+#include "media/base/waiting.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 "ui/gfx/geometry/size.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class MediaPlayerTizen;
+class MediaResource;
+}  // namespace media
+
+namespace content {
+
+class WebContents;
+class MediaPlayerRendererWebContentsObserver;
+
+class CONTENT_EXPORT TizenRendererImpl
+    : public media::Renderer,
+      public media::mojom::MediaPlayerRendererExtension {
+ public:
+  using RendererExtension = media::mojom::MediaPlayerRendererExtension;
+  using ClientExtension = media::mojom::MediaPlayerRendererClientExtension;
+
+  TizenRendererImpl(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+      media::VideoRendererSink* sink);
+  TizenRendererImpl(
+      int process_id,
+      int routing_id,
+      WebContents* web_contents,
+      mojo::PendingReceiver<RendererExtension> renderer_extension_receiver,
+      mojo::PendingRemote<ClientExtension> client_extension_remote);
+  TizenRendererImpl(const TizenRendererImpl&) = delete;
+  TizenRendererImpl& operator=(const TizenRendererImpl&) = delete;
+  ~TizenRendererImpl();
+
+  // Renderer implementation.
+  void Initialize(media::MediaResource* media_resource,
+                  media::RendererClient* client,
+                  media::PipelineStatusCallback init_cb) override;
+  void SetCdm(media::CdmContext* cdm_context,
+              CdmAttachedCB cdm_attached_cb) override;
+  void SetLatencyHint(absl::optional<base::TimeDelta> latency_hint) override;
+  void SetPreservesPitch(bool preserves_pitch) override;
+  void Flush(base::OnceClosure flush_cb) override;
+  void StartPlayingFrom(base::TimeDelta time) override;
+  void SetPlaybackRate(double playback_rate) override;
+  void SetVolume(float volume) override;
+  base::TimeDelta GetMediaTime() override;
+  void OnSelectedVideoTracksChanged(
+      const std::vector<media::DemuxerStream*>& enabled_tracks,
+      base::OnceClosure change_completed_cb) override;
+  void OnEnabledAudioTracksChanged(
+      const std::vector<media::DemuxerStream*>& enabled_tracks,
+      base::OnceClosure change_completed_cb) override;
+  void InitiateScopedSurfaceRequest(
+      InitiateScopedSurfaceRequestCallback callback) override;
+  void OnUpdateAudioMutingState(bool muted) {}
+  void OnWebContentsDestroyed() {}
+
+  // Should be removed.
+  void Seek(base::TimeDelta time);
+
+  void OnNewFrameAvailable(uint32_t playerId,
+                           base::UnsafeSharedMemoryRegion frame,
+                           uint32_t size,
+                           base::TimeDelta timestamp,
+                           uint32_t width,
+                           uint32_t height);
+
+ private:
+  class RendererClientInternal;
+
+  enum State {
+    STATE_UNINITIALIZED,
+    STATE_INIT_PENDING_CDM,  // Initialization is waiting for the CDM to be set.
+    STATE_INITIALIZING,      // Initializing audio/video renderers.
+    STATE_FLUSHING,          // Flushing is in progress.
+    STATE_FLUSHED,           // After initialization or after flush completed.
+    STATE_PLAYING,           // After StartPlayingFrom has been called.
+    STATE_ERROR
+  };
+
+  void SetStreamInfo();
+  void OnRendererEnded();
+  void OnError(media::PipelineStatus error);
+  void OnStatisticsUpdate(const media::PipelineStatistics& stats);
+  void OnBufferingStateChange(media::BufferingState new_buffering_state,
+                              media::BufferingStateChangeReason reason);
+  void OnWaiting(media::WaitingReason reason);
+  void OnAudioConfigChange(const media::AudioDecoderConfig& config);
+  void OnVideoConfigChange(const media::VideoDecoderConfig& config);
+  void OnVideoNaturalSizeChange(const gfx::Size& size);
+  void OnVideoOpacityChange(bool opaque);
+  void OnVideoFrameRateChange(absl::optional<int> fps);
+
+  media::RendererClient* client_;
+
+  State state_;
+
+  // Task runner used to execute pipeline tasks.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  media::VideoRendererSink* const sink_ = nullptr;
+  mojo::Remote<ClientExtension> client_extension_;
+  // Identifiers to find the RenderFrameHost that created |this|.
+  // NOTE: We store these IDs rather than a RenderFrameHost* because we do not
+  // know when the RenderFrameHost is destroyed.
+  int render_process_id_;
+  int routing_id_;
+
+  // Temporary callback used for Initialize() and Flush().
+  media::PipelineStatusCallback init_cb_;
+  base::OnceClosure flush_cb_;
+
+  std::unique_ptr<RendererClientInternal> audio_renderer_client_;
+  std::unique_ptr<RendererClientInternal> video_renderer_client_;
+
+  media::MediaResource* media_resource_;
+  media::DemuxerStream* audio_stream_;
+  media::DemuxerStream* video_stream_;
+
+  double volume_ = 0.0;
+  double playback_rate_ = 0;
+
+  // The time to start playback from after starting/seeking has completed.
+  base::TimeDelta start_time_;
+
+  media::BufferingState audio_buffering_state_;
+  media::BufferingState video_buffering_state_;
+
+  // Whether we've received the audio/video ended events.
+  bool media_ended_;
+  bool web_contents_muted_;
+  mojo::Receiver<MediaPlayerRendererExtension> renderer_extension_receiver_;
+  raw_ptr<content::MediaPlayerRendererWebContentsObserver>
+      web_contents_observer_;
+
+  // Indicates if a serious error has been encountered by the |media_player_|.
+  bool has_error_ = false;
+
+  base::WeakPtrFactory<TizenRendererImpl> weak_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // MEDIA_RENDERERS_TIZEN_RENDERER_IMPL_H_
diff --git a/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.cc b/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.cc
new file mode 100644 (file)
index 0000000..26b405b
--- /dev/null
@@ -0,0 +1,130 @@
+// 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 "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
+
+namespace content {
+
+MediaPlayerRendererClient::MediaPlayerRendererClient(
+    mojo::PendingRemote<RendererExtention> renderer_extension_remote,
+    mojo::PendingReceiver<ClientExtention> client_extension_receiver,
+    scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    std::unique_ptr<media::MojoRenderer> mojo_renderer,
+    media::VideoRendererSink* sink)
+    : MojoRendererWrapper(std::move(mojo_renderer)),
+      client_(nullptr),
+      sink_(sink),
+      media_task_runner_(std::move(media_task_runner)),
+      compositor_task_runner_(std::move(compositor_task_runner)),
+      delayed_bind_client_extension_receiver_(
+          std::move(client_extension_receiver)),
+      delayed_bind_renderer_extention_remote_(
+          std::move(renderer_extension_remote)) {}
+
+MediaPlayerRendererClient::~MediaPlayerRendererClient() {
+  // Clearing the STW's callback into |this| must happen first. Otherwise, the
+  // underlying StreamTextureProxy can callback into OnFrameAvailable() on the
+  // |compositor_task_runner_|, while we are destroying |this|.
+  // See https://crbug.com/688466.
+}
+
+void MediaPlayerRendererClient::Initialize(
+    media::MediaResource* media_resource,
+    media::RendererClient* client,
+    media::PipelineStatusCallback init_cb) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(!init_cb_);
+
+  // Consume and bind the delayed PendingRemote and PendingReceiver now that we
+  // are on |media_task_runner_|.
+  renderer_extension_remote_.Bind(
+      std::move(delayed_bind_renderer_extention_remote_), media_task_runner_);
+  client_extension_receiver_.Bind(
+      std::move(delayed_bind_client_extension_receiver_), media_task_runner_);
+
+  media_resource_ = media_resource;
+  client_ = client;
+  init_cb_ = std::move(init_cb);
+
+  MojoRendererWrapper::Initialize(
+      media_resource, client_,
+      base::BindOnce(&MediaPlayerRendererClient::OnRemoteRendererInitialized,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void MediaPlayerRendererClient::OnRemoteRendererInitialized(
+    media::PipelineStatus status) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(!init_cb_.is_null());
+
+  if (status == media::PIPELINE_OK) {
+    // Signal that we're using MediaPlayer so that we can properly differentiate
+    // within our metrics.
+    media::PipelineStatistics stats;
+    stats.video_pipeline_info = {true, false,
+                                 media::VideoDecoderType::kMediaCodec};
+    stats.audio_pipeline_info = {true, false,
+                                 media::AudioDecoderType::kMediaCodec};
+    client_->OnStatisticsUpdate(stats);
+  }
+  std::move(init_cb_).Run(status);
+}
+
+void MediaPlayerRendererClient::OnVideoSizeChange(const gfx::Size& size) {
+  client_->OnVideoNaturalSizeChange(size);
+}
+
+void MediaPlayerRendererClient::OnDurationChange(base::TimeDelta duration) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  media_resource_->ForwardDurationChangeToDemuxerHost(duration);
+}
+
+void MediaPlayerRendererClient::OnNewFrameAvailable(
+    uint32_t playerId,
+    base::UnsafeSharedMemoryRegion frame,
+    uint32_t yuv_size,
+    base::TimeDelta timestamp,
+    uint32_t width,
+    uint32_t height) {
+  base::WritableSharedMemoryMapping mapping = frame.MapAt(0, yuv_size);
+  if (!mapping.IsValid()) {
+    LOG(ERROR) << "Failed to map shared memory for size " << yuv_size;
+    return;
+  }
+
+  uint8_t* const yuv_buffer = static_cast<uint8_t*>(mapping.memory());
+  gfx::Size size(width, height);
+  scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
+      media::PIXEL_FORMAT_I420, size, gfx::Rect(size), size, timestamp);
+
+  uint8_t* video_buf = yuv_buffer;
+  const uint c_frm_size = yuv_size / 6;
+  const uint y_frm_size = c_frm_size << 2;  // * 4;
+
+  // U Plane buffer.
+  uint8_t* video_buf_u = video_buf + y_frm_size;
+
+  // V Plane buffer.
+  uint8_t* video_buf_v = video_buf_u + c_frm_size;
+  libyuv::I420Copy(
+      video_buf, video_frame.get()->stride(media::VideoFrame::kYPlane),
+      video_buf_u, video_frame.get()->stride(media::VideoFrame::kYPlane) / 2,
+      video_buf_v, video_frame.get()->stride(media::VideoFrame::kYPlane) / 2,
+      video_frame.get()->GetWritableVisibleData(media::VideoFrame::kYPlane),
+      video_frame.get()->stride(media::VideoFrame::kYPlane),
+      video_frame.get()->GetWritableVisibleData(media::VideoFrame::kUPlane),
+      video_frame.get()->stride(media::VideoFrame::kUPlane),
+      video_frame.get()->GetWritableVisibleData(media::VideoFrame::kVPlane),
+      video_frame.get()->stride(media::VideoFrame::kVPlane), width, height);
+  sink_->PaintSingleFrame(std::move(video_frame));
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h b/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h
new file mode 100644 (file)
index 0000000..b7e9e87
--- /dev/null
@@ -0,0 +1,111 @@
+// 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 CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_H_
+#define CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/single_thread_task_runner.h"
+#include "content/common/content_export.h"
+#include "media/base/media_resource.h"
+#include "media/base/renderer.h"
+#include "media/base/renderer_client.h"
+#include "media/base/video_renderer_sink.h"
+#include "media/mojo/clients/mojo_renderer.h"
+#include "media/mojo/clients/mojo_renderer_wrapper.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"
+
+namespace content {
+
+// MediaPlayerRendererClient lives in Renderer process and mirrors a
+// MediaPlayerRenderer living in the Browser process.
+//
+// It is primarily used as a media::Renderer that forwards calls from WMPI to
+// the MediaPlayerRenderer, by inheriting from MojoRendererWrapper.
+class CONTENT_EXPORT MediaPlayerRendererClient
+    : public media::mojom::MediaPlayerRendererClientExtension,
+      public media::MojoRendererWrapper {
+ public:
+  using RendererExtention = media::mojom::MediaPlayerRendererExtension;
+  using ClientExtention = media::mojom::MediaPlayerRendererClientExtension;
+
+  MediaPlayerRendererClient(
+      mojo::PendingRemote<RendererExtention> renderer_extension_remote,
+      mojo::PendingReceiver<ClientExtention> client_extension_receiver,
+      scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      std::unique_ptr<media::MojoRenderer> mojo_renderer,
+      media::VideoRendererSink* sink);
+
+  MediaPlayerRendererClient(const MediaPlayerRendererClient&) = delete;
+  MediaPlayerRendererClient& operator=(const MediaPlayerRendererClient&) =
+      delete;
+
+  ~MediaPlayerRendererClient() override;
+
+  // media::Renderer implementation (inherited from media::MojoRendererWrapper).
+  // and do not support encrypted media.
+  void Initialize(media::MediaResource* media_resource,
+                  media::RendererClient* client,
+                  media::PipelineStatusCallback init_cb) override;
+
+  // media::mojom::MediaPlayerRendererClientExtension implementation
+  void OnDurationChange(base::TimeDelta duration) override;
+  void OnVideoSizeChange(const gfx::Size& size) override;
+  void OnNewFrameAvailable(uint32_t playerId,
+                           base::UnsafeSharedMemoryRegion frame,
+                           uint32_t size,
+                           base::TimeDelta timestamp,
+                           uint32_t width,
+                           uint32_t height) override;
+
+ private:
+  void OnRemoteRendererInitialized(media::PipelineStatus status);
+
+  // The underlying type should always be a MediaUrlDemuxer, but we only use
+  // methods from the MediaResource interface.
+  media::MediaResource* media_resource_;
+
+  media::RendererClient* client_;
+
+  media::VideoRendererSink* sink_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+
+  // VideoFrames to |sink_| on the right thread.
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+
+  media::PipelineStatusCallback init_cb_;
+
+  // This class is constructed on the main task runner, and used on
+  // |media_task_runner_|. These member are used to delay calls to Bind() for
+  // |renderer_extension_ptr_| and |client_extension_binding_|, until we are on
+  // |media_task_runner_|.
+  // Both are set in the constructor, and consumed in Initialize().
+  mojo::PendingReceiver<ClientExtention>
+      delayed_bind_client_extension_receiver_;
+  mojo::PendingRemote<RendererExtention>
+      delayed_bind_renderer_extention_remote_;
+
+  // Used to call methods on the MediaPlayerRenderer in the browser process.
+  mojo::Remote<RendererExtention> renderer_extension_remote_;
+
+  // Used to receive events from MediaPlayerRenderer in the browser process.
+  mojo::Receiver<MediaPlayerRendererClientExtension> client_extension_receiver_{
+      this};
+
+  // NOTE: Weak pointers must be invalidated before all other member variables.
+  base::WeakPtrFactory<MediaPlayerRendererClient> weak_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_H_
diff --git a/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.cc b/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.cc
new file mode 100644 (file)
index 0000000..8519ca7
--- /dev/null
@@ -0,0 +1,58 @@
+// 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 "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h"
+
+#include "media/mojo/clients/mojo_renderer.h"
+#include "media/mojo/clients/mojo_renderer_factory.h"
+#include "media/mojo/mojom/renderer_extensions.mojom.h"
+#include "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.h"
+
+namespace content {
+
+MediaPlayerRendererClientFactory::MediaPlayerRendererClientFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory)
+    : compositor_task_runner_(std::move(compositor_task_runner)),
+      mojo_renderer_factory_(std::move(mojo_renderer_factory)) {}
+
+MediaPlayerRendererClientFactory::~MediaPlayerRendererClientFactory() {}
+
+std::unique_ptr<media::Renderer>
+MediaPlayerRendererClientFactory::CreateRenderer(
+    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+    const scoped_refptr<base::TaskRunner>& worker_task_runner,
+    media::AudioRendererSink* audio_renderer_sink,
+    media::VideoRendererSink* video_renderer_sink,
+    media::RequestOverlayInfoCB request_overlay_info_cb,
+    const gfx::ColorSpace& target_color_space) {
+  // Used to send messages from the MPRC (Renderer process), to the MPR (Browser
+  // process). The |renderer_extension_request| will be bound in
+  // MediaPlayerRenderer.
+  mojo::PendingRemote<media::mojom::MediaPlayerRendererExtension>
+      renderer_extension_remote;
+  auto renderer_extension_receiver =
+      renderer_extension_remote.InitWithNewPipeAndPassReceiver();
+
+  // Used to send messages from the MPR (Browser process), to the MPRC (Renderer
+  // process). The |client_extension_request| will be bound in
+  // MediaPlayerRendererClient.
+  mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
+      client_extension_remote;
+  auto client_extension_receiver =
+      client_extension_remote.InitWithNewPipeAndPassReceiver();
+
+  std::unique_ptr<media::MojoRenderer> mojo_renderer =
+      mojo_renderer_factory_->CreateMediaPlayerRenderer(
+          std::move(renderer_extension_receiver),
+          std::move(client_extension_remote), media_task_runner,
+          video_renderer_sink);
+
+  return std::make_unique<MediaPlayerRendererClient>(
+      std::move(renderer_extension_remote),
+      std::move(client_extension_receiver), media_task_runner,
+      compositor_task_runner_, std::move(mojo_renderer), video_renderer_sink);
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h b/tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h
new file mode 100644 (file)
index 0000000..319b4b3
--- /dev/null
@@ -0,0 +1,48 @@
+// 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 CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_FACTORY_H_
+#define CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_FACTORY_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/task/single_thread_task_runner.h"
+#include "content/common/content_export.h"
+#include "media/base/renderer_factory.h"
+#include "media/mojo/clients/mojo_renderer_factory.h"
+
+namespace media {
+class MojoRendererFactory;
+}
+
+namespace content {
+
+// The default class for creating a MediaPlayerRendererClient
+// and its associated MediaPlayerRenderer.
+class CONTENT_EXPORT MediaPlayerRendererClientFactory
+    : public media::RendererFactory {
+ public:
+  MediaPlayerRendererClientFactory(
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory);
+  ~MediaPlayerRendererClientFactory() override;
+
+  std::unique_ptr<media::Renderer> CreateRenderer(
+      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+      const scoped_refptr<base::TaskRunner>& worker_task_runner,
+      media::AudioRendererSink* audio_renderer_sink,
+      media::VideoRendererSink* video_renderer_sink,
+      media::RequestOverlayInfoCB request_surface_cb,
+      const gfx::ColorSpace& target_color_space) override;
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+
+  std::unique_ptr<media::MojoRendererFactory> mojo_renderer_factory_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_TIZEN_MEDIA_PLAYER_RENDERER_CLIENT_FACTORY_H_
index 9911a1a..8a8408e 100644 (file)
@@ -58,3 +58,12 @@ if (tizen_multimedia_support) {
     "media/tizen/audio_decoder_capi.cc",
   ]
 }
+
+if (tizen_multimedia) {
+  external_content_renderer_efl_sources += [
+    "//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/content/renderer/media/tizen/media_player_renderer_client_factory.cc",
+    "//tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.h",
+  ]
+}
index 5d9a968..1078804 100644 (file)
@@ -4,20 +4,11 @@
 
 import("//tizen_src/build/config/tizen_features.gni")
 
-if (tizen_multimedia_support) {
-  config("media_efl_config") {
-    defines = [
-      "TIZEN_VIDEO_CAPTURE_SUPPORT",
-      "TIZEN_MULTIMEDIA_ZEROCOPY_SUPPORT",
-    ]
-
-    include_dirs = [
-      "//third_party/WebKit",
-      "//third_party/libyuv/include",
-      "//third_party/mojo/src",
-      "//v8/include",
-    ]
-
-    defines += [ "TIZEN_MULTIMEDIA_EME_SUPPORT = 1" ]
-  }
+config("media_efl_config") {
+  include_dirs = [
+    "//third_party/WebKit",
+    "//third_party/libyuv/include",
+    "//third_party/mojo/src",
+    "//v8/include",
+  ]
 }
index 5c31cc3..82f7e45 100644 (file)
@@ -16,6 +16,8 @@ namespace media {
 class MEDIA_EXPORT AudioManagerCapi : public AudioManagerBase {
  public:
   explicit AudioManagerCapi(AudioLogFactory* audio_log_factory);
+  AudioManagerCapi(const AudioManagerCapi&) = delete;
+  AudioManagerCapi& operator=(const AudioManagerCapi&) = delete;
   ~AudioManagerCapi() override;
 
   AudioManagerCapi(const AudioManagerCapi&) = delete;
@@ -27,10 +29,8 @@ class MEDIA_EXPORT AudioManagerCapi : public AudioManagerBase {
   bool HasAudioOutputDevices() override;
   bool HasAudioInputDevices() override;
   void ShowAudioInputSettings() override;
-  void GetAudioInputDeviceNames(
-      AudioDeviceNames* device_names) override;
-  void GetAudioOutputDeviceNames(
-      AudioDeviceNames* device_names) override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
   AudioParameters GetInputStreamParameters(
       const std::string& device_id) override;
   // Implementation of AudioManagerBase.
@@ -40,9 +40,11 @@ class MEDIA_EXPORT AudioManagerCapi : public AudioManagerBase {
       const AudioParameters& params,
       const std::string& device_id) override;
   AudioInputStream* MakeLinearInputStream(
-      const AudioParameters& params, const std::string& device_id) override;
+      const AudioParameters& params,
+      const std::string& device_id) override;
   AudioInputStream* MakeLowLatencyInputStream(
-      const AudioParameters& params, const std::string& device_id) override;
+      const AudioParameters& params,
+      const std::string& device_id) override;
 
  protected:
   AudioParameters GetPreferredOutputStreamParameters(
index 97d50f6..7fa0fac 100644 (file)
@@ -27,7 +27,8 @@ class CapiAudioInputStream final : public AgcAudioStream<AudioInputStream> {
   CapiAudioInputStream(AudioManagerCapi* audio_manager,
                        const std::string& device_name,
                        const AudioParameters& params);
-
+  CapiAudioInputStream(const CapiAudioInputStream&) = delete;
+  CapiAudioInputStream& operator=(const CapiAudioInputStream&) = delete;
   ~CapiAudioInputStream() override;
 
   CapiAudioInputStream(const CapiAudioInputStream&) = delete;
@@ -44,6 +45,7 @@ class CapiAudioInputStream final : public AgcAudioStream<AudioInputStream> {
   void OnAudioIOData(const AudioBus* audio_bus,
                      uint32_t hardware_delay_bytes,
                      double normalized_volume);
+
  private:
   static void AudioStreamReadCB(audio_in_h handle,
                                 size_t nbytes,
index 5d58a57..6b7c4bd 100644 (file)
@@ -5,7 +5,6 @@
 // Creates an audio output stream based on the Tizen Core API.
 // Tizen Core api uses PULSE Synchronized API.
 
-
 #ifndef MEDIA_AUDIO_TIZEN_CAPI_AUDIO_OUTPUT_H_
 #define MEDIA_AUDIO_TIZEN_CAPI_AUDIO_OUTPUT_H_
 
@@ -25,7 +24,8 @@ class CapiAudioOutputStream : public AudioOutputStream {
  public:
   CapiAudioOutputStream(const AudioParameters& params,
                         AudioManagerBase* manager);
-
+  CapiAudioOutputStream(const CapiAudioOutputStream&) = delete;
+  CapiAudioOutputStream& operator=(const CapiAudioOutputStream&) = delete;
   ~CapiAudioOutputStream() override;
 
   CapiAudioOutputStream(const CapiAudioOutputStream&) = delete;
index 2ced3a2..901a1c1 100644 (file)
@@ -32,13 +32,19 @@ class MEDIA_EXPORT MediaPlayerEfl {
     int64_t end;
   } TimeRanges;
 
-  static MediaPlayerEfl* CreatePlayer(
-      int player_id, const GURL& url, double volume,
-      MediaPlayerManager* manager, const std::string& user_agent);
+  static MediaPlayerEfl* CreatePlayer(int player_id,
+                                      const GURL& url,
+                                      double volume,
+                                      MediaPlayerManager* manager,
+                                      const std::string& user_agent);
 
-  static MediaPlayerEfl* CreatePlayer(
-      int player_id, content::BrowserDemuxerEfl* demuxer,
-      int dumxer_id, MediaPlayerManager* manager);
+  static MediaPlayerEfl* CreatePlayer(int player_id,
+                                      content::BrowserDemuxerEfl* demuxer,
+                                      int dumxer_id,
+                                      MediaPlayerManager* manager);
+
+  MediaPlayerEfl(const MediaPlayerEfl&) = delete;
+  MediaPlayerEfl& operator=(const MediaPlayerEfl&) = delete;
 
   virtual ~MediaPlayerEfl() {}
 
index 6b51945..e4f2d09 100644 (file)
 #include "base/timer/timer.h"
 #include "base/tracked_objects.h"
 #include "content/public/browser/browser_message_filter.h"
-#include "media/base/ranges.h"
 #include "media/base/efl/media_player_efl.h"
+#include "media/base/ranges.h"
 #include "media/base/video_frame.h"
 
 namespace media {
 
-class MEDIA_EXPORT MediaPlayerBridgeCapi
-    : public MediaPlayerEfl {
+class MEDIA_EXPORT MediaPlayerBridgeCapi : public MediaPlayerEfl {
  public:
   typedef base::OnceCallback<void(const bool)> CompleteCB;
 
-  MediaPlayerBridgeCapi(int player_id, const GURL& url, double volume,
+  MediaPlayerBridgeCapi(int player_id,
+                        const GURL& url,
+                        double volume,
                         MediaPlayerManager* manager,
                         const std::string& user_agent);
+  MediaPlayerBridgeCapi(const MediaPlayerBridgeCapi&) = delete;
+  MediaPlayerBridgeCapi& operator=(const MediaPlayerBridgeCapi&) = delete;
   ~MediaPlayerBridgeCapi() override;
 
   MediaPlayerBridgeCapi(const MediaPlayerBridgeCapi&) = delete;
@@ -78,8 +81,7 @@ class MEDIA_EXPORT MediaPlayerBridgeCapi
   void SeekCompleteUpdate();
   void PlayerPrepared();
   void HandleBufferingStatus(int percent);
-  void RunCompleteCB(bool success,
-                     const tracked_objects::Location& from);
+  void RunCompleteCB(bool success, const tracked_objects::Location& from);
   player_state_e GetPlayerState();
 
  private:
index a2d0ae0..16b3c09 100644 (file)
 namespace media {
 
 // This class handles media source extensions for CAPI port.
-class MEDIA_EXPORT MediaSourcePlayerCapi
-    : public MediaPlayerEfl, public DemuxerEflClient {
-
+class MEDIA_EXPORT MediaSourcePlayerCapi : public MediaPlayerEfl,
+                                           public DemuxerEflClient {
  public:
   // Constructs a player with the given ID and demuxer. |manager| must outlive
   // the lifetime of this object.
-  MediaSourcePlayerCapi(
-      int player_id, std::unique_ptr<DemuxerEfl> demuxer,
-      MediaPlayerManager* manager);
-
+  MediaSourcePlayerCapi(int player_id,
+                        std::unique_ptr<DemuxerEfl> demuxer,
+                        MediaPlayerManager* manager);
+  MediaSourcePlayerCapi(const MediaSourcePlayerCapi&) = delete;
+  MediaSourcePlayerCapi& operator=(const MediaSourcePlayerCapi&) = delete;
   ~MediaSourcePlayerCapi() override;
 
   MediaSourcePlayerCapi(const MediaSourcePlayerCapi&) = delete;
@@ -45,8 +45,7 @@ class MEDIA_EXPORT MediaSourcePlayerCapi
   void Initialize() override;
 
   // DemuxerEflClient implementation.
-  void OnDemuxerConfigsAvailable(
-      const DemuxerConfigs& params) override;
+  void OnDemuxerConfigsAvailable(const DemuxerConfigs& params) override;
   void OnDemuxerDataAvailable(
       base::SharedMemoryHandle foreign_memory_handle,
       const media::DemuxedBufferMetaData& meta_data) override;
@@ -97,9 +96,8 @@ class MEDIA_EXPORT MediaSourcePlayerCapi
   void StopCurrentTimeUpdateTimer();
 
   void ReadFromQueueIfAny(DemuxerStream::Type type);
-  void SaveDecoderBuffer(
-      base::SharedMemoryHandle foreign_memory_handle,
-      const media::DemuxedBufferMetaData& meta_data);
+  void SaveDecoderBuffer(base::SharedMemoryHandle foreign_memory_handle,
+                         const media::DemuxedBufferMetaData& meta_data);
 
   std::unique_ptr<DemuxerEfl> demuxer_;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
index e1bd0d7..4205b15 100644 (file)
@@ -45,16 +45,16 @@ static std::string GetCameraErrorMessage(int err_code) {
   return media::VideoCaptureDeviceTizen::GetCameraErrorMessage(err_code);
 }
 
-bool OnCameraSupportedPreviewResolution(
-   int width, int height, void* user_data) {
+bool OnCameraSupportedPreviewResolution(int width,
+                                        int height,
+                                        void* user_data) {
   std::vector<gfx::Size>* sizes =
-      static_cast< std::vector<gfx::Size>* >(user_data);
+      static_cast<std::vector<gfx::Size>*>(user_data);
   DCHECK(sizes);
 
   if ((width > kMaxWidth && height > kMaxHeight) ||
       (height > kMaxWidth && width > kMaxHeight)) {
-    DVLOG(1) << "Ignore resolution [width:" << width
-             << " x height:" << height
+    DVLOG(1) << "Ignore resolution [width:" << width << " x height:" << height
              << "] and continue to next resolution";
     return true;
   }
@@ -62,10 +62,10 @@ bool OnCameraSupportedPreviewResolution(
   return true;
 }
 
-bool OnCameraSupportedPreviewFormat(
-    camera_pixel_format_e format, void* user_data) {
+bool OnCameraSupportedPreviewFormat(camera_pixel_format_e format,
+                                    void* user_data) {
   std::vector<media::VideoPixelFormat>* list_format =
-      static_cast< std::vector<media::VideoPixelFormat>* >(user_data);
+      static_cast<std::vector<media::VideoPixelFormat>*>(user_data);
   DCHECK(list_format);
 
   list_format->push_back(toChromiumType(format));
@@ -73,13 +73,13 @@ bool OnCameraSupportedPreviewFormat(
 }
 
 bool OnCameraSupportedFPS(camera_attr_fps_e fps, void* user_data) {
-  std::vector<int>* list_fps = static_cast< std::vector<int>* >(user_data);
+  std::vector<int>* list_fps = static_cast<std::vector<int>*>(user_data);
   DCHECK(list_fps);
   if (CAMERA_ATTR_FPS_AUTO == fps ||
       static_cast<camera_attr_fps_e>(kMaxFramerate) < fps) {
     // AUTO format is not defined on Chromium, so skip.
     DVLOG(1) << "Ignore fps: [CAMERA_ATTR_FPS_AUTO = " << CAMERA_ATTR_FPS_AUTO
-         << "] OR fps: [" << fps << " > " << kMaxFramerate << "]";
+             << "] OR fps: [" << fps << " > " << kMaxFramerate << "]";
     return true;
   }
   list_fps->push_back(static_cast<int>(fps));
@@ -100,8 +100,8 @@ void GenerateChromiumVideoCaptureFormat(
         format.pixel_format = *itrFMT;
         outSupportedFormats.push_back(format);
 
-        DVLOG(1) << " frame_size:" << format.frame_size.width()
-                 << "X" << format.frame_size.height()
+        DVLOG(1) << " frame_size:" << format.frame_size.width() << "X"
+                 << format.frame_size.height()
                  << " frame_rate:" << format.frame_rate
                  << " pixel_format:" << format.pixel_format;
       }
@@ -115,9 +115,7 @@ class CameraHandle final {
     return base::Singleton<CameraHandle>::get();
   }
 
-  bool IsValid() const {
-    return camera_handle_ != NULL;
-  }
+  bool IsValid() const { return camera_handle_ != NULL; }
 
   void GetDeviceSupportedFormats(
       media::VideoCaptureFormats& supported_formats) const {
@@ -131,44 +129,42 @@ class CameraHandle final {
       return;
     }
 
-    if (CAMERA_ERROR_NONE != (err = camera_foreach_supported_preview_resolution(
-          camera_handle_,
-          OnCameraSupportedPreviewResolution,
-          &supported_frame_sizes))) {
+    if (CAMERA_ERROR_NONE !=
+        (err = camera_foreach_supported_preview_resolution(
+             camera_handle_, OnCameraSupportedPreviewResolution,
+             &supported_frame_sizes))) {
       LOG(ERROR) << "Cannot get the supported resolutions for camera, Error:"
                  << GetCameraErrorMessage(err);
       return;
     }
 
-    if (CAMERA_ERROR_NONE != (err = camera_foreach_supported_preview_format(
-          camera_handle_,
-          OnCameraSupportedPreviewFormat,
-          &supported_pixel_formats))) {
+    if (CAMERA_ERROR_NONE !=
+        (err = camera_foreach_supported_preview_format(
+             camera_handle_, OnCameraSupportedPreviewFormat,
+             &supported_pixel_formats))) {
       LOG(ERROR) << "Cannot get the supported formats for camera, Error:"
                  << GetCameraErrorMessage(err);
       return;
     }
 
-    if (CAMERA_ERROR_NONE != (err = camera_attr_foreach_supported_fps(
-          camera_handle_,
-          OnCameraSupportedFPS,
-          &supported_frame_rates))) {
+    if (CAMERA_ERROR_NONE !=
+        (err = camera_attr_foreach_supported_fps(
+             camera_handle_, OnCameraSupportedFPS, &supported_frame_rates))) {
       LOG(ERROR) << "Cannot get the supported FPS for camera, Error:"
                  << GetCameraErrorMessage(err);
       return;
     }
     supported_formats.clear();
-    GenerateChromiumVideoCaptureFormat(supported_frame_sizes,
-                                       supported_frame_rates,
-                                       supported_pixel_formats,
-                                       supported_formats);
+    GenerateChromiumVideoCaptureFormat(
+        supported_frame_sizes, supported_frame_rates, supported_pixel_formats,
+        supported_formats);
   }
 
   int GetDeviceCounts() const {
     int device_count = 0;
     int err = 0;
-    if (CAMERA_ERROR_NONE != (err = camera_get_device_count(camera_handle_,
-                                                            &device_count))) {
+    if (CAMERA_ERROR_NONE !=
+        (err = camera_get_device_count(camera_handle_, &device_count))) {
       device_count = 0;
       LOG(ERROR) << "Cannot read camera count, Error:"
                  << GetCameraErrorMessage(err);
@@ -177,17 +173,19 @@ class CameraHandle final {
   }
 
  private:
-  CameraHandle()
-      : camera_handle_(NULL) {
+  CameraHandle() : camera_handle_(NULL) {
     int err = 0;
-    if (CAMERA_ERROR_NONE  != (err = camera_create(CAMERA_DEVICE_CAMERA0,
-                                                   &camera_handle_))) {
+    if (CAMERA_ERROR_NONE !=
+        (err = camera_create(CAMERA_DEVICE_CAMERA0, &camera_handle_))) {
       camera_handle_ = NULL;
       LOG(ERROR) << "Cannot create camera, Error:"
                  << GetCameraErrorMessage(err);
     }
   }
 
+  CameraHandle(const CameraHandle&) = delete;
+  CameraHandle& operator=(const CameraHandle&) = delete;
+
   ~CameraHandle() {
     if (camera_handle_ != NULL)
       camera_destroy(camera_handle_);
@@ -201,17 +199,15 @@ class CameraHandle final {
   friend struct base::DefaultSingletonTraits<CameraHandle>;
 };
 
-} // namespace
+}  // namespace
 
 namespace media {
 
 VideoCaptureDeviceFactoryTizen::VideoCaptureDeviceFactoryTizen(
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
-    : ui_task_runner_(ui_task_runner) {
-}
+    : ui_task_runner_(ui_task_runner) {}
 
-VideoCaptureDeviceFactoryTizen::~VideoCaptureDeviceFactoryTizen() {
-}
+VideoCaptureDeviceFactoryTizen::~VideoCaptureDeviceFactoryTizen() {}
 
 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryTizen::Create(
     const VideoCaptureDevice::Name& name) {
index 6257dd4..8c4f77a 100644 (file)
@@ -18,6 +18,10 @@ class MEDIA_EXPORT VideoCaptureDeviceFactoryTizen
  public:
   explicit VideoCaptureDeviceFactoryTizen(
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+  VideoCaptureDeviceFactoryTizen(const VideoCaptureDeviceFactoryTizen&) =
+      delete;
+  VideoCaptureDeviceFactoryTizen& operator=(
+      const VideoCaptureDeviceFactoryTizen&) = delete;
   ~VideoCaptureDeviceFactoryTizen() override;
 
   VideoCaptureDeviceFactoryTizen(const VideoCaptureDeviceFactoryTizen&) =
diff --git a/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h b/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h
new file mode 100644 (file)
index 0000000..d8fa257
--- /dev/null
@@ -0,0 +1,63 @@
+// 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_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_H_
+#define MEDIA_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_H_
+
+#include <esplusplayer_capi.h>
+
+#include "media/base/demuxer_stream.h"
+
+namespace media {
+
+using espp_buffer_size_t = unsigned long long;
+
+// Buffering level in percent.
+const int kUnderrunBufferThreshold = 1;
+const int kMinBufferThreshold = 30;
+const int kMaxBufferThreshold = 80;
+const int kOverflowBufferThreshold = 95;
+const int kMediaStreamBufferMinThreshold = 100;
+
+// Limit of platform player's audio or video buffer in milliseconds
+const int kMaxBufferTime = 3000;
+
+// Minimum platform player buffer fill to keep
+const int kMinBufferTimePercent = 80;
+
+enum BufferStatus {
+  kBufferNone,
+  kBufferUnderrun,
+  kBufferMinThreshold,
+  kBufferNormal,
+  kBufferMaxThreshold,
+  kBufferOverflow,
+  kBufferAhead,
+  kBufferEos,
+};
+
+class BufferObserver {
+ public:
+  using BufferingCallback =
+      base::RepeatingCallback<void(DemuxerStream::Type, BufferStatus)>;
+
+  static BufferObserver* CreateBufferObserver();
+
+  virtual ~BufferObserver() = default;
+
+  virtual void SetBufferSize(DemuxerStream::Type, espp_buffer_size_t) = 0;
+  virtual void SetBufferingCallback(const BufferingCallback&) = 0;
+  virtual void SetEos(DemuxerStream::Type) = 0;
+  virtual void UpdateBufferedSize(DemuxerStream::Type, espp_buffer_size_t) = 0;
+  virtual void ResetBufferStatus() = 0;
+  virtual int SetMediaStreamStatusCallback(
+      esplusplayer_handle,
+      esplusplayer_stream_type stream_type) = 0;
+  virtual BufferStatus GetAudioStatus() const = 0;
+  virtual BufferStatus GetVideoStatus() const = 0;
+  virtual void SetAudioVideoDtsDifference(int diff) = 0;
+};
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_H_
diff --git a/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.cc b/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.cc
new file mode 100644 (file)
index 0000000..cc04f52
--- /dev/null
@@ -0,0 +1,272 @@
+// 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/filters/esplusplayer_buffer_observer_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+
+namespace media {
+
+// static
+BufferObserver* BufferObserver::CreateBufferObserver() {
+  return new BufferObserverImpl();
+}
+
+// static
+void BufferObserverImpl::OnBufferedSizeChanged(
+    const esplusplayer_stream_type stream_type,
+    const esplusplayer_buffer_status status,
+    espp_buffer_size_t bytes,
+    void* user_data) {
+  if (stream_type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+    static_cast<BufferObserver*>(user_data)->UpdateBufferedSize(
+        DemuxerStream::VIDEO, bytes);
+  } else if (stream_type == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {
+    static_cast<BufferObserver*>(user_data)->UpdateBufferedSize(
+        DemuxerStream::AUDIO, bytes);
+  }
+}
+
+// static
+void BufferObserverImpl::OnBufferedTimeChanged(
+    const esplusplayer_stream_type stream_type,
+    const esplusplayer_buffer_status status,
+    uint64_t time,
+    void* user_data) {
+  if (stream_type == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {
+    static_cast<BufferObserverImpl*>(user_data)->SetNewStatusFromTime(
+        DemuxerStream::AUDIO, time);
+  } else if (stream_type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+    static_cast<BufferObserverImpl*>(user_data)->SetNewStatusFromTime(
+        DemuxerStream::VIDEO, time);
+  }
+}
+
+// static
+int BufferObserverImpl::ToPercent(uint64_t value, uint64_t max) {
+  return (value * 100 / max) ?: (value > 0);
+}
+
+void BufferObserverImpl::SetBufferSize(DemuxerStream::Type type,
+                                       espp_buffer_size_t size) {
+  GetBuffer(type).buffer_size = size;
+}
+
+// |handler| will be invoked from capi-player's thread.
+void BufferObserverImpl::SetBufferingCallback(
+    const BufferingCallback& handler) {
+  buffering_callback_ = handler;
+}
+
+void BufferObserverImpl::SetEos(DemuxerStream::Type type) {
+  if (GetBuffer(type).last_buffer_status != kBufferEos) {
+    CallbackIfNeed(type, kBufferEos);
+  }
+}
+
+void BufferObserverImpl::UpdateBufferedSize(DemuxerStream::Type type,
+                                            espp_buffer_size_t bytes) {
+  auto& buffer = GetBuffer(type);
+  DCHECK(buffer.buffer_size != 0);
+
+  if (buffer.last_buffer_status == kBufferEos || !buffer.buffer_size)
+    return;
+
+  // return 1% as long as there is something in the buffer
+  buffer.buffer_percent = ToPercent(bytes, buffer.buffer_size);
+  BufferStatus buffer_status = GetBufferStatusFromSize(buffer.buffer_percent);
+  DVLOG(INFO) << "Current Buffer Level: VIDEO: "
+              << GetBuffer(DemuxerStream::VIDEO).buffer_percent << "%"
+              << "\tAUDIO: " << GetBuffer(DemuxerStream::AUDIO).buffer_percent
+              << "%";
+
+  CallbackIfNeed(type, buffer_status);
+}
+
+void BufferObserverImpl::ResetBufferStatus() {
+  for (auto& buffer : buffers_) {
+    buffer.last_buffer_status = kBufferNone;
+  }
+}
+
+int BufferObserverImpl::SetMediaStreamStatusCallback(
+    esplusplayer_handle player,
+    esplusplayer_stream_type stream_type) {
+  const BufferStatusCallbacks callbacks = {
+      BufferObserverImpl::OnBufferedSizeChanged,
+      BufferObserverImpl::OnBufferedTimeChanged};
+  return SetBufferStatusCallbacks(player, stream_type, callbacks);
+}
+
+BufferStatus BufferObserverImpl::GetAudioStatus() const {
+  return GetBuffer(DemuxerStream::AUDIO).last_buffer_status;
+}
+
+BufferStatus BufferObserverImpl::GetVideoStatus() const {
+  return GetBuffer(DemuxerStream::VIDEO).last_buffer_status;
+}
+
+void BufferObserverImpl::SetAudioVideoDtsDifference(int diff) {
+  av_diff_ = diff;
+  if (!EnsureSimilarBufferedDuration()) {
+    const auto& audio = GetBuffer(DemuxerStream::AUDIO);
+    const auto& video = GetBuffer(DemuxerStream::VIDEO);
+    CallbackIfNeed(DemuxerStream::AUDIO, GetBufferStatusFromTime(audio));
+    CallbackIfNeed(DemuxerStream::VIDEO, GetBufferStatusFromTime(video));
+  }
+}
+
+void BufferObserverImpl::CallbackIfNeed(DemuxerStream::Type type,
+                                        BufferStatus status) {
+  if (buffering_callback_.is_null()) {
+    LOG(WARNING) << "buffering_callback_ is null, return";
+    return;
+  }
+
+  auto& buffer = GetBuffer(type);
+  if (buffer.last_buffer_status == status)
+    return;
+
+  buffer.last_buffer_status = status;
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&BufferObserverImpl::TriggerStatusChangeCallback,
+                     weak_factory_.GetWeakPtr(), type,
+                     buffer.last_buffer_status));
+}
+
+void BufferObserverImpl::TriggerStatusChangeCallback(
+    DemuxerStream::Type type,
+    BufferStatus status) const {
+  buffering_callback_.Run(type, status);
+}
+
+BufferStatus BufferObserverImpl::GetBufferStatusFromSize(int buffered_percent) {
+  if (buffered_percent < kUnderrunBufferThreshold) {
+    return kBufferUnderrun;
+  } else if (buffered_percent < kMinBufferThreshold) {
+    return kBufferMinThreshold;
+  } else if (buffered_percent > kOverflowBufferThreshold) {
+    return kBufferOverflow;
+  } else if (buffered_percent > kMaxBufferThreshold) {
+    return kBufferMaxThreshold;
+  }
+  return kBufferNormal;
+}
+
+BufferStatus BufferObserverImpl::GetBufferStatusFromTime(
+    const BufferObserverImpl::BufferDescriptor& buffer) {
+  if (buffer.has_size_overflow)
+    return kBufferOverflow;
+  if (buffer.duration_percent < kUnderrunBufferThreshold)
+    return kBufferUnderrun;
+  if (buffer.duration_percent < kMinBufferTimePercent)
+    return kBufferMinThreshold;
+  if (buffer.duration_percent > kOverflowBufferThreshold)
+    return kBufferOverflow;
+  return kBufferNormal;
+}
+
+void BufferObserverImpl::SetNewStatusFromTime(DemuxerStream::Type type,
+                                              uint64_t time) {
+  auto& buffer = GetBuffer(type);
+  if (buffer.last_buffer_status == kBufferEos)
+    return;
+
+  buffer.duration_percent = ToPercent(time, kMaxBufferTime);
+  if (!EnsureSimilarBufferedDuration())
+    CallbackIfNeed(type, GetBufferStatusFromTime(buffer));
+}
+
+bool BufferObserverImpl::EnsureSimilarBufferedDuration() {
+  const auto& audio = GetBuffer(DemuxerStream::AUDIO);
+  const auto& video = GetBuffer(DemuxerStream::VIDEO);
+  const bool has_both_av = (audio.last_buffer_status != kBufferNone) &&
+                           (video.last_buffer_status != kBufferNone);
+
+  const int kMaxDiff = 250;
+  const int av_diff = av_diff_;
+
+  if (has_both_av && (std::abs(av_diff) > kMaxDiff)) {
+    const auto audio_status =
+        (av_diff > kMaxDiff) ? kBufferAhead : GetBufferStatusFromTime(audio);
+    const auto video_status =
+        (av_diff < -kMaxDiff) ? kBufferAhead : GetBufferStatusFromTime(video);
+
+    CallbackIfNeed(DemuxerStream::AUDIO, audio_status);
+    CallbackIfNeed(DemuxerStream::VIDEO, video_status);
+    LOG(INFO) << "Uneven A/V buffering, audio fill: " << audio.duration_percent
+              << "%, video fill: " << video.duration_percent
+              << "%, audio DTS - video DTS: " << av_diff << "ms";
+    return true;
+  } else {
+    return false;
+  }
+}
+
+int BufferObserverImpl::SetBufferStatusCallbacks(
+    esplusplayer_handle player,
+    esplusplayer_stream_type type,
+    const BufferStatusCallbacks& callbacks) {
+  int ret{ESPLUSPLAYER_ERROR_TYPE_NONE};
+
+  esplusplayer_buffer_option option =
+      (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO)
+          ? ESPLUSPLAYER_BUFFER_VIDEO_MIN_BYTE_THRESHOLD
+          : ESPLUSPLAYER_BUFFER_AUDIO_MIN_BYTE_THRESHOLD;
+
+  if ((ret = esplusplayer_set_buffer_size(player, option,
+                                          kMediaStreamBufferMinThreshold)) !=
+      ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(WARNING) << "Failed setting buffer size threshold: " << ret;
+  }
+
+  if ((ret = esplusplayer_set_buffer_byte_status_cb(
+           player, callbacks.size_status_cb, this)) !=
+      ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(WARNING) << "Failed setting buffer size status callback: " << ret;
+  }
+  option = (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO)
+               ? ESPLUSPLAYER_BUFFER_VIDEO_MAX_TIME_SIZE
+               : ESPLUSPLAYER_BUFFER_AUDIO_MAX_TIME_SIZE;
+  if ((ret = esplusplayer_set_buffer_size(player, option, kMaxBufferTime)) !=
+      ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(WARNING) << "Failed setting buffer time limit: " << ret;
+  }
+
+  option = (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO)
+               ? ESPLUSPLAYER_BUFFER_VIDEO_MIN_TIME_THRESHOLD
+               : ESPLUSPLAYER_BUFFER_AUDIO_MIN_TIME_THRESHOLD;
+  // Due to bug in MMPlayer if threshold != 100 then it stops accepting data
+  // when the threshold is reached instead of when max is reached. Also status
+  // callbacks stop arriving after this.
+  if ((ret = esplusplayer_set_buffer_size(player, option,
+                                          kMediaStreamBufferMinThreshold)) !=
+      ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(WARNING) << "Failed setting buffer time threshold: " << ret;
+  }
+
+  // FIXME: TM1 is giving wrong time size for pipeline.
+#if 0
+  if ((ret = esplusplayer_set_buffer_time_status_cb(
+           player, callbacks.time_status_cb, this)) !=
+      ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(WARNING) << "Failed setting buffer time status callback: " << ret;
+  }
+#endif
+  return ret;
+}
+
+BufferObserverImpl::BufferDescriptor& BufferObserverImpl::GetBuffer(
+    DemuxerStream::Type type) {
+  return buffers_[GetElementryStreamIndex(type)];
+}
+
+const BufferObserverImpl::BufferDescriptor& BufferObserverImpl::GetBuffer(
+    DemuxerStream::Type type) const {
+  return buffers_[GetElementryStreamIndex(type)];
+}
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.h b/tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.h
new file mode 100644 (file)
index 0000000..bfae69e
--- /dev/null
@@ -0,0 +1,99 @@
+// 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_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_IMPL_H_
+#define MEDIA_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_IMPL_H_
+
+#include "tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h"
+
+#include "base/task/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "tizen_src/chromium_impl/media/filters/esplusplayer_util.h"
+
+namespace media {
+
+// Observes audio or video buffer status (underrun/overflow) reported from
+// platform player. Platform player reports buffer status in two ways:
+// - Total size of pushed frames
+// - Total duration of pushed frames
+//
+// Any change is propagated to Espp, which can then stop or start pushing
+// frame data accordingly.
+//
+// Espp will be informed about overflow when any of the two reaches maximum
+// (size / time overflow). However only time underrun will be propagated. This
+// way buffer fill is generally controlled on time basis.
+// This makes it independent of stream bitrate.
+class BufferObserverImpl : public BufferObserver {
+ public:
+  BufferObserverImpl()
+      : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+        weak_factory_(this) {}
+
+  void SetBufferSize(DemuxerStream::Type, espp_buffer_size_t) final;
+
+  // |handler| will be invoked from player's thread.
+  void SetBufferingCallback(const BufferingCallback&) final;
+  void SetEos(DemuxerStream::Type) final;
+  void UpdateBufferedSize(DemuxerStream::Type, espp_buffer_size_t) final;
+  void ResetBufferStatus() final;
+  int SetMediaStreamStatusCallback(esplusplayer_handle,
+                                   esplusplayer_stream_type stream_type) final;
+  BufferStatus GetAudioStatus() const final;
+  BufferStatus GetVideoStatus() const final;
+  void SetAudioVideoDtsDifference(int diff) final;
+
+ private:
+  struct BufferStatusCallbacks {
+    esplusplayer_buffer_byte_status_cb size_status_cb;
+    esplusplayer_buffer_time_status_cb time_status_cb;
+  };
+
+  struct BufferDescriptor {
+    BufferDescriptor() {}
+    espp_buffer_size_t buffer_size{};
+    int buffer_percent{};
+    int duration_percent{};
+    BufferStatus last_buffer_status{kBufferNone};
+    bool has_size_overflow{};
+  };
+
+  static int ToPercent(uint64_t value, uint64_t max);
+  static void OnBufferedSizeChanged(const esplusplayer_stream_type stream_type,
+                                    const esplusplayer_buffer_status status,
+                                    espp_buffer_size_t bytes,
+                                    void* user_data);
+  static void OnBufferedTimeChanged(const esplusplayer_stream_type stream_type,
+                                    const esplusplayer_buffer_status status,
+                                    uint64_t time,
+                                    void* user_data);
+
+  void CallbackIfNeed(DemuxerStream::Type type, BufferStatus status);
+  void TriggerStatusChangeCallback(DemuxerStream::Type type,
+                                   BufferStatus status) const;
+
+  BufferStatus GetBufferStatusFromSize(int buffered_percent);
+  BufferStatus GetBufferStatusFromTime(const BufferDescriptor& buffer);
+
+  void SetNewStatusFromTime(DemuxerStream::Type type, uint64_t time);
+  bool EnsureSimilarBufferedDuration();
+
+  int SetBufferStatusCallbacks(esplusplayer_handle player,
+                               esplusplayer_stream_type type,
+                               const BufferStatusCallbacks& callbacks);
+
+  BufferDescriptor& GetBuffer(DemuxerStream::Type type);
+  const BufferDescriptor& GetBuffer(DemuxerStream::Type type) const;
+
+  std::array<BufferDescriptor, kElementryStreamCount> buffers_;
+  BufferingCallback buffering_callback_;
+
+  std::atomic<int> av_diff_{};
+
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtrFactory<BufferObserverImpl> weak_factory_;
+};
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_ESPLUSPLAYER_BUFFER_OBSERVER_IMPL_H_
diff --git a/tizen_src/chromium_impl/media/filters/esplusplayer_util.cc b/tizen_src/chromium_impl/media/filters/esplusplayer_util.cc
new file mode 100644 (file)
index 0000000..1eab5b8
--- /dev/null
@@ -0,0 +1,179 @@
+// 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 "tizen_src/chromium_impl/media/filters/esplusplayer_util.h"
+
+#include "base/logging.h"
+
+namespace {
+
+#define ENUM_CASE(x) \
+  case x:            \
+    return #x;       \
+    break
+
+}  // namespace
+
+namespace media {
+
+const char* GetString(media::BufferStatus status) {
+  switch (status) {
+    ENUM_CASE(media::kBufferNone);
+    ENUM_CASE(media::kBufferUnderrun);
+    ENUM_CASE(media::kBufferMinThreshold);
+    ENUM_CASE(media::kBufferNormal);
+    ENUM_CASE(media::kBufferMaxThreshold);
+    ENUM_CASE(media::kBufferOverflow);
+    ENUM_CASE(media::kBufferAhead);
+    ENUM_CASE(media::kBufferEos);
+  };
+  NOTREACHED() << "Invalid BufferStatus (" << status << ")";
+  return "";
+}
+
+const char* GetString(esplusplayer_submit_status status) {
+  switch (status) {
+    ENUM_CASE(ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED);
+    ENUM_CASE(ESPLUSPLAYER_SUBMIT_STATUS_INVALID_PACKET);
+    ENUM_CASE(ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY);
+    ENUM_CASE(ESPLUSPLAYER_SUBMIT_STATUS_FULL);
+    ENUM_CASE(ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS);
+  };
+  NOTREACHED() << "Invalid Submit Status (" << status << ")";
+  return "";
+}
+
+const char* GetString(esplusplayer_state state) {
+  switch (state) {
+    ENUM_CASE(ESPLUSPLAYER_STATE_NONE);
+    ENUM_CASE(ESPLUSPLAYER_STATE_IDLE);
+    ENUM_CASE(ESPLUSPLAYER_STATE_READY);
+    ENUM_CASE(ESPLUSPLAYER_STATE_PLAYING);
+    ENUM_CASE(ESPLUSPLAYER_STATE_PAUSED);
+    ENUM_CASE(ESPLUSPLAYER_STATE_MAX);
+  };
+  NOTREACHED() << "Invalid state (" << state << ")";
+  return "";
+}
+
+esplusplayer_audio_mime_type ConvertToESPlusAudioMimeType(
+    media::AudioCodec codec) {
+  esplusplayer_audio_mime_type audioMimeType;
+  switch (codec) {
+    case media::AudioCodec::kAAC:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC;
+      break;
+    case media::AudioCodec::kMP3:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_MP3;
+      break;
+    case media::AudioCodec::kOpus:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_OPUS;
+      break;
+    case media::AudioCodec::kPCM:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S16LE;
+      break;
+    case media::AudioCodec::kVorbis:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_VORBIS;
+      break;
+    case media::AudioCodec::kEAC3:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_EAC3;
+      break;
+    case media::AudioCodec::kAC3:
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_AC3;
+      break;
+    default: {
+      LOG(WARNING) << "Unknown codec :" << codec << ". Returning MP3.";
+      audioMimeType = ESPLUSPLAYER_AUDIO_MIME_TYPE_MP3;
+    }
+  }
+  return audioMimeType;
+}
+
+esplusplayer_video_mime_type ConvertToESPlusVideoMimeType(
+    media::VideoCodec codec) {
+  esplusplayer_video_mime_type videoMimeType;
+  switch (codec) {
+    case media::VideoCodec::kH264:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_H264;
+      break;
+    case media::VideoCodec::kMPEG2:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_MPEG2;
+      break;
+    case media::VideoCodec::kMPEG4:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_MPEG4;
+      break;
+    case media::VideoCodec::kVP8:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_VP8;
+      break;
+    case media::VideoCodec::kVP9:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_VP9;
+      break;
+    case media::VideoCodec::kHEVC:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_HEVC;
+      break;
+    case media::VideoCodec::kAV1:
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_AV1;
+      break;
+    default: {
+      LOG(WARNING) << "Unknown codec :" << codec << ". Returning H264.";
+      videoMimeType = ESPLUSPLAYER_VIDEO_MIME_TYPE_H264;
+    }
+  }
+  return videoMimeType;
+}
+
+int GetElementryStreamIndex(DemuxerStream::Type type) {
+  switch (type) {
+    case DemuxerStream::AUDIO:
+      return 0;
+    case DemuxerStream::VIDEO:
+      return 1;
+    default:
+      LOG(WARNING) << "Stream type [" << type << "] is not supported";
+      return -1;
+  }
+}
+
+DemuxerStream::Type GetDemuxerStreamType(
+    const esplusplayer_stream_type stream_type) {
+  switch (stream_type) {
+    case ESPLUSPLAYER_STREAM_TYPE_AUDIO:
+      return DemuxerStream::AUDIO;
+    case ESPLUSPLAYER_STREAM_TYPE_VIDEO:
+      return DemuxerStream::VIDEO;
+    default:
+      return DemuxerStream::UNKNOWN;
+  }
+}
+
+esplusplayer_stream_type GetESPlusPlayerStreamType(DemuxerStream::Type type) {
+  switch (type) {
+    case DemuxerStream::AUDIO:
+      return ESPLUSPLAYER_STREAM_TYPE_AUDIO;
+    case DemuxerStream::VIDEO:
+      return ESPLUSPLAYER_STREAM_TYPE_VIDEO;
+    default:
+      return ESPLUSPLAYER_STREAM_TYPE_MAX;
+  }
+}
+
+PipelineStatus GetPipelineError(const esplusplayer_error_type error) {
+  if (error == ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_AUDIO_CODEC ||
+      error == ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_VIDEO_CODEC ||
+      error == ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FORMAT ||
+      error == ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE)
+    return DECODER_ERROR_NOT_SUPPORTED;
+  else if (error == ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED)
+    return PIPELINE_ERROR_READ;
+  else if (error == ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE)
+    return PIPELINE_ERROR_INVALID_STATE;
+  else if (error == ESPLUSPLAYER_ERROR_TYPE_OUT_OF_MEMORY)
+    return PIPELINE_ERROR_ABORT;
+  else if (error == ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED)
+    return PIPELINE_ERROR_NETWORK;
+
+  return PIPELINE_ERROR_DECODE;
+}
+
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/filters/esplusplayer_util.h b/tizen_src/chromium_impl/media/filters/esplusplayer_util.h
new file mode 100644 (file)
index 0000000..a89992e
--- /dev/null
@@ -0,0 +1,47 @@
+// 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_FILTERS_ESPP_PLAYER_UTIL_H_
+#define MEDIA_FILTERS_ESPP_PLAYER_UTIL_H_
+
+#include <esplusplayer_capi.h>
+
+#include "media/base/audio_codecs.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/video_codecs.h"
+#include "tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h"
+
+namespace media {
+
+const int kElementryStreamCount = 2;  // Audio, Video only.
+
+const int kVideoFramerateDen = 100;
+const int kVideoFramerateNum = 2997;
+const int kFHDVideoMaxWidth = 1920;
+const int kFHDVideoMaxHeight = 1080;
+const int k4KVideoMaxWidth = 3840;
+const int k4KVideoMaxHeight = 2160;
+const int kMaxFramerate = 60;
+const int kBuffingLimit = 5;  // Seconds
+
+const char* GetString(media::BufferStatus status);
+const char* GetString(esplusplayer_submit_status status);
+const char* GetString(esplusplayer_state state);
+
+esplusplayer_audio_mime_type ConvertToESPlusAudioMimeType(
+    media::AudioCodec codec);
+esplusplayer_video_mime_type ConvertToESPlusVideoMimeType(
+    media::VideoCodec codec);
+
+int GetElementryStreamIndex(DemuxerStream::Type type);
+
+DemuxerStream::Type GetDemuxerStreamType(
+    const esplusplayer_stream_type stream_type);
+esplusplayer_stream_type GetESPlusPlayerStreamType(DemuxerStream::Type type);
+
+PipelineStatus GetPipelineError(const esplusplayer_error_type error);
+
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_ESPP_PLAYER_UTIL_H_
diff --git a/tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc b/tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc
new file mode 100644 (file)
index 0000000..f50d4c4
--- /dev/null
@@ -0,0 +1,986 @@
+// 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 "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h"
+
+#include <esplusplayer_internal.h>
+#include <tbm_surface.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/renderer_client.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
+
+namespace media {
+
+espp_buffer_size_t GetMaxAudioBufferSize() {
+  // Assume MPEG-1 Audio Layer III 320kbit/s audio.
+  return 320 * 1000 / 8 * kBuffingLimit;
+}
+
+espp_buffer_size_t GetMaxVideoBufferSize(gfx::Size size) {
+  const int fps = 60;  // Asume max.
+
+  // Reference of buffer size estimation:
+  // http://stackoverflow.com/questions/5024114/suggested-compression-ratio-with-h-264
+  return static_cast<espp_buffer_size_t>(size.width()) * size.height() * fps *
+         2 * 7 / 100 / 8 * kBuffingLimit;
+}
+
+void ReadyToPrepareCallback(const esplusplayer_stream_type stream_type,
+                            void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnReadyToPrepare(stream_type);
+}
+
+void PrepareCompleteCallback(bool result, void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnPrepareComplete(result);
+}
+
+void EosCallback(void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnEos();
+}
+
+void FrameReadyCallback(const esplusplayer_decoded_video_packet* packet,
+                        void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnFrameReady(packet);
+}
+
+void FlushCompleteCallback(void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnFlushComplete();
+}
+
+void ReadyToSeekCallback(const esplusplayer_stream_type stream_type,
+                         const uint64_t seek_time,
+                         void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnReadyToSeek(stream_type, seek_time);
+}
+
+void SeekCompleteCallback(void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnSeekComplete();
+}
+
+void ResourceConflictCallback(void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnResourceConflict();
+}
+
+void ErrorCallback(const esplusplayer_error_type error_type, void* user_data) {
+  MediaPlayerESPlusPlayer* player =
+      static_cast<MediaPlayerESPlusPlayer*>(user_data);
+  if (!player)
+    return;
+
+  player->OnError(error_type);
+}
+
+// static
+MediaPlayerESPlusPlayer* MediaPlayerESPlusPlayer::GetEsppPlayer() {
+  return base::Singleton<MediaPlayerESPlusPlayer>::get();
+}
+
+MediaPlayerESPlusPlayer::~MediaPlayerESPlusPlayer() {
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  weak_factory_.InvalidateWeakPtrs();
+
+  int error = esplusplayer_destroy(esplayer_);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
+    LOG(ERROR) << "esplusplayer_destroy failed, error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+  esplayer_ = nullptr;
+}
+
+bool MediaPlayerESPlusPlayer::Initialize(VideoRendererSink* sink) {
+  if (esplayer_) {
+    LOG(INFO) << "esplus player exists!";
+    sink_ = sink;
+    return true;
+  }
+
+  esplayer_ = esplusplayer_create();
+  if (!esplayer_) {
+    LOG(ERROR) << "Cannot create esplus player!";
+    return false;
+  }
+
+  esplusplayer_set_ready_to_prepare_cb(esplayer_, &ReadyToPrepareCallback,
+                                       this);
+  esplusplayer_set_prepare_async_done_cb(esplayer_, &PrepareCompleteCallback,
+                                         this);
+  esplusplayer_set_eos_cb(esplayer_, &EosCallback, this);
+  esplusplayer_set_media_packet_video_decoded_cb(esplayer_, &FrameReadyCallback,
+                                                 this);
+  esplusplayer_set_flush_done_cb(esplayer_, &FlushCompleteCallback, this);
+  esplusplayer_set_ready_to_seek_cb(esplayer_, &ReadyToSeekCallback, this);
+  esplusplayer_set_seek_done_cb(esplayer_, &SeekCompleteCallback, this);
+  esplusplayer_set_resource_conflicted_cb(esplayer_, &ResourceConflictCallback,
+                                          this);
+  esplusplayer_set_error_cb(esplayer_, &ErrorCallback, this);
+
+  int error = esplusplayer_open(esplayer_);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_open failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return false;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " state:" << GetString(GetPlayerState());
+  error = esplusplayer_set_video_frame_buffer_type(
+      esplayer_, ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_set_video_frame_buffer_type failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return false;
+  }
+
+  sink_ = sink;
+  buffer_observer_.reset(BufferObserver::CreateBufferObserver());
+  buffer_observer_->SetBufferingCallback(
+      base::BindRepeating(&MediaPlayerESPlusPlayer::OnBufferingStatusChanged,
+                          weak_factory_.GetWeakPtr()));
+  return true;
+}
+
+void MediaPlayerESPlusPlayer::SetTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+  task_runner_ = task_runner.get();
+}
+
+void MediaPlayerESPlusPlayer::SetStreamInfo(DemuxerStream::Type type,
+                                            DemuxerStream* stream,
+                                            RendererClient* client) {
+  if (!esplayer_) {
+    LOG(ERROR) << "Invalid player handle. Send error to client.";
+    client->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
+  SetDemuxerStream(type, stream);
+  SetRendererClient(type, client);
+  InitializeStreamConfig(type);
+}
+
+void MediaPlayerESPlusPlayer::Prepare() {
+  if (GetPlayerState() != ESPLUSPLAYER_STATE_IDLE) {
+    LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
+               << " Prepare called on invalid state : "
+               << GetString(GetPlayerState());
+    return;
+  }
+
+  int error = esplusplayer_prepare_async(esplayer_);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "player prepare failed! error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+
+    if (IsValid(DemuxerStream::AUDIO))
+      GetRendererClient(DemuxerStream::AUDIO)
+          ->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    else if (IsValid(DemuxerStream::VIDEO))
+      GetRendererClient(DemuxerStream::VIDEO)
+          ->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
+  buffer_observer_->ResetBufferStatus();
+}
+
+void MediaPlayerESPlusPlayer::Play() {
+  int error = ESPLUSPLAYER_ERROR_TYPE_NONE;
+  esplusplayer_state state = GetPlayerState();
+
+  is_paused_ = false;
+  if (state < ESPLUSPLAYER_STATE_READY || !is_prepared_ || is_seeking_) {
+    LOG(INFO) << "state : " << GetString(state)
+              << " is_prepared : " << is_prepared_
+              << " is_seeking : " << is_seeking_
+              << " is_buffering : " << is_buffering_;
+    return;
+  }
+
+  if (is_buffering_) {
+    LOG(INFO) << "state : " << GetString(state) << " is_paused : " << is_paused_
+              << " is_buffering : " << is_buffering_;
+    // return;
+  }
+
+  SetVolume(volume_);
+  if (should_set_playback_rate_)
+    SetRate(playback_rate_);
+
+  if (state == ESPLUSPLAYER_STATE_READY)
+    error = esplusplayer_start(esplayer_);
+  else if (state == ESPLUSPLAYER_STATE_PAUSED ||
+           state == ESPLUSPLAYER_STATE_PLAYING)
+    error = esplusplayer_resume(esplayer_);
+
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "player play failed! state #" << GetString(state)
+               << " error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    // TODO: handle error!
+    return;
+  }
+}
+
+void MediaPlayerESPlusPlayer::Pause(bool is_media_related_action) {
+  if (!is_media_related_action)
+    is_paused_ = true;
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " is_buffering : " << is_buffering_
+            << " is_media_action : " << is_media_related_action;
+  esplusplayer_state state = GetPlayerState();
+  if (state != ESPLUSPLAYER_STATE_PLAYING) {
+    LOG(WARNING) << "Cannot pause in " << GetString(state) << " state";
+    return;
+  }
+
+  int error = esplusplayer_pause(esplayer_);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "player pause failed! error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    // TODO: handle error!
+    return;
+  }
+}
+
+void MediaPlayerESPlusPlayer::SetRate(double rate) {
+  if (playback_rate_ == rate)
+    return;
+
+  playback_rate_ = rate;
+  if (GetPlayerState() < ESPLUSPLAYER_STATE_READY) {
+    should_set_playback_rate_ = true;
+    return;
+  }
+
+  bool should_mute = rate >= 2.0;
+  int error =
+      esplusplayer_set_playback_rate(esplayer_, playback_rate_, should_mute);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "player pause failed! error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return;
+  }
+  should_set_playback_rate_ = false;
+}
+
+void MediaPlayerESPlusPlayer::Seek(base::TimeDelta time) {
+  if (GetCurrentTime() == time)
+    return;
+
+  if (GetPlayerState() < ESPLUSPLAYER_STATE_READY || !is_prepared_ ||
+      is_seeking_) {
+    LOG(INFO) << "Add to pending seek ("
+              << ") state: " << GetString(GetPlayerState())
+              << " is_prepared : " << is_prepared_
+              << " is_seeking : " << is_seeking_;
+    pending_seek_ = true;
+    pending_seek_position_ = time;
+    return;
+  }
+
+  LOG(INFO) << __func__ << " : " << time;
+  int error = esplusplayer_seek(esplayer_, time.InMilliseconds());
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "Seek failed. Current time : " << time.InSecondsF()
+               << " error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return;
+  }
+
+  is_seeking_ = true;
+  pending_seek_ = false;
+  seek_position_ = time;
+  pending_seek_position_ = base::TimeDelta();
+}
+
+void MediaPlayerESPlusPlayer::Flush(base::OnceClosure flush_cb) {
+  NOTIMPLEMENTED();
+  std::move(flush_cb).Run();
+}
+
+void MediaPlayerESPlusPlayer::SetVolume(double volume) {
+  volume_ = volume;
+  if (GetPlayerState() == ESPLUSPLAYER_STATE_NONE)
+    return;
+
+  int error = esplusplayer_set_volume(esplayer_, 100 * volume_);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
+    LOG(ERROR) << "esplusplayer_set_volume failed, error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+}
+
+base::TimeDelta MediaPlayerESPlusPlayer::GetCurrentTime() {
+  if (GetPlayerState() < ESPLUSPLAYER_STATE_PLAYING)
+    return base::TimeDelta();
+
+  if (is_seeking_)
+    return seek_position_;
+
+  uint64_t time = 0;  // In milliseconds.
+  int error = esplusplayer_get_playing_time(esplayer_, &time);
+  if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+    LOG(ERROR) << "esplusplayer_get_playing_time failed. error #"
+               << esplusplayer_get_error_string(
+                      static_cast<esplusplayer_error_type>(error));
+    return base::TimeDelta();
+  }
+
+  return base::Milliseconds(time);
+}
+
+esplusplayer_state MediaPlayerESPlusPlayer::GetPlayerState() {
+  return esplusplayer_get_state(esplayer_);
+}
+
+void MediaPlayerESPlusPlayer::InitializeStreamConfig(DemuxerStream::Type type) {
+  DemuxerStream* stream = GetDemuxerStream(type);
+  CHECK(stream);
+
+  if (type == DemuxerStream::AUDIO) {
+    media::AudioDecoderConfig audio_config = stream->audio_decoder_config();
+    if (!audio_config.IsValidConfig()) {
+      LOG(INFO) << "Invalid audio config";
+      return;
+    }
+
+    LOG(INFO) << "Audio config : " << audio_config.AsHumanReadableString();
+    esplusplayer_audio_stream_info audio_stream_info;
+    memset(&audio_stream_info, 0, sizeof(esplusplayer_audio_stream_info));
+
+    audio_stream_info.mime_type =
+        ConvertToESPlusAudioMimeType(audio_config.codec());
+    audio_stream_info.bitrate = audio_config.bytes_per_channel() *
+                                audio_config.samples_per_second() * 8;
+    audio_stream_info.channels =
+        media::ChannelLayoutToChannelCount(audio_config.channel_layout());
+    audio_stream_info.sample_rate = audio_config.samples_per_second();
+    audio_stream_info.codec_data_length = audio_config.extra_data().size();
+
+    if (audio_stream_info.codec_data_length > 0) {
+      audio_stream_info.codec_data =
+          (char*)(const_cast<unsigned char*>(audio_config.extra_data().data()));
+    } else {
+      audio_stream_info.codec_data = NULL;
+    }
+
+    int error =
+        esplusplayer_set_audio_stream_info(esplayer_, &audio_stream_info);
+    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+      LOG(ERROR) << "esplusplayer_set_audio_stream_info failed. error code "
+                 << error;
+      GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+      return;
+    }
+
+    espp_buffer_size_t max_buffer_size = GetMaxAudioBufferSize();
+    buffer_observer_->SetMediaStreamStatusCallback(
+        esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+    buffer_observer_->SetBufferSize(type, max_buffer_size);
+
+    error = esplusplayer_set_buffer_size(
+        esplayer_, ESPLUSPLAYER_BUFFER_AUDIO_MAX_BYTE_SIZE, max_buffer_size);
+    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
+      LOG(WARNING) << "Failed to set audio buffer to " << max_buffer_size / 1024
+                   << "KB";
+
+    SetIsValid(type, true);
+    GetRendererClient(type)->OnAudioConfigChange(audio_config);
+  }
+
+  if (type == DemuxerStream::VIDEO) {
+    media::VideoDecoderConfig video_config = stream->video_decoder_config();
+    if (!video_config.IsValidConfig()) {
+      LOG(ERROR) << "Invalid video config.";
+      return;
+    }
+
+    LOG(INFO) << "Video config : " << video_config.AsHumanReadableString();
+    esplusplayer_video_stream_info video_stream_info;
+    memset(&video_stream_info, 0, sizeof(esplusplayer_video_stream_info));
+
+    video_stream_info.width = video_config.coded_size().width();
+    video_stream_info.height = video_config.coded_size().height();
+    video_stream_info.mime_type =
+        ConvertToESPlusVideoMimeType(video_config.codec());
+
+    // TODO: Fetch frame rate from demuxer?
+    video_stream_info.framerate_num = kVideoFramerateNum;
+    video_stream_info.framerate_den = kVideoFramerateDen;
+    video_stream_info.codec_data_length = video_config.extra_data().size();
+    if (video_stream_info.codec_data_length > 0) {
+      video_stream_info.codec_data =
+          (char*)(const_cast<unsigned char*>(video_config.extra_data().data()));
+    } else {
+      video_stream_info.codec_data = NULL;
+    }
+
+    video_stream_info.max_width = k4KVideoMaxWidth;
+    video_stream_info.max_height = k4KVideoMaxHeight;
+
+    int error =
+        esplusplayer_set_video_stream_info(esplayer_, &video_stream_info);
+    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE) {
+      LOG(ERROR) << "esplusplayer_set_video_stream_info failed. error code "
+                 << error;
+      GetRendererClient(type)->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+      return;
+    }
+
+    espp_buffer_size_t max_buffer_size =
+        GetMaxVideoBufferSize(video_config.coded_size());
+    buffer_observer_->SetMediaStreamStatusCallback(
+        esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+    buffer_observer_->SetBufferSize(type, max_buffer_size);
+
+    error = esplusplayer_set_buffer_size(
+        esplayer_, ESPLUSPLAYER_BUFFER_VIDEO_MAX_BYTE_SIZE, max_buffer_size);
+    if (error != ESPLUSPLAYER_ERROR_TYPE_NONE)
+      LOG(WARNING) << "Failed to set video buffer to " << max_buffer_size / 1024
+                   << "KB";
+
+    SetIsValid(type, true);
+    GetRendererClient(type)->OnVideoConfigChange(video_config);
+  }
+}
+
+void MediaPlayerESPlusPlayer::ReadBuffer(DemuxerStream::Type type) {
+  // TODO: Check and read from Queue.
+  if (!ShouldFeed(type) || ReadRequested(type))
+    return;
+
+  SetReadRequested(type, true);
+  GetDemuxerStream(type)->Read(
+      base::BindOnce(&MediaPlayerESPlusPlayer::OnBufferReady,
+                     weak_factory_.GetWeakPtr(), type));
+}
+
+void MediaPlayerESPlusPlayer::OnBufferReady(
+    DemuxerStream::Type type,
+    DemuxerStream::Status status,
+    scoped_refptr<DecoderBuffer> buffer) {
+  switch (status) {
+    case DemuxerStream::kAborted:
+    case DemuxerStream::kError:
+      GetBufferQueue(type).clear();
+      break;
+    case DemuxerStream::kConfigChanged:
+      // Clear pending buffer of older config
+      GetBufferQueue(type).clear();
+      InitializeStreamConfig(type);
+      break;
+    case DemuxerStream::kOk: {
+      if (buffer.get()->end_of_stream()) {
+        SubmitEosPacket(type);
+        return;
+      }
+
+      SubmitEsPacket(type, std::move(buffer));
+      break;
+    }
+  }
+
+  SetReadRequested(type, false);
+  ReadBuffer(type);
+}
+
+void MediaPlayerESPlusPlayer::SubmitEosPacket(DemuxerStream::Type type) {
+  if (IsEos(type))
+    return;
+
+  esplusplayer_submit_status ret = esplusplayer_submit_eos_packet(
+      esplayer_, GetESPlusPlayerStreamType(type));
+  if (ret != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Submit Eos (" << DemuxerStream::GetTypeName(type)
+               << ") Packet failed, ret:" << ret;
+    return;
+  }
+
+  buffer_observer_->SetEos(type);
+  SetIsEos(type, true);
+}
+
+void MediaPlayerESPlusPlayer::SubmitEsPacket(
+    DemuxerStream::Type type,
+    scoped_refptr<DecoderBuffer> buffer) {
+  esplusplayer_es_packet packet;
+  memset(&packet, 0, sizeof(esplusplayer_es_packet));
+  packet.type = GetESPlusPlayerStreamType(type);
+
+  packet.buffer = (char*)(const_cast<unsigned char*>(buffer->data()));
+  packet.buffer_size = buffer->data_size();
+  packet.pts = buffer->timestamp().InMilliseconds();
+  packet.duration = buffer->duration().InMilliseconds();
+
+  // This filed only set when PushMediaPacket
+  packet.matroska_color_info = nullptr;
+  esplusplayer_submit_status ret =
+      esplusplayer_submit_packet(esplayer_, &packet);
+  if (ret != ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS) {
+    if (ret == ESPLUSPLAYER_SUBMIT_STATUS_FULL ||
+        ret == ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY ||
+        ret == ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED) {
+      GetBufferQueue(type).push_back(buffer);
+    }
+
+    LOG(WARNING) << "submit " << DemuxerStream::GetTypeName(type)
+                 << " packet : " << GetString(ret);
+  }
+}
+
+void MediaPlayerESPlusPlayer::OnBufferingStatusChanged(DemuxerStream::Type type,
+                                                       BufferStatus status) {
+  LOG(INFO) << __func__ << " " << DemuxerStream::GetTypeName(type) << " : "
+            << GetString(status);
+  const auto RequestStateChange = [this](esplusplayer_state state) {
+    LOG(INFO) << __func__ << " req. change state : " << GetString(state);
+    switch (state) {
+      case ESPLUSPLAYER_STATE_PLAYING:
+        Play();
+        break;
+      case ESPLUSPLAYER_STATE_PAUSED:
+        Pause(true);
+        break;
+      default:
+        NOTREACHED();
+    }
+  };
+
+  SetBufferStatus(type, status);
+  switch (status) {
+    case kBufferUnderrun:
+      //  RequestStateChange(ESPLUSPLAYER_STATE_PAUSED);
+      [[fallthrough]];
+    case kBufferMinThreshold:
+    case kBufferNormal:
+      is_buffering_ = true;
+      SetShouldFeed(type, true);
+      ReadBuffer(type);
+      break;
+
+    case kBufferMaxThreshold:
+    case kBufferOverflow:
+    case kBufferEos:
+    case kBufferAhead:
+      is_buffering_ = false;
+      SetShouldFeed(type, false);
+      //  if (!is_paused_)
+      //    RequestStateChange(ESPLUSPLAYER_STATE_PLAYING);
+      break;
+    case kBufferNone:
+      NOTREACHED();
+  }
+
+  // TODO: Check if controller can control underflow. Currently buffering
+  // related state changes are commented for the same (in Play and
+  // RequestStateChange).
+  if (status == kBufferUnderrun) {
+    GetRendererClient(type)->OnBufferingStateChange(BUFFERING_HAVE_NOTHING,
+                                                    DEMUXER_UNDERFLOW);
+  } else if (status == kBufferMaxThreshold || status == kBufferOverflow ||
+             status == kBufferAhead) {
+    GetRendererClient(type)->OnBufferingStateChange(
+        BUFFERING_HAVE_ENOUGH, BUFFERING_CHANGE_REASON_UNKNOWN);
+  }
+}
+
+void MediaPlayerESPlusPlayer::OnReadyToPrepare(
+    const esplusplayer_stream_type stream_type) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnReadyToPrepare,
+                                  weak_factory_.GetWeakPtr(), stream_type));
+    return;
+  }
+
+  LOG(INFO) << "OnReadyToPrepare : "
+            << DemuxerStream::GetTypeName(GetDemuxerStreamType(stream_type));
+  if (stream_type != ESPLUSPLAYER_STREAM_TYPE_AUDIO &&
+      stream_type != ESPLUSPLAYER_STREAM_TYPE_VIDEO)
+    return;
+
+  DemuxerStream::Type type = GetDemuxerStreamType(stream_type);
+  if (IsValid(type)) {
+    SetShouldFeed(type, true);
+    ReadBuffer(type);
+  }
+}
+
+void MediaPlayerESPlusPlayer::OnPrepareComplete(bool result) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnPrepareComplete,
+                                  weak_factory_.GetWeakPtr(), result));
+    return;
+  }
+
+  if (!result) {
+    LOG(ERROR) << "OnPrepareComplete prepare_async failed.";
+    if (IsValid(DemuxerStream::AUDIO))
+      GetRendererClient(DemuxerStream::AUDIO)
+          ->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    else if (IsValid(DemuxerStream::VIDEO))
+      GetRendererClient(DemuxerStream::VIDEO)
+          ->OnError(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
+  if (GetPlayerState() != ESPLUSPLAYER_STATE_READY) {
+    LOG(ERROR) << "Invalid state (" << GetString(GetPlayerState())
+               << ") change during prepare. Returning.";
+    return;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " seek_position : " << seek_position_
+            << " pending_seek_ : " << pending_seek_
+            << " pending_seek_position_ : " << pending_seek_position_;
+
+  is_prepared_ = true;
+  if (pending_seek_) {
+    Seek(pending_seek_position_);
+    pending_seek_position_ = base::TimeDelta();
+  }
+}
+
+void MediaPlayerESPlusPlayer::OnEos() {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&MediaPlayerESPlusPlayer::OnEos,
+                                          weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  if (IsValid(DemuxerStream::VIDEO))
+    GetRendererClient(DemuxerStream::VIDEO)->OnEnded();
+  else if (IsValid(DemuxerStream::AUDIO))
+    GetRendererClient(DemuxerStream::AUDIO)->OnEnded();
+}
+
+void MediaPlayerESPlusPlayer::OnFrameReady(
+    const esplusplayer_decoded_video_packet* packet) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnFrameReady,
+                                  weak_factory_.GetWeakPtr(), packet));
+    return;
+  }
+  tbm_surface_info_s suf_info = {
+      0,
+  };
+  tbm_surface_h tbm_surface = static_cast<tbm_surface_h>(packet->surface_data);
+
+  if (TBM_SURFACE_ERROR_NONE != tbm_surface_get_info(tbm_surface, &suf_info)) {
+    LOG(ERROR) << "|tbm_surface_get_info| failed";
+    return;
+  }
+
+  int width = static_cast<int>(suf_info.width);
+  int height = static_cast<int>(suf_info.height);
+  gfx::Size size(width, height);
+
+  base::TimeDelta timestamp = base::Milliseconds(packet->pts);
+  DLOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+             << " size:" << size.ToString()
+             << ", timestamp:" << timestamp.InMilliseconds()
+             << ", duration:" << packet->duration << ", Player(" << esplayer_
+             << ", state:" << GetString(GetPlayerState());
+
+  base::UnsafeSharedMemoryRegion shared_memory;
+  uint32_t shared_memory_size =
+      suf_info.planes[0].size + (suf_info.planes[0].size / 2);
+  shared_memory = base::UnsafeSharedMemoryRegion::Create(shared_memory_size);
+  if (!shared_memory.IsValid()) {
+    LOG(ERROR) << "Shared Memory creation failed.";
+    return;
+  }
+
+  base::WritableSharedMemoryMapping memory_mapping;
+  memory_mapping = shared_memory.Map();
+  if (!memory_mapping.IsValid()) {
+    LOG(ERROR) << "Shared Memory handle could not be obtained";
+    return;
+  }
+
+  unsigned char* y_ptr = static_cast<unsigned char*>(memory_mapping.memory());
+
+  // Video format will always be converted to I420
+  switch (suf_info.format) {
+    case TBM_FORMAT_NV12: {
+      unsigned char* u_ptr = y_ptr + suf_info.planes[0].size;
+      unsigned char* v_ptr = u_ptr + (suf_info.planes[0].size / 4);
+      libyuv::NV12ToI420(suf_info.planes[0].ptr, suf_info.planes[0].stride,
+                         suf_info.planes[1].ptr, suf_info.planes[1].stride,
+                         y_ptr, suf_info.planes[0].stride, u_ptr,
+                         suf_info.planes[1].stride / 2, v_ptr,
+                         suf_info.planes[1].stride / 2, suf_info.width,
+                         suf_info.height);
+      break;
+    }
+    case TBM_FORMAT_YUV420: {
+      unsigned char* u_ptr = y_ptr + suf_info.planes[0].size;
+      unsigned char* v_ptr = u_ptr + suf_info.planes[1].size;
+      libyuv::I420Copy(
+          suf_info.planes[0].ptr, suf_info.planes[0].stride,
+          suf_info.planes[1].ptr, suf_info.planes[1].stride,
+          suf_info.planes[2].ptr, suf_info.planes[2].stride, y_ptr,
+          suf_info.planes[0].stride, u_ptr, suf_info.planes[1].stride, v_ptr,
+          suf_info.planes[2].stride, suf_info.width, suf_info.height);
+      break;
+    }
+    default: {
+      NOTIMPLEMENTED();
+      LOG(WARNING) << "Not supported format";
+      return;
+    }
+  }
+  if (!sink_) {
+    esplusplayer_decoded_buffer_destroy(
+        esplayer_, const_cast<esplusplayer_decoded_video_packet*>(packet));
+    data_cb_.Run(0, std::move(shared_memory), shared_memory_size, timestamp,
+                 width, height);
+  } else {
+    uint8_t* const yuv_buffer = static_cast<uint8_t*>(memory_mapping.memory());
+    scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateFrame(
+        media::PIXEL_FORMAT_I420, size, gfx::Rect(size), size, timestamp);
+
+    uint8_t* video_buf = yuv_buffer;
+    const uint c_frm_size = shared_memory_size / 6;
+    const uint y_frm_size = c_frm_size << 2;  // * 4;
+
+    // U Plane buffer.
+    uint8_t* video_buf_u = video_buf + y_frm_size;
+
+    // V Plane buffer.
+    uint8_t* video_buf_v = video_buf_u + c_frm_size;
+
+    libyuv::I420Copy(
+        video_buf, video_frame.get()->stride(VideoFrame::kYPlane), video_buf_u,
+        video_frame.get()->stride(VideoFrame::kYPlane) / 2, video_buf_v,
+        video_frame.get()->stride(VideoFrame::kYPlane) / 2,
+        video_frame.get()->GetWritableVisibleData(VideoFrame::kYPlane),
+        video_frame.get()->stride(VideoFrame::kYPlane),
+        video_frame.get()->GetWritableVisibleData(VideoFrame::kUPlane),
+        video_frame.get()->stride(VideoFrame::kUPlane),
+        video_frame.get()->GetWritableVisibleData(VideoFrame::kVPlane),
+        video_frame.get()->stride(VideoFrame::kVPlane), width, height);
+    esplusplayer_decoded_buffer_destroy(
+        esplayer_, const_cast<esplusplayer_decoded_video_packet*>(packet));
+
+    sink_->PaintSingleFrame(video_frame);
+  }
+}
+
+void MediaPlayerESPlusPlayer::OnFlushComplete() {
+  NOTIMPLEMENTED();
+}
+
+void MediaPlayerESPlusPlayer::OnReadyToSeek(
+    const esplusplayer_stream_type stream_type,
+    const uint64_t seek_time) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MediaPlayerESPlusPlayer::OnReadyToSeek,
+                       weak_factory_.GetWeakPtr(), stream_type, seek_time));
+    return;
+  }
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " : "
+            << DemuxerStream::GetTypeName(GetDemuxerStreamType(stream_type))
+            << " : " << seek_time;
+
+  SetShouldFeed(GetDemuxerStreamType(stream_type), true);
+  SetReadRequested(GetDemuxerStreamType(stream_type), false);
+  ReadBuffer(GetDemuxerStreamType(stream_type));
+}
+
+void MediaPlayerESPlusPlayer::OnSeekComplete() {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnSeekComplete,
+                                  weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
+            << " is_paused:" << is_paused_;
+  is_seeking_ = false;
+  if (pending_seek_) {
+    Seek(pending_seek_position_);
+    pending_seek_position_ = base::TimeDelta();
+    return;
+  }
+
+  if (!is_paused_)
+    Play();
+}
+
+void MediaPlayerESPlusPlayer::OnResourceConflict() {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaPlayerESPlusPlayer::OnResourceConflict,
+                                  weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+  NOTIMPLEMENTED();
+}
+
+void MediaPlayerESPlusPlayer::OnError(const esplusplayer_error_type error) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&MediaPlayerESPlusPlayer::OnError,
+                                          weak_factory_.GetWeakPtr(), error));
+    return;
+  }
+
+  LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " : "
+            << esplusplayer_get_error_string(
+                   static_cast<esplusplayer_error_type>(error));
+
+  if (IsValid(DemuxerStream::VIDEO))
+    GetRendererClient(DemuxerStream::VIDEO)->OnError(GetPipelineError(error));
+  else if (IsValid(DemuxerStream::AUDIO))
+    GetRendererClient(DemuxerStream::AUDIO)->OnError(GetPipelineError(error));
+}
+
+MediaPlayerESPlusPlayer::ElementryStream&
+MediaPlayerESPlusPlayer::GetElementryStream(DemuxerStream::Type type) {
+  return elementry_stream_[GetElementryStreamIndex(type)];
+}
+
+const MediaPlayerESPlusPlayer::ElementryStream&
+MediaPlayerESPlusPlayer::GetElementryStream(DemuxerStream::Type type) const {
+  return elementry_stream_[GetElementryStreamIndex(type)];
+}
+
+bool MediaPlayerESPlusPlayer::IsValid(DemuxerStream::Type type) const {
+  return GetElementryStream(type).is_valid_;
+}
+
+void MediaPlayerESPlusPlayer::SetIsValid(DemuxerStream::Type type, bool value) {
+  GetElementryStream(type).is_valid_ = value;
+}
+
+bool MediaPlayerESPlusPlayer::IsEos(DemuxerStream::Type type) const {
+  return GetElementryStream(type).is_eos_;
+}
+
+void MediaPlayerESPlusPlayer::SetIsEos(DemuxerStream::Type type, bool value) {
+  GetElementryStream(type).is_eos_ = value;
+}
+
+bool MediaPlayerESPlusPlayer::ShouldFeed(DemuxerStream::Type type) const {
+  return GetElementryStream(type).should_feed_;
+}
+
+void MediaPlayerESPlusPlayer::SetShouldFeed(DemuxerStream::Type type,
+                                            bool value) {
+  GetElementryStream(type).should_feed_ = value;
+}
+
+bool MediaPlayerESPlusPlayer::ReadRequested(DemuxerStream::Type type) const {
+  return GetElementryStream(type).read_requested_;
+}
+
+void MediaPlayerESPlusPlayer::SetReadRequested(DemuxerStream::Type type,
+                                               bool value) {
+  GetElementryStream(type).read_requested_ = value;
+}
+
+BufferStatus MediaPlayerESPlusPlayer::GetBufferStatus(
+    DemuxerStream::Type type) {
+  return GetElementryStream(type).buffer_status_;
+}
+
+void MediaPlayerESPlusPlayer::SetBufferStatus(DemuxerStream::Type type,
+                                              BufferStatus status) {
+  GetElementryStream(type).buffer_status_ = status;
+}
+
+DemuxerStream* MediaPlayerESPlusPlayer::GetDemuxerStream(
+    DemuxerStream::Type type) const {
+  return GetElementryStream(type).input_stream_;
+}
+
+void MediaPlayerESPlusPlayer::SetDemuxerStream(DemuxerStream::Type type,
+                                               DemuxerStream* stream) {
+  GetElementryStream(type).input_stream_ = stream;
+}
+
+RendererClient* MediaPlayerESPlusPlayer::GetRendererClient(
+    DemuxerStream::Type type) const {
+  return GetElementryStream(type).renderer_client_;
+}
+
+void MediaPlayerESPlusPlayer::SetRendererClient(DemuxerStream::Type type,
+                                                RendererClient* client) {
+  GetElementryStream(type).renderer_client_ = client;
+}
+
+base::circular_deque<scoped_refptr<DecoderBuffer>>
+MediaPlayerESPlusPlayer::GetBufferQueue(DemuxerStream::Type type) {
+  return GetElementryStream(type).pending_buffers_;
+}
+
+void MediaPlayerESPlusPlayer::SetFrameAvailableCallback(
+    const DataRequestCB& datacb) {
+  data_cb_ = datacb;
+}
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h b/tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h
new file mode 100644 (file)
index 0000000..ec5ce91
--- /dev/null
@@ -0,0 +1,149 @@
+// 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_FILTERS_MEDIA_PLAYER_ESPLUSPLAYER_H_
+#define MEDIA_FILTERS_MEDIA_PLAYER_ESPLUSPLAYER_H_
+
+#include <esplusplayer_capi.h>
+
+#include "base/containers/circular_deque.h"
+#include "base/memory/singleton.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_renderer_sink.h"
+#include "tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h"
+#include "tizen_src/chromium_impl/media/filters/esplusplayer_util.h"
+
+namespace media {
+
+class DemuxerStream;
+class RendererClient;
+
+// This class handles media source extensions for CAPI port.
+class MEDIA_EXPORT MediaPlayerESPlusPlayer {
+ public:
+  static MediaPlayerESPlusPlayer* GetEsppPlayer();
+
+  using DataRequestCB =
+      base::RepeatingCallback<void(uint32_t,
+                                   base::UnsafeSharedMemoryRegion,
+                                   uint32_t,
+                                   base::TimeDelta,
+                                   uint32_t,
+                                   uint32_t)>;
+  bool Initialize(VideoRendererSink* sink);
+  void Prepare();
+  void SetTaskRunner(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+  void SetStreamInfo(DemuxerStream::Type type,
+                     DemuxerStream* stream,
+                     RendererClient* client);
+
+  void Play();
+  void Pause(bool is_media_related_action = false);
+  void SetRate(double rate);
+  void Seek(base::TimeDelta time);
+  void Flush(base::OnceClosure flush_cb);
+  void SetVolume(double volume);
+  base::TimeDelta GetCurrentTime();
+
+  // Callback handler
+  void OnReadyToPrepare(const esplusplayer_stream_type stream_type);
+  void OnPrepareComplete(bool result);
+  void OnEos();
+  void OnFrameReady(const esplusplayer_decoded_video_packet* packet);
+  void OnFlushComplete();
+  void OnReadyToSeek(const esplusplayer_stream_type stream_type,
+                     const uint64_t seek_time);
+  void OnSeekComplete();
+  void OnResourceConflict();
+  void OnError(const esplusplayer_error_type error_type);
+
+  void OnBufferingStatusChanged(DemuxerStream::Type type, BufferStatus status);
+  void SetFrameAvailableCallback(const DataRequestCB& datacb);
+
+ private:
+  struct ElementryStream {
+    bool is_valid_ = false;
+    bool is_eos_ = false;
+    bool should_feed_ = false;
+    bool read_requested_ = false;
+    BufferStatus buffer_status_ = kBufferNone;  // Not used!
+    DemuxerStream* input_stream_ = nullptr;
+    RendererClient* renderer_client_ = nullptr;
+    base::circular_deque<scoped_refptr<DecoderBuffer>> pending_buffers_;
+  };
+
+  friend struct base::DefaultSingletonTraits<MediaPlayerESPlusPlayer>;
+
+  explicit MediaPlayerESPlusPlayer() = default;
+  ~MediaPlayerESPlusPlayer();
+  MediaPlayerESPlusPlayer(const MediaPlayerESPlusPlayer&) = delete;
+  MediaPlayerESPlusPlayer& operator=(const MediaPlayerESPlusPlayer&) = delete;
+
+  esplusplayer_state GetPlayerState();
+
+  void InitializeStreamConfig(DemuxerStream::Type type);
+  void ReadBuffer(DemuxerStream::Type type);
+  void OnBufferReady(DemuxerStream::Type type,
+                     DemuxerStream::Status status,
+                     scoped_refptr<DecoderBuffer> buffer);
+  void SubmitEosPacket(DemuxerStream::Type type);
+  void SubmitEsPacket(DemuxerStream::Type type,
+                      scoped_refptr<DecoderBuffer> buffer);
+
+  ElementryStream& GetElementryStream(DemuxerStream::Type type);
+  const ElementryStream& GetElementryStream(DemuxerStream::Type type) const;
+  bool IsValid(DemuxerStream::Type type) const;
+  void SetIsValid(DemuxerStream::Type type, bool value);
+  bool IsEos(DemuxerStream::Type type) const;
+  void SetIsEos(DemuxerStream::Type type, bool value);
+  bool ShouldFeed(DemuxerStream::Type type) const;
+  void SetShouldFeed(DemuxerStream::Type type, bool value);
+  bool ReadRequested(DemuxerStream::Type type) const;
+  void SetReadRequested(DemuxerStream::Type type, bool value);
+  BufferStatus GetBufferStatus(DemuxerStream::Type type);
+  void SetBufferStatus(DemuxerStream::Type type, BufferStatus status);
+  DemuxerStream* GetDemuxerStream(DemuxerStream::Type type) const;
+  void SetDemuxerStream(DemuxerStream::Type type, DemuxerStream* stream);
+  RendererClient* GetRendererClient(DemuxerStream::Type type) const;
+  void SetRendererClient(DemuxerStream::Type type, RendererClient* client);
+  base::circular_deque<scoped_refptr<DecoderBuffer>> GetBufferQueue(
+      DemuxerStream::Type type);
+
+  esplusplayer_handle esplayer_ = nullptr;
+
+  double volume_ = 1.0;
+  bool playback_rate_ = 0.0;
+  bool is_prepared_ = false;
+  bool is_paused_ = true;
+  bool is_buffering_ = false;
+
+  bool is_flushing_ = false;
+  bool is_seeking_ = false;
+  base::TimeDelta seek_position_;
+
+  bool pending_seek_ = false;
+  base::TimeDelta pending_seek_position_;
+
+  bool should_set_playback_rate_ = false;
+
+  base::OnceClosure flush_cb_;
+  DataRequestCB data_cb_;
+
+  VideoRendererSink* sink_;
+
+  std::array<ElementryStream, kElementryStreamCount> elementry_stream_;
+  std::unique_ptr<BufferObserver> buffer_observer_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<MediaPlayerESPlusPlayer> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_MEDIA_PLAYER_ESPLUSPLAYER_H_
index e7a983e..ce25af0 100644 (file)
@@ -8,6 +8,8 @@ external_media_efl_deps = []
 external_media_efl_sources = []
 external_exclude_media_efl_sources = []
 external_media_efl_config = []
+external_media_video_decode_config = []
+external_media_video_decode_sources = []
 
 if (use_ozone) {
   external_media_efl_deps += [ "//tizen_src/chromium_impl/ui/ozone:ozone_efl" ]
@@ -84,3 +86,21 @@ if (tizen_multimedia_support) {
 
   external_media_efl_config += [ ":media_efl_config" ]
 }
+
+if (tizen_multimedia) {
+  external_media_video_decode_config += [
+    "//tizen_src/build:esplusplayer",
+    "//tizen_src/build:libesplusplayer",
+    "//tizen_src/chromium_impl/media:media_efl_config",
+  ]
+
+  external_media_video_decode_sources += [
+    "//tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer.h",
+    "//tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.cc",
+    "//tizen_src/chromium_impl/media/filters/esplusplayer_buffer_observer_impl.h",
+    "//tizen_src/chromium_impl/media/filters/esplusplayer_util.cc",
+    "//tizen_src/chromium_impl/media/filters/esplusplayer_util.h",
+    "//tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc",
+    "//tizen_src/chromium_impl/media/filters/media_player_esplusplayer.h",
+  ]
+}