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