- 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>
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
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>
#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 {
mojo::PendingReceiver<media::mojom::MediaPlayerRendererExtension>
renderer_extension_receiver) {
DCHECK(thread_checker_.CalledOnValidThread());
-
media::MojoRendererService::Create(
nullptr,
std::make_unique<MediaPlayerRenderer>(
}
#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,
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,
#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
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_);
}
#endif
-#if BUILDFLAG(IS_ANDROID)
+#if defined(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
void MediaInterfaceFactory::CreateMediaPlayerRenderer(
mojo::PendingRemote<media::mojom::MediaPlayerRendererClientExtension>
client_extension_remote,
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>
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,
}
}
-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")
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" ]
#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);
// 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_),
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);
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
"h264_bitstream_buffer.h",
]
}
+
+ if (tizen_multimedia) {
+ configs += external_media_video_decode_config
+ sources += external_media_video_decode_sources
+ }
}
source_set("perftests") {
# 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.
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,
video_renderer_sink,
std::move(renderer_remote));
}
-#endif // BUILDFLAG(IS_ANDROID)
+#endif // BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
} // namespace media
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,
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
enabled_features += [ "enable_cast_renderer" ]
}
+ if (tizen_multimedia) {
+ enabled_features += [ "tizen_multimedia" ]
+ }
+
shared_typemaps = [
{
types = [
(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);
};
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";
// 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
}
#endif
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) || defined(TIZEN_MULTIMEDIA)
void InterfaceFactoryImpl::CreateMediaPlayerRenderer(
mojo::PendingRemote<mojom::MediaPlayerRendererClientExtension>
client_extension_ptr,
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>
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>
BuildRequires: pkgconfig(ecore-input)
BuildRequires: pkgconfig(efl-extension)
BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(esplusplayer)
BuildRequires: pkgconfig(evas)
BuildRequires: pkgconfig(feedback)
BuildRequires: pkgconfig(fontconfig)
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 += [
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"
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"
}
}
+if (tizen_multimedia) {
+ config("esplusplayer") {
+ ldflags = [ "-lesplusplayer" ]
+ }
+
+ tizen_pkg_config("libesplusplayer") {
+ packages = [ "esplusplayer" ]
+ }
+}
+
tizen_pkg_config("libmm-player") {
packages = []
if (is_tizen) {
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"
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
}
enable_nacl=false
enable_basic_printing=true
enable_print_preview=true
- tizen_multimedia_eme_support=false
use_xkbcommon=true
target_os="\"tizen\""
target_sysroot=\"/\"
# [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
"
}
"//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",
--- /dev/null
+// 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
--- /dev/null
+// 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_
// 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;
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
"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",
+ ]
+}
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",
+ ]
}
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;
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.
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(
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;
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,
// 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_
public:
CapiAudioOutputStream(const AudioParameters& params,
AudioManagerBase* manager);
-
+ CapiAudioOutputStream(const CapiAudioOutputStream&) = delete;
+ CapiAudioOutputStream& operator=(const CapiAudioOutputStream&) = delete;
~CapiAudioOutputStream() override;
CapiAudioOutputStream(const CapiAudioOutputStream&) = delete;
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() {}
#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;
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:
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;
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;
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_;
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;
}
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));
}
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));
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;
}
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 {
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);
}
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_);
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) {
public:
explicit VideoCaptureDeviceFactoryTizen(
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+ VideoCaptureDeviceFactoryTizen(const VideoCaptureDeviceFactoryTizen&) =
+ delete;
+ VideoCaptureDeviceFactoryTizen& operator=(
+ const VideoCaptureDeviceFactoryTizen&) = delete;
~VideoCaptureDeviceFactoryTizen() override;
VideoCaptureDeviceFactoryTizen(const VideoCaptureDeviceFactoryTizen&) =
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
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" ]
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",
+ ]
+}