"//tizen_src/build:omxil-e4x12-v4l2",
]
sources += [
+ "tizen/category_selector.cc",
+ "tizen/category_selector.h",
"tizen/cdm_utils.cc",
"tizen/cdm_utils.h",
"tizen/decoder_facade_factory.cc",
"tizen/dummy_drm.cc",
"tizen/dummy_drm.h",
"tizen/extended_video_decoder_config.h",
+ "tizen/latency_mode.h",
"tizen/lazy_frame_ranges.cc",
"tizen/lazy_frame_ranges.h",
"tizen/media_video_codec.cc",
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/filters/tizen/category_selector.h"
+
+#include "base/bits.h"
+#include "media/base/tizen/logger/media_logger.h"
+#include "media/filters/tizen/resource_manager_utils.h"
+#include "media/video/h264_level_limits.h"
+
+namespace media {
+
+namespace {
+constexpr const gfx::Size kFullHd(1920, 1080);
+constexpr const gfx::Size kQHD(2560, 1440);
+constexpr const gfx::Size k4k(3840, 2160);
+
+absl::optional<uint32_t> H264MaxLumaSamples(VideoCodecLevel level) {
+ constexpr static const uint32_t kMaxMacroblockSize = 16;
+ auto max_macroblocks = H264LevelToMaxMBPS(level);
+ if (!max_macroblocks) {
+ return absl::nullopt;
+ }
+ return max_macroblocks * kMaxMacroblockSize * kMaxMacroblockSize;
+}
+
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+constexpr static const uint32_t kH265Level10 = 30;
+constexpr static const uint32_t kH265Level20 = 60;
+constexpr static const uint32_t kH265Level21 = 63;
+constexpr static const uint32_t kH265Level30 = 90;
+constexpr static const uint32_t kH265Level31 = 93;
+constexpr static const uint32_t kH265Level40 = 120;
+constexpr static const uint32_t kH265Level41 = 123;
+constexpr static const uint32_t kH265Level50 = 150;
+constexpr static const uint32_t kH265Level51 = 153;
+constexpr static const uint32_t kH265Level52 = 156;
+constexpr static const uint32_t kH265Level60 = 180;
+constexpr static const uint32_t kH265Level61 = 183;
+constexpr static const uint32_t kH265Level62 = 186;
+
+absl::optional<uint32_t> H265MaxLumaSamples(VideoCodecLevel level) {
+ switch (level) {
+ case kH265Level10:
+ return 552'960u;
+ case kH265Level20:
+ return 3'686'400u;
+ case kH265Level21:
+ return 7'372'800u;
+ case kH265Level30:
+ return 16'588'800u;
+ case kH265Level31:
+ return 33'177'600u;
+ case kH265Level40:
+ return 66'846'720u;
+ case kH265Level41:
+ return 133'693'440u;
+ case kH265Level50:
+ return 267'386'880u;
+ case kH265Level51:
+ return 534'773'760u;
+ case kH265Level52:
+ return 1'069'547'520u;
+ case kH265Level60:
+ return 1'069'547'520u;
+ case kH265Level61:
+ return 2'139'095'040u;
+ case kH265Level62:
+ return 4'278'190'080u;
+ default:
+ TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR) << "Invalid codec level: " << level;
+ return absl::nullopt;
+ }
+}
+#endif
+
+constexpr static const uint32_t kAV1Level20 = 0;
+constexpr static const uint32_t kAV1Level21 = 1;
+constexpr static const uint32_t kAV1Level22 = 2;
+constexpr static const uint32_t kAV1Level23 = 3;
+constexpr static const uint32_t kAV1Level30 = 4;
+constexpr static const uint32_t kAV1Level31 = 5;
+constexpr static const uint32_t kAV1Level32 = 6;
+constexpr static const uint32_t kAV1Level33 = 7;
+constexpr static const uint32_t kAV1Level40 = 8;
+constexpr static const uint32_t kAV1Level41 = 9;
+constexpr static const uint32_t kAV1Level42 = 10;
+constexpr static const uint32_t kAV1Level43 = 11;
+constexpr static const uint32_t kAV1Level50 = 12;
+constexpr static const uint32_t kAV1Level51 = 13;
+constexpr static const uint32_t kAV1Level52 = 14;
+constexpr static const uint32_t kAV1Level53 = 15;
+constexpr static const uint32_t kAV1Level60 = 16;
+constexpr static const uint32_t kAV1Level61 = 17;
+constexpr static const uint32_t kAV1Level62 = 18;
+constexpr static const uint32_t kAV1Level63 = 19;
+constexpr static const uint32_t kAV1Level70 = 20;
+constexpr static const uint32_t kAV1Level71 = 21;
+constexpr static const uint32_t kAV1Level72 = 22;
+constexpr static const uint32_t kAV1Level73 = 23;
+
+// AV1 Spec, Annex A, A.3. Levels
+absl::optional<uint32_t> AV1MaxDisplayRate(VideoCodecLevel level) {
+ switch (level) {
+ case kAV1Level73: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level72: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level71: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level70: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level63:
+ return 4'278'190'080u;
+ case kAV1Level62:
+ return 4'278'190'080u;
+ case kAV1Level61:
+ return 2'139'095'040u;
+ case kAV1Level60:
+ return 1'069'547'520u;
+ case kAV1Level53:
+ return 1'069'547'520u;
+ case kAV1Level52:
+ return 1'069'547'520u;
+ case kAV1Level51:
+ return 534'773'760u;
+ case kAV1Level50:
+ return 267'386'880u;
+ case kAV1Level43: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level42: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level41:
+ return 141'557'760u;
+ case kAV1Level40:
+ return 70'778'880u;
+ case kAV1Level33: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level32: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level31:
+ return 31'950'720u;
+ case kAV1Level30:
+ return 19'975'680u;
+ case kAV1Level23: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level22: // limit not specified in AV1 spec
+ [[fallthrough]];
+ case kAV1Level21:
+ return 8'363'520u;
+ case kAV1Level20:
+ return 4'423'680u;
+ default:
+ TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR) << "Invalid codec level: " << level;
+ return absl::nullopt;
+ }
+}
+
+absl::optional<float> GetMaximalFramerate(MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size) {
+ if (level == kNoVideoCodecLevel) {
+ TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING) << "No video codec level";
+ return absl::nullopt;
+ }
+
+ switch (codec) {
+ case MediaVideoCodec::kCodecH264: {
+ auto max_luma_samples = H264MaxLumaSamples(level);
+ if (!max_luma_samples) {
+ return absl::nullopt;
+ }
+ return static_cast<float>(*max_luma_samples) / coded_size.width() /
+ coded_size.height();
+ }
+#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
+ case MediaVideoCodec::kCodecHEVC: {
+ auto max_luma_samples = H265MaxLumaSamples(level);
+ if (!max_luma_samples) {
+ return absl::nullopt;
+ }
+ return static_cast<float>(*max_luma_samples) /
+ base::bits::AlignUp(coded_size.width(), 64) /
+ base::bits::AlignUp(coded_size.height(), 64);
+ }
+#endif
+ case MediaVideoCodec::kCodecAV1: {
+ auto max_display_rate = AV1MaxDisplayRate(level);
+ if (!max_display_rate) {
+ return absl::nullopt;
+ }
+ return static_cast<float>(*max_display_rate) / coded_size.width() /
+ coded_size.height();
+ }
+ default:
+ // TODO(vdwasm) Analyze need for other codec framerate calculation.
+ TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING)
+ << "Use default framerate for " << VideoCodecToCodecName(codec);
+ return absl::nullopt;
+ }
+}
+
+ri_rsc_category_e FindDecoderForResolution(
+ base::ResourceManager* manager,
+ const char* codec_name,
+ int32_t color_depth,
+ const gfx::Size& resolution,
+ uint32_t framerate,
+ ri_sampling_format sampling_format,
+ base::PreferTexturingSupport prefer_texturing_support) {
+ // Lists framerates thresholds for different resource manager decoder
+ // categories, e.g.:
+ // - RI_CATEGORY_VIDEO_DECODER_HEVC_UHD_8BIT_30P
+ // - RI_CATEGORY_VIDEO_DECODER_HEVC_UHD_8BIT_60P
+ constexpr uint32_t framerates[] = {0, 30, 60, 120};
+ auto it =
+ std::lower_bound(std::begin(framerates), std::end(framerates), framerate);
+ if (it == std::end(framerates))
+ --it;
+
+ uint32_t fps = framerate;
+ ri_rsc_category_e category_option = RI_CATEGORY_NONE;
+ for (; category_option <= RI_CATEGORY_NONE && *it > 0; --it) {
+ fps = *it;
+ category_option =
+ static_cast<ri_rsc_category_e>(manager->GetCapableVideoCategoryId(
+ codec_name, color_depth, resolution.width(), resolution.height(),
+ fps, sampling_format, prefer_texturing_support));
+ TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE)
+ << "codec: '" << codec_name << "'"
+ << " size: " << resolution.ToString() << " fps: " << fps
+ << " color_depth: " << color_depth
+ << " sampling_format: " << sampling_format
+ << " category: " << category_option;
+ }
+
+ if (category_option > RI_CATEGORY_NONE && fps != framerate) {
+ TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING)
+ << "codec: '" << codec_name << "'"
+ << " size: " << resolution.ToString() << " color_depth: " << color_depth
+ << " sampling_format: " << sampling_format
+ << " allocated category: " << category_option
+ << " using different framerate than requested,"
+ << " requested fps: " << framerate << " vs. actual fps: " << fps;
+ }
+
+ return category_option;
+}
+
+bool IsLarger(gfx::Size s1, gfx::Size s2) {
+ return s1.width() > s2.width() || s1.height() > s2.height();
+}
+
+gfx::Size RoundResolutionUp(gfx::Size coded_size) {
+ if (!IsLarger(coded_size, kFullHd)) {
+ return kFullHd;
+ } else if (!IsLarger(coded_size, kQHD)) {
+ return kQHD;
+ } else if (!IsLarger(coded_size, k4k)) {
+ return k4k;
+ } else {
+ return coded_size;
+ }
+}
+
+enum class UseHigherDecoder {
+ kNo,
+ kRecommended,
+ kRequired,
+};
+
+std::ostream& operator<<(std::ostream& stream, UseHigherDecoder value) {
+ switch (value) {
+ case UseHigherDecoder::kNo:
+ stream << "no";
+ break;
+ case UseHigherDecoder::kRecommended:
+ stream << "recommended";
+ break;
+ case UseHigherDecoder::kRequired:
+ stream << "required";
+ break;
+ }
+ return stream;
+}
+
+UseHigherDecoder ShouldUseHigherH264Decoder(
+ MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size,
+ LatencyMode latency,
+ const gpu::GpuDriverBugWorkarounds* workarounds) {
+ const bool use_h264_level50_workaround =
+ level >= 50 && !IsLarger(coded_size, kFullHd);
+ if (use_h264_level50_workaround) {
+ return UseHigherDecoder::kRequired;
+ }
+
+ if (latency == LatencyMode::kGameMode && workarounds &&
+ workarounds->ttvd_disable_faster_decoder_selection) {
+ TIZEN_MEDIA_LOG_NO_INSTANCE(INFO)
+ << "Disabling picking better decoder for game streaming";
+ return UseHigherDecoder::kNo;
+ }
+
+ return latency == LatencyMode::kGameMode ? UseHigherDecoder::kRecommended
+ : UseHigherDecoder::kNo;
+}
+
+ri_sampling_format ConvertChromaSampling(VideoChromaSampling sampling) {
+ switch (sampling) {
+ case VideoChromaSampling::k420:
+ return RI_SAMPLING_FORMAT_420;
+ case VideoChromaSampling::k422:
+ return RI_SAMPLING_FORMAT_422;
+ case VideoChromaSampling::k444:
+ case VideoChromaSampling::k400:
+ return RI_SAMPLING_FORMAT_OTHERS;
+ case VideoChromaSampling::kUnknown:
+ LOG(ERROR) << "Unknown chroma sampling";
+ return RI_SAMPLING_FORMAT_OTHERS;
+ }
+}
+} // namespace
+
+absl::optional<uint32_t> DetectMaximalFramerate(MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size) {
+ auto maybe_framerate = GetMaximalFramerate(codec, level, coded_size);
+ if (!maybe_framerate) {
+ return absl::nullopt;
+ }
+ const auto framerate = *maybe_framerate;
+ TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE) << "Calculated framerate: " << framerate;
+
+ if (framerate <= 35.0f) {
+ return 30;
+ } else if (framerate <= 75.0f) {
+ return 60;
+ } else {
+ return 120;
+ }
+}
+
+ri_rsc_category_e SelectVideoDecoderCategory(
+ base::ResourceManager* resource_manager,
+ MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size,
+ VideoChromaSampling sampling,
+ size_t bit_depth,
+ LatencyMode latency,
+ const gpu::GpuDriverBugWorkarounds* workarounds) {
+ const char* codec_name = VideoCodecToCodecName(codec);
+ if (!codec_name) {
+ TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR)
+ << "Unsupported codec: " << static_cast<uint32_t>(codec);
+ return RI_CATEGORY_NONE;
+ }
+
+ // MJPEG has different path in Resource Manager.
+ if (codec == MediaVideoCodec::kCodecMJPEG) {
+ return static_cast<ri_rsc_category_e>(
+ resource_manager->GetJpegCategoryId(codec_name, coded_size.width()));
+ }
+
+ uint32_t framerate =
+ DetectMaximalFramerate(codec, level, RoundResolutionUp(coded_size))
+ .value_or(kDefaultFramerate);
+
+ // Ugly workaround for not supported profile by one of H264 decoder.
+ TIZEN_MEDIA_LOG_NO_INSTANCE(INFO)
+ << "Codec: '" << codec_name << "'"
+ << ", level: " << level << ", size: " << coded_size.ToString();
+ const auto use_h264_level50_workaround = ShouldUseHigherH264Decoder(
+ codec, level, coded_size, latency, workarounds);
+
+ const bool has_uhd_decoder = resource_manager->HasUHDVideoDecoder();
+ // Check if lowest possible configuration of HEVC is supported.
+ const bool has_hevc_decoder = resource_manager->IsCategorySupported(
+ RI_CATEGORY_VIDEO_DECODER_HEVC_FHD_8BIT_30P);
+ TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE)
+ << "use_h264_level50_workaround: " << use_h264_level50_workaround
+ << " has_uhd_decoder: " << has_uhd_decoder
+ << " has_hevc_decoder: " << has_hevc_decoder;
+
+ // When manual rendering is prefered, we should allocate decoder that
+ // supports texturing to be able to utilize software rendering mode.
+ const auto prefer_texturing_support =
+ workarounds && workarounds->ttvd_prefer_manual_rendering_for_low_latency
+ ? base::PreferTexturingSupport::kYes
+ : base::PreferTexturingSupport::kNo;
+
+ if (codec == MediaVideoCodec::kCodecH264 &&
+ use_h264_level50_workaround != UseHigherDecoder::kNo &&
+ (has_uhd_decoder || has_hevc_decoder)) {
+ const char* workaround_codec_name = codec_name;
+ gfx::Size workaround_coded_size = coded_size;
+ uint32_t workaround_framerate = framerate;
+
+ // Doing any of this might affect total bandwidth of resources, but there
+ // is no other way to do this. To ensure content is decoded without errors
+ // or latency is best affordable, we need to request higher category
+ // somehow.
+ if (has_uhd_decoder) {
+ // Request decoder for 4K resolution and calculate required framerate
+ // according to new size.
+ workaround_coded_size = k4k;
+ workaround_framerate =
+ DetectMaximalFramerate(codec, level, RoundResolutionUp(k4k))
+ .value_or(kDefaultFramerate);
+ } else if (has_hevc_decoder) {
+ // Keep the same resolution and framerate, just trick with codec,
+ // so DVDE is used.
+ workaround_codec_name =
+ VideoCodecToCodecName(MediaVideoCodec::kCodecHEVC);
+ }
+
+ auto result = FindDecoderForResolution(
+ resource_manager, workaround_codec_name, bit_depth,
+ workaround_coded_size, workaround_framerate,
+ ConvertChromaSampling(sampling), prefer_texturing_support);
+ if (result > 0) {
+ return result;
+ }
+ }
+
+ // Level 5.0 contents are not decodable using standard FullHD decoders, so
+ // do not allocate any resource in such case. Although in some cases it
+ // might work, in general it might lead to some weird errors or crashes.
+ // Note that for game streaming, we'll continue with original parameters
+ // allocation.
+ if (use_h264_level50_workaround == UseHigherDecoder::kRequired) {
+ return RI_CATEGORY_NONE;
+ }
+
+ // Try to allocate decoder using requested parameters, because either:
+ // - this codec doesn't need workaround above,
+ // - we can't apply workaround e.g. board doesn't have DVDE decoder,
+ // - we failed to allocate better decoder (DVDE), so as a fallback
+ // try to use MFC.
+ return FindDecoderForResolution(
+ resource_manager, codec_name, bit_depth, coded_size, framerate,
+ ConvertChromaSampling(sampling), prefer_texturing_support);
+}
+
+} // namespace media
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_FILTERS_TIZEN_CATEGORY_SELECTOR_H_
+#define MEDIA_FILTERS_TIZEN_CATEGORY_SELECTOR_H_
+
+#include <ri-api.h>
+
+#include "absl/types/optional.h"
+#include "base/tizen/resource_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_types.h"
+#include "media/filters/tizen/latency_mode.h"
+#include "media/filters/tizen/media_video_codec.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+constexpr static const uint32_t kDefaultFramerate = 30;
+
+// Calculates maximal possible framerate of video in specified parameters.
+// Returns |absl::nullopt| in case it cannot be calculated (e.g codec does not
+// specify this).
+absl::optional<uint32_t> DetectMaximalFramerate(MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size);
+
+// Tries to select appropriate video decoder category to be used for hardware
+// accelerated decoding.
+ri_rsc_category_e SelectVideoDecoderCategory(
+ base::ResourceManager* resource_manager,
+ MediaVideoCodec codec,
+ VideoCodecLevel level,
+ gfx::Size coded_size,
+ VideoChromaSampling sampling,
+ size_t bit_depth,
+ LatencyMode latency,
+ const gpu::GpuDriverBugWorkarounds* workarounds);
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_TIZEN_CATEGORY_SELECTOR_H_
#include "absl/types/optional.h"
#include "base/auto_reset.h"
-#include "base/bits.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "media/base/media_switches.h"
#include "media/base/tizen/logger/media_logger.h"
#include "media/base/video_codecs.h"
-#include "media/filters/tizen/media_video_codec.h"
-#include "media/filters/tizen/resource_manager_utils.h"
-#include "media/video/h264_level_limits.h"
+#include "media/filters/tizen/category_selector.h"
#include "tizen_src/chromium_impl/build/tizen_version.h"
namespace media {
namespace {
-constexpr static const uint32_t kDefaultFramerate = 30;
constexpr static const uint32_t kMaxNDecodingFramerate = 30;
-constexpr const gfx::Size kFullHd(1920, 1080);
-constexpr const gfx::Size kQHD(2560, 1440);
-constexpr const gfx::Size k4k(3840, 2160);
-
-absl::optional<uint32_t> H264MaxLumaSamples(VideoCodecLevel level) {
- constexpr static const uint32_t kMaxMacroblockSize = 16;
- auto max_macroblocks = H264LevelToMaxMBPS(level);
- if (!max_macroblocks) {
- return absl::nullopt;
- }
- return max_macroblocks * kMaxMacroblockSize * kMaxMacroblockSize;
-}
-
-#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
-constexpr static const uint32_t kH265Level10 = 30;
-constexpr static const uint32_t kH265Level20 = 60;
-constexpr static const uint32_t kH265Level21 = 63;
-constexpr static const uint32_t kH265Level30 = 90;
-constexpr static const uint32_t kH265Level31 = 93;
-constexpr static const uint32_t kH265Level40 = 120;
-constexpr static const uint32_t kH265Level41 = 123;
-constexpr static const uint32_t kH265Level50 = 150;
-constexpr static const uint32_t kH265Level51 = 153;
-constexpr static const uint32_t kH265Level52 = 156;
-constexpr static const uint32_t kH265Level60 = 180;
-constexpr static const uint32_t kH265Level61 = 183;
-constexpr static const uint32_t kH265Level62 = 186;
-
-absl::optional<uint32_t> H265MaxLumaSamples(VideoCodecLevel level) {
- switch (level) {
- case kH265Level10:
- return 552'960u;
- case kH265Level20:
- return 3'686'400u;
- case kH265Level21:
- return 7'372'800u;
- case kH265Level30:
- return 16'588'800u;
- case kH265Level31:
- return 33'177'600u;
- case kH265Level40:
- return 66'846'720u;
- case kH265Level41:
- return 133'693'440u;
- case kH265Level50:
- return 267'386'880u;
- case kH265Level51:
- return 534'773'760u;
- case kH265Level52:
- return 1'069'547'520u;
- case kH265Level60:
- return 1'069'547'520u;
- case kH265Level61:
- return 2'139'095'040u;
- case kH265Level62:
- return 4'278'190'080u;
- default:
- TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR) << "Invalid codec level: " << level;
- return absl::nullopt;
- }
-}
-#endif
-
-constexpr static const uint32_t kAV1Level20 = 0;
-constexpr static const uint32_t kAV1Level21 = 1;
-constexpr static const uint32_t kAV1Level22 = 2;
-constexpr static const uint32_t kAV1Level23 = 3;
-constexpr static const uint32_t kAV1Level30 = 4;
-constexpr static const uint32_t kAV1Level31 = 5;
-constexpr static const uint32_t kAV1Level32 = 6;
-constexpr static const uint32_t kAV1Level33 = 7;
-constexpr static const uint32_t kAV1Level40 = 8;
-constexpr static const uint32_t kAV1Level41 = 9;
-constexpr static const uint32_t kAV1Level42 = 10;
-constexpr static const uint32_t kAV1Level43 = 11;
-constexpr static const uint32_t kAV1Level50 = 12;
-constexpr static const uint32_t kAV1Level51 = 13;
-constexpr static const uint32_t kAV1Level52 = 14;
-constexpr static const uint32_t kAV1Level53 = 15;
-constexpr static const uint32_t kAV1Level60 = 16;
-constexpr static const uint32_t kAV1Level61 = 17;
-constexpr static const uint32_t kAV1Level62 = 18;
-constexpr static const uint32_t kAV1Level63 = 19;
-constexpr static const uint32_t kAV1Level70 = 20;
-constexpr static const uint32_t kAV1Level71 = 21;
-constexpr static const uint32_t kAV1Level72 = 22;
-constexpr static const uint32_t kAV1Level73 = 23;
-
-// AV1 Spec, Annex A, A.3. Levels
-absl::optional<uint32_t> AV1MaxDisplayRate(VideoCodecLevel level) {
- switch (level) {
- case kAV1Level73: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level72: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level71: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level70: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level63:
- return 4'278'190'080u;
- case kAV1Level62:
- return 4'278'190'080u;
- case kAV1Level61:
- return 2'139'095'040u;
- case kAV1Level60:
- return 1'069'547'520u;
- case kAV1Level53:
- return 1'069'547'520u;
- case kAV1Level52:
- return 1'069'547'520u;
- case kAV1Level51:
- return 534'773'760u;
- case kAV1Level50:
- return 267'386'880u;
- case kAV1Level43: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level42: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level41:
- return 141'557'760u;
- case kAV1Level40:
- return 70'778'880u;
- case kAV1Level33: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level32: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level31:
- return 31'950'720u;
- case kAV1Level30:
- return 19'975'680u;
- case kAV1Level23: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level22: // limit not specified in AV1 spec
- [[fallthrough]];
- case kAV1Level21:
- return 8'363'520u;
- case kAV1Level20:
- return 4'423'680u;
- default:
- TIZEN_MEDIA_LOG_NO_INSTANCE(ERROR) << "Invalid codec level: " << level;
- return absl::nullopt;
- }
-}
-
-absl::optional<float> GetMaximalFramerate(MediaVideoCodec codec,
- VideoCodecLevel level,
- gfx::Size coded_size) {
- if (level == kNoVideoCodecLevel) {
- TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING) << "No video codec level";
- return absl::nullopt;
- }
-
- switch (codec) {
- case MediaVideoCodec::kCodecH264: {
- auto max_luma_samples = H264MaxLumaSamples(level);
- if (!max_luma_samples) {
- return absl::nullopt;
- }
- return static_cast<float>(*max_luma_samples) / coded_size.width() /
- coded_size.height();
- }
-#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- case MediaVideoCodec::kCodecHEVC: {
- auto max_luma_samples = H265MaxLumaSamples(level);
- if (!max_luma_samples) {
- return absl::nullopt;
- }
- return static_cast<float>(*max_luma_samples) /
- base::bits::AlignUp(coded_size.width(), 64) /
- base::bits::AlignUp(coded_size.height(), 64);
- }
-#endif
- case MediaVideoCodec::kCodecAV1: {
- auto max_display_rate = AV1MaxDisplayRate(level);
- if (!max_display_rate) {
- return absl::nullopt;
- }
- return static_cast<float>(*max_display_rate) / coded_size.width() /
- coded_size.height();
- }
- default:
- // TODO(vdwasm) Analyze need for other codec framerate calculation.
- TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING)
- << "Use default framerate for " << VideoCodecToCodecName(codec);
- return absl::nullopt;
- }
-}
-
-absl::optional<uint32_t> DetectMaximalFramerate(MediaVideoCodec codec,
- VideoCodecLevel level,
- gfx::Size coded_size) {
- auto maybe_framerate = GetMaximalFramerate(codec, level, coded_size);
- if (!maybe_framerate) {
- return absl::nullopt;
- }
- const auto framerate = *maybe_framerate;
- TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE) << "Calculated framerate: " << framerate;
-
- if (framerate <= 35.0f) {
- return 30;
- } else if (framerate <= 75.0f) {
- return 60;
- } else {
- return 120;
- }
-}
-
-ri_rsc_category_e FindDecoderForResolution(
- base::ResourceManager* manager,
- const char* codec_name,
- int32_t color_depth,
- const gfx::Size& resolution,
- uint32_t framerate,
- ri_sampling_format sampling_format,
- base::PreferTexturingSupport prefer_texturing_support) {
- // Lists framerates thresholds for different resource manager decoder
- // categories, e.g.:
- // - RM_CATEGORY_VIDEO_DECODER_HEVC_UHD_8BIT_30P
- // - RM_CATEGORY_VIDEO_DECODER_HEVC_UHD_8BIT_60P
- constexpr uint32_t framerates[] = {0, 30, 60, 120};
- auto it =
- std::lower_bound(std::begin(framerates), std::end(framerates), framerate);
- if (it == std::end(framerates))
- --it;
-
- uint32_t fps = framerate;
- ri_rsc_category_e category_option = RI_CATEGORY_NONE;
- for (; category_option <= RI_CATEGORY_NONE && *it > 0; --it) {
- fps = *it;
- category_option =
- static_cast<ri_rsc_category_e>(manager->GetCapableVideoCategoryId(
- codec_name, color_depth, resolution.width(), resolution.height(),
- fps, sampling_format, prefer_texturing_support));
- TIZEN_MEDIA_LOG_NO_INSTANCE(VERBOSE)
- << "codec: '" << codec_name << "'"
- << " size: " << resolution.ToString() << " fps: " << fps
- << " color_depth: " << color_depth
- << " sampling_format: " << sampling_format
- << " category: " << category_option;
- }
-
- if (category_option > RI_CATEGORY_NONE && fps != framerate) {
- TIZEN_MEDIA_LOG_NO_INSTANCE(WARNING)
- << "codec: '" << codec_name << "'"
- << " size: " << resolution.ToString() << " color_depth: " << color_depth
- << " sampling_format: " << sampling_format
- << " allocated category: " << category_option
- << " using different framerate than requested,"
- << " requested fps: " << framerate << " vs. actual fps: " << fps;
- }
-
- return category_option;
-}
-
bool CategoryIsNDecoding(int category) {
if (category == RI_CATEGORY_VIDEO_DECODER_H264_HD_8BIT_30P) {
return true;
return false;
}
-int MainPriority(DecoderPromotion::LatencyMode latency) {
- if (latency == DecoderPromotion::LatencyMode::kGameMode) {
+int MainPriority(LatencyMode latency) {
+ if (latency == LatencyMode::kGameMode) {
// We want WebRTC stream to have absolute priority before everything
// else when game mode is set.
return 3;
- } else if (latency == DecoderPromotion::LatencyMode::kNormal) {
+ } else if (latency == LatencyMode::kNormal) {
// For other purposes, prefer non-RTC players as they are less configurable
// in terms of resolution and framerate.
return 2;
}
}
-int CalculatePriority(gfx::Size coded_size,
- DecoderPromotion::LatencyMode latency) {
+int CalculatePriority(gfx::Size coded_size, LatencyMode latency) {
int main_priority = MainPriority(latency);
int sub_priority = coded_size.GetArea();
(sub_priority & 0b1111'1111'1111'1111'1111'1111'1111);
}
-ri_sampling_format ConvertChromaSampling(VideoChromaSampling sampling) {
- switch (sampling) {
- case VideoChromaSampling::k420:
- return RI_SAMPLING_FORMAT_420;
- case VideoChromaSampling::k422:
- return RI_SAMPLING_FORMAT_422;
- case VideoChromaSampling::k444:
- case VideoChromaSampling::k400:
- return RI_SAMPLING_FORMAT_OTHERS;
- case VideoChromaSampling::kUnknown:
- LOG(ERROR) << "Unknown chroma sampling";
- return RI_SAMPLING_FORMAT_OTHERS;
- }
+struct NDecodingDef {
+ gfx::Size max_resolution;
+ MediaVideoCodec codec;
+ int category;
+ size_t max_decoders;
+};
+
+constexpr static const std::array<NDecodingDef, 3> kNDecodings{{
+ {gfx::Size(1280, 720), MediaVideoCodec::kCodecH264,
+ RM_CATEGORY_VIDEO_DECODER_H264_HD_8BIT_30P, 4},
+ {gfx::Size(1920, 1080), MediaVideoCodec::kCodecH264,
+ RM_CATEGORY_VIDEO_DECODER_H264_FHD_N_8BIT_30P, 2},
+ {gfx::Size(1024, 576), MediaVideoCodec::kCodecVP8,
+ RM_CATEGORY_VIDEO_DECODER_VP8_qHD_8BIT_30P, 4},
+}};
+
+bool IsNDecodingEnabled() {
+ static const bool is_enabled =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableNDecoding);
+ return is_enabled;
}
} // namespace
return new_decoder;
}
-namespace {
-struct NDecodingDef {
- gfx::Size max_resolution;
- MediaVideoCodec codec;
- int category;
- size_t max_decoders;
-};
-constexpr static const std::array<NDecodingDef, 3> kNDecodings{{
- {gfx::Size(1280, 720), MediaVideoCodec::kCodecH264,
- RM_CATEGORY_VIDEO_DECODER_H264_HD_8BIT_30P, 4},
- {gfx::Size(1920, 1080), MediaVideoCodec::kCodecH264,
- RM_CATEGORY_VIDEO_DECODER_H264_FHD_N_8BIT_30P, 2},
- {gfx::Size(1024, 576), MediaVideoCodec::kCodecVP8,
- RM_CATEGORY_VIDEO_DECODER_VP8_qHD_8BIT_30P, 4},
-}};
-} // namespace
-
absl::optional<AllocatedDecoder> DecoderPromotion::ExtendNDecoding(
MediaVideoCodec codec,
VideoCodecLevel level,
return absl::nullopt;
}
-namespace {
-bool IsLarger(gfx::Size s1, gfx::Size s2) {
- return s1.width() > s2.width() || s1.height() > s2.height();
-}
-
-gfx::Size RoundResolutionUp(gfx::Size coded_size) {
- if (!IsLarger(coded_size, kFullHd)) {
- return kFullHd;
- } else if (!IsLarger(coded_size, kQHD)) {
- return kQHD;
- } else if (!IsLarger(coded_size, k4k)) {
- return k4k;
- } else {
- return coded_size;
- }
-}
-
-bool IsNDecodingEnabled() {
- static const bool is_enabled =
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableNDecoding);
- return is_enabled;
-}
-} // namespace
-
absl::optional<AllocatedDecoder> DecoderPromotion::SelectDecoder(
MediaVideoCodec codec,
VideoCodecLevel level,
DumpAllocationStateLocked();
- uint32_t framerate =
- DetectMaximalFramerate(codec, level, RoundResolutionUp(coded_size))
- .value_or(kDefaultFramerate);
-
- const char* codec_name = VideoCodecToCodecName(codec);
- if (!codec_name) {
- TIZEN_MEDIA_LOG(ERROR) << "Unsupported codec: "
- << static_cast<uint32_t>(codec);
- return absl::nullopt;
- }
-
- // MJPEG has different path in Resource Manager.
- ri_rsc_category_e category_option = RI_CATEGORY_NONE;
- if (codec == MediaVideoCodec::kCodecMJPEG) {
- category_option =
- static_cast<ri_rsc_category_e>(GetResourceManager()->GetJpegCategoryId(
- codec_name, coded_size.width()));
- } else {
- // Ugly workaround for not supported profile by one of H264 decoder.
- TIZEN_MEDIA_LOG(INFO) << "Codec: '" << codec_name << "'"
- << ", level: " << level
- << ", size: " << coded_size.ToString();
- const bool use_h264_level50_workaround =
- level >= 50 && !IsLarger(coded_size, kFullHd);
- const bool has_udh_decoder = GetResourceManager()->HasUDHVideoDecoder();
- // Check if lowest possible configuration of HEVC is supported.
- const bool has_hevc_decoder = GetResourceManager()->IsCategorySupported(
- RM_CATEGORY_VIDEO_DECODER_HEVC_FHD_8BIT_30P);
- TIZEN_MEDIA_LOG(VERBOSE)
- << "use_h264_level50_workaround: " << use_h264_level50_workaround
- << " has_udh_decoder: " << has_udh_decoder
- << " has_hevc_decoder: " << has_hevc_decoder;
-
- bool should_use_faster_decoder_for_game_streaming =
- latency == LatencyMode::kGameMode;
-
- if (should_use_faster_decoder_for_game_streaming && workarounds &&
- workarounds->ttvd_disable_faster_decoder_selection) {
- should_use_faster_decoder_for_game_streaming = false;
- TIZEN_MEDIA_LOG(INFO)
- << "Disabling picking better decoder for game streaming";
- }
-
- // When manual rendering is prefered, we should allocate decoder that
- // supports texturing to be able to utilize software rendering mode.
- const auto prefer_texturing_support =
- workarounds && workarounds->ttvd_prefer_manual_rendering_for_low_latency
- ? base::PreferTexturingSupport::kYes
- : base::PreferTexturingSupport::kNo;
-
- if (codec == MediaVideoCodec::kCodecH264 &&
- (use_h264_level50_workaround ||
- should_use_faster_decoder_for_game_streaming) &&
- (has_udh_decoder || has_hevc_decoder)) {
- const char* workaround_codec_name = codec_name;
- gfx::Size workaround_coded_size = coded_size;
- uint32_t workaround_framerate = framerate;
-
- // Doing any of this might affect total bandwidth of resources, but there
- // is no other way to do this. To ensure content is decoded without errors
- // or latency is best affordable, we need to request higher category
- // somehow.
- if (has_udh_decoder) {
- // Request decoder for 4K resolution and calculate required framerate
- // according to new size.
- workaround_coded_size = k4k;
- workaround_framerate =
- DetectMaximalFramerate(codec, level, RoundResolutionUp(k4k))
- .value_or(kDefaultFramerate);
- } else if (has_hevc_decoder) {
- // Keep the same resolution and framerate, just trick with codec,
- // so DVDE is used.
- workaround_codec_name =
- VideoCodecToCodecName(MediaVideoCodec::kCodecHEVC);
- }
-
- category_option = FindDecoderForResolution(
- GetResourceManager(), workaround_codec_name, bit_depth,
- workaround_coded_size, workaround_framerate,
- ConvertChromaSampling(sampling), prefer_texturing_support);
- }
-
- // Level 5.0 contents are not decodable using standard FullHD decoders, so
- // do not allocate any resource in such case. Although in some cases it
- // might work, in general it might lead to some weird errors or crashes.
- // Note that for game streaming, we'll continue with original parameters
- // allocation.
- if (category_option <= 0 && use_h264_level50_workaround) {
- return absl::nullopt;
- }
-
- if (category_option <= 0) {
- // Try to allocate decoder using requested parameters, because either:
- // - this codec doesn't need workaround above,
- // - we can't apply workaround e.g. board doesn't have DVDE decoder,
- // - we failed to allocate better decoder (DVDE), so as a fallback
- // try to use MFC.
- category_option = FindDecoderForResolution(
- GetResourceManager(), codec_name, bit_depth, coded_size, framerate,
- ConvertChromaSampling(sampling), prefer_texturing_support);
- }
- }
-
+ ri_rsc_category_e category_option =
+ SelectVideoDecoderCategory(GetResourceManager(), codec, level, coded_size,
+ sampling, bit_depth, latency, workarounds);
if (category_option <= 0) {
TIZEN_MEDIA_LOG(ERROR) << "Failed on ri_get_capable_video_category_id: "
<< category_option;
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "media/base/video_types.h"
#include "media/filters/tizen/decoding_mode.h"
+#include "media/filters/tizen/latency_mode.h"
#include "media/filters/tizen/media_video_codec.h"
#include "ui/gfx/geometry/size.h"
class DecoderPromotion {
public:
- enum class LatencyMode {
- kNormal,
- kLow,
- kGameMode,
- };
-
// Returns the DecoderPromotion singleton.
static DecoderPromotion* GetInstance() {
return base::Singleton<DecoderPromotion>::get();
bool CanAllocate(int, int) override { return true; }
bool SetPriority(int) override { return true; }
bool IsCategorySupported(int) override { return true; }
- bool HasUDHVideoDecoder() override { return true; }
+ bool HasUHDVideoDecoder() override { return true; }
void ReleaseDevice() {
auto token_cb = release_cb_.Run();
base::IgnoreResult(std::move(token_cb).Run());
};
TEST(DecoderPromotionTest, ReleaseWhileHandlingConflict) {
- using LatencyMode = media::DecoderPromotion::LatencyMode;
FakeResourceManager fake_resource_manager;
DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
decoder_promotion->SetResourceManagerForTesting(&fake_resource_manager);
}
TEST(DecoderPromotionTest, HandleConflictAfterReleasing) {
- using LatencyMode = media::DecoderPromotion::LatencyMode;
FakeResourceManager fake_resource_manager;
DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
decoder_promotion->SetResourceManagerForTesting(&fake_resource_manager);
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_FILTERS_TIZEN_LATENCY_MODE_H_
+#define MEDIA_FILTERS_TIZEN_LATENCY_MODE_H_
+
+namespace media {
+
+enum class LatencyMode {
+ kNormal,
+ kLow,
+ kGameMode,
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_TIZEN_LATENCY_MODE_H_
[](AllocatedDecoder decoder) { LOG(ERROR) << "To be implemented!"; });
auto decoder = DecoderPromotion::GetInstance()->SelectDecoder(
- codec, level, coded_size, sampling, bit_depth,
- DecoderPromotion::LatencyMode::kNormal, std::move(callbacks));
+ codec, level, coded_size, sampling, bit_depth, LatencyMode::kNormal,
+ std::move(callbacks));
if (!decoder) {
LOG(ERROR) << "No decoder allocated";
std::move(init_cb).Run(DecoderStatus::Codes::kFailedToCreateDecoder);
// return that valid resolutions (e.g 3840x2176) are not supported on
// several boards.
auto latency = [&]() {
- using LatencyMode = DecoderPromotion::LatencyMode;
if (IsForGameStreaming())
return LatencyMode::kGameMode;
if (config_.is_rtc())
return category;
}
-bool ResourceManager::HasUDHVideoDecoder() {
+bool ResourceManager::HasUHDVideoDecoder() {
// Our TVs doesn't support decoder hot-swap, so it is safe to cache this
// value here, to avoid repetitive allocations of resource list.
static const bool has_uhd_decoder =
// Checks if on the TV board there is physically present decoder capable
// of decoding UHD (4K) content (aka DVDE).
- virtual bool HasUDHVideoDecoder();
+ virtual bool HasUHDVideoDecoder();
enum Decoder {
kMinValue = 0,