[TTVD] Skip decoder reinitialization when lowering video configuration 86/321086/2
authorJakub Gajownik <j.gajownik2@samsung.com>
Mon, 4 Nov 2024 12:09:24 +0000 (13:09 +0100)
committerInsoon Kim <is46.kim@samsung.com>
Wed, 27 Nov 2024 09:49:36 +0000 (09:49 +0000)
Before this change, when there was configuration change
on video decoder it always checked whether same category
would be used on clean initialization. This is problematic
as video configuration might degrade for short period and
current decoder might still be able to decode it.

After this change, we're checking if currently allocated
decoder might still decode stream with the new configuration
and reselection is done if it's not fulfilled.

Bug: https://jira-eu.sec.samsung.net/browse/VDGAME-615
Change-Id: I915458d3316bf84c161589d994b9c48959491158
Signed-off-by: Jakub Gajownik <j.gajownik2@samsung.com>
media/filters/tizen/category_selector.cc
media/filters/tizen/category_selector.h
media/filters/tizen/decoder_promotion.cc
media/filters/tizen/decoder_promotion_test.cc

index d5f2e96af80f72a5988c62c626f76f7b22a7b4e9..6bd7dd30724206e7a93dd9ec21fb7748b627a03e 100644 (file)
@@ -275,10 +275,6 @@ ri_rsc_category_e FindDecoderForResolution(
   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;
@@ -355,6 +351,10 @@ absl::optional<ri_sampling_format> ConvertChromaSampling(
 }
 }  // namespace
 
+bool IsLarger(gfx::Size s1, gfx::Size s2) {
+  return s1.width() > s2.width() || s1.height() > s2.height();
+}
+
 absl::optional<uint32_t> DetectMaximalFramerate(MediaVideoCodec codec,
                                                 VideoCodecLevel level,
                                                 gfx::Size coded_size) {
@@ -399,9 +399,9 @@ absl::optional<CategorySelectionResult> SelectVideoDecoderCategory(
                                    .bit_depth = options.bit_depth};
   }
 
-  uint32_t framerate =
-      DetectMaximalFramerate(options.codec, options.level,
-                             RoundResolutionUp(options.coded_size))
+  const gfx::Size resolution_group = RoundResolutionUp(options.coded_size);
+  const uint32_t framerate =
+      DetectMaximalFramerate(options.codec, options.level, resolution_group)
           .value_or(kDefaultFramerate);
 
   // Ugly workaround for not supported profile by one of H264 decoder.
@@ -490,7 +490,7 @@ absl::optional<CategorySelectionResult> SelectVideoDecoderCategory(
     return absl::nullopt;
   }
   return CategorySelectionResult{.category = result,
-                                 .coded_size = options.coded_size,
+                                 .coded_size = resolution_group,
                                  .framerate = framerate,
                                  .sampling = options.sampling,
                                  .bit_depth = options.bit_depth};
index e5281cc5e4f4f261b76b3feada31b66eaedf2146..f24224161e8549907404618f341a214949000b44 100644 (file)
@@ -19,6 +19,8 @@ namespace media {
 
 constexpr static const uint32_t kDefaultFramerate = 30;
 
+bool IsLarger(gfx::Size s1, gfx::Size s2);
+
 // 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).
index 904871d29c03b793f92b39254d4228d42c23c434..d0472dfe771c383df62a018d61a6d54bfebd17aa 100644 (file)
@@ -523,8 +523,37 @@ bool DecoderPromotion::NeedsDecoderReselection(
   if (!maybe_selection_result) {
     return true;
   }
-  return maybe_selection_result->category !=
-         decoder_it->second.allocation.category;
+
+  const auto& selection_result = *maybe_selection_result;
+
+  const auto throughput =
+      selection_result.coded_size.GetArea() * selection_result.framerate;
+  const auto old_throughput =
+      decoder_it->second.allocation.coded_size.GetArea() *
+      decoder_it->second.allocation.framerate;
+
+  if (!IsLarger(selection_result.coded_size,
+                decoder_it->second.allocation.coded_size) &&
+      throughput <= old_throughput &&
+      selection_result.bit_depth <= decoder_it->second.allocation.bit_depth) {
+    return false;
+  }
+
+  TIZEN_MEDIA_LOG(INFO) << "Different options, old ("
+                        << "resolution: "
+                        << decoder_it->second.allocation.coded_size.ToString()
+                        << ", framerate: "
+                        << decoder_it->second.allocation.framerate
+                        << ", bit depth: "
+                        << decoder_it->second.allocation.bit_depth
+                        << ") vs new ("
+                        << "resolution: "
+                        << selection_result.coded_size.ToString()
+                        << ", framerate: " << selection_result.framerate
+                        << ", bit depth: "
+                        << selection_result.bit_depth
+                        << "), needs reselection";
+  return true;
 }
 
 absl::optional<AllocatedDecoder> DecoderPromotion::SelectAudioDecoder(
index a437152a53d50699dd5bbf19a1bd46eb79890a3b..52ce5a028452b20a5a87403894cab1e7d97f465c 100644 (file)
@@ -9,12 +9,36 @@
 #include "base/tizen/provider_callbacks_helper.h"
 #include "base/tizen/resource_manager.h"
 #include "media/filters/tizen/media_video_codec.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace switches {
 extern const char kTizenAppId[] = "widget-id";
 }  // namespace switches
 
+namespace base {
+class MockResourceManager : public ResourceManager {
+ public:
+  MockResourceManager() = default;
+  ~MockResourceManager() override = default;
+
+  MOCK_METHOD(absl::optional<AllocatedResource>,
+              AllocateResource,
+              (int, int, ReleaseCB));
+
+  MOCK_METHOD(bool, CanAllocate, (int, int));
+  MOCK_METHOD(bool, SetPriority, (int));
+  MOCK_METHOD(bool, IsCategorySupported, (int));
+
+  MOCK_METHOD(int, GetJpegCategoryId, (const char*, int));
+  MOCK_METHOD(int,
+              GetCapableVideoCategoryId,
+              (const char*, int, int, int, int, int, PreferTexturingSupport));
+
+  MOCK_METHOD(bool, HasUHDVideoDecoder, ());
+};
+}  // namespace base
+
 namespace media {
 
 class FakeResourceManager : public base::ResourceManager {
@@ -176,4 +200,225 @@ TEST(DecoderPromotionTest, HandleConflictAfterReleasing) {
   side_thread.FlushForTesting();
 }
 
+using ResolutionList = std::vector<gfx::Size>;
+ResolutionList kDecreasingResolutionData[] = {
+    {{3840, 2160},
+     {1920, 1080},
+     {1280, 720},
+     {960, 540},
+     {640, 360},
+     {320, 180}},
+    {{1920, 1080}, {1280, 720}, {960, 540}, {640, 360}, {320, 180}},
+    {{1280, 720}, {960, 540}, {640, 360}, {320, 180}},
+    {{960, 540}, {640, 360}, {320, 180}},
+    {{640, 360}, {320, 180}}};
+
+class DecoderPromotionTestReuseDecoder
+    : public testing::TestWithParam<ResolutionList> {};
+
+TEST_P(DecoderPromotionTestReuseDecoder,
+       H264DecoderMightBeReusedWithLowerResolution) {
+  base::MockResourceManager resource_manager;
+  DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
+  decoder_promotion->SetResourceManagerForTesting(&resource_manager);
+
+  // Do not need to assign callback implementations, as they won't be called.
+  AllocationCallbacks callbacks;
+
+  constexpr const VideoCodecLevel kDefaultLevel = 40;
+  constexpr const size_t kDefaultBitDepth = 8;
+  constexpr const auto kDefaultSampling = VideoChromaSampling::k420;
+  constexpr const auto kDefaultLatency = LatencyMode::kNormal;
+  constexpr const auto kDefaultTexturing = base::PreferTexturingSupport::kNo;
+
+  EXPECT_CALL(resource_manager, SetPriority(testing::_))
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, HasUHDVideoDecoder())
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, IsCategorySupported(testing::_))
+      .WillRepeatedly(testing::Return(true));
+
+  int category = 1;
+  EXPECT_CALL(
+      resource_manager,
+      GetCapableVideoCategoryId(testing::_, testing::_, testing::_, testing::_,
+                                testing::_, testing::_, testing::_))
+      .WillRepeatedly([&]() { return category++; });
+  EXPECT_CALL(resource_manager,
+              AllocateResource(testing::_, testing::_, testing::_))
+      .WillOnce([](int, int, base::ResourceManager::ReleaseCB)
+                    -> absl::optional<base::AllocatedResource> {
+        return base::AllocatedResource(base::AllocatedResource::Token{},
+                                       "testing", 1);
+      });
+
+  const auto resolution_list = GetParam();
+
+  absl::optional<AllocatedDecoder> decoder = decoder_promotion->SelectDecoder(
+      {MediaVideoCodec::kCodecH264, kDefaultLevel, resolution_list[0],
+       kDefaultSampling, kDefaultBitDepth, kDefaultLatency, kDefaultTexturing,
+       nullptr},
+      std::move(callbacks));
+  ASSERT_TRUE(decoder.has_value());
+
+  for (size_t i = 1; i < resolution_list.size(); ++i) {
+    EXPECT_FALSE(decoder_promotion->NeedsDecoderReselection(
+        *decoder, {MediaVideoCodec::kCodecH264, kDefaultLevel,
+                   resolution_list[i], kDefaultSampling, kDefaultBitDepth,
+                   kDefaultLatency, kDefaultTexturing, nullptr}));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(H264DecoderMightBeReusedWithLowerResolution,
+                         DecoderPromotionTestReuseDecoder,
+                         testing::ValuesIn(kDecreasingResolutionData));
+
+TEST(DecoderPromotionTestReuseDecoder, H264DecoderMightBeReusedWithLowerLevel) {
+  base::MockResourceManager resource_manager;
+  DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
+  decoder_promotion->SetResourceManagerForTesting(&resource_manager);
+
+  // Do not need to assign callback implementations, as they won't be called.
+  AllocationCallbacks callbacks;
+
+  constexpr const gfx::Size kDefaultCodedSize(1920, 1080);
+  constexpr const MediaVideoCodec kDefaultCodec = MediaVideoCodec::kCodecH264;
+  constexpr const size_t kDefaultBitDepth = 8;
+  constexpr const auto kDefaultSampling = VideoChromaSampling::k420;
+  constexpr const auto kDefaultLatency = LatencyMode::kNormal;
+  constexpr const auto kDefaultTexturing = base::PreferTexturingSupport::kNo;
+
+  EXPECT_CALL(resource_manager, SetPriority(testing::_))
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, HasUHDVideoDecoder())
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, IsCategorySupported(testing::_))
+      .WillRepeatedly(testing::Return(true));
+
+  int category = 1;
+  EXPECT_CALL(
+      resource_manager,
+      GetCapableVideoCategoryId(testing::_, testing::_, testing::_, testing::_,
+                                testing::_, testing::_, testing::_))
+      .WillRepeatedly([&]() { return category++; });
+  EXPECT_CALL(resource_manager,
+              AllocateResource(testing::_, testing::_, testing::_))
+      .WillOnce([](int, int, base::ResourceManager::ReleaseCB)
+                    -> absl::optional<base::AllocatedResource> {
+        return base::AllocatedResource(base::AllocatedResource::Token{},
+                                       "testing", 1);
+      });
+
+  absl::optional<AllocatedDecoder> decoder = decoder_promotion->SelectDecoder(
+      {kDefaultCodec, 42, kDefaultCodedSize, kDefaultSampling, kDefaultBitDepth,
+       kDefaultLatency, kDefaultTexturing, nullptr},
+      std::move(callbacks));
+  ASSERT_TRUE(decoder.has_value());
+
+  for (VideoCodecLevel level : {41, 40}) {
+    EXPECT_FALSE(decoder_promotion->NeedsDecoderReselection(
+        *decoder,
+        {kDefaultCodec, level, kDefaultCodedSize, kDefaultSampling,
+         kDefaultBitDepth, kDefaultLatency, kDefaultTexturing, nullptr}));
+  }
+}
+
+TEST(DecoderPromotionTestReuseDecoder,
+     H264DecoderMightBeReusedUptoFHDResolution) {
+  base::MockResourceManager resource_manager;
+  DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
+  decoder_promotion->SetResourceManagerForTesting(&resource_manager);
+
+  // Do not need to assign callback implementations, as they won't be called.
+  AllocationCallbacks callbacks;
+
+  constexpr const VideoCodecLevel kDefaultLevel = 42;
+  constexpr const MediaVideoCodec kDefaultCodec = MediaVideoCodec::kCodecH264;
+  constexpr const size_t kDefaultBitDepth = 8;
+  constexpr const auto kDefaultSampling = VideoChromaSampling::k420;
+  constexpr const auto kDefaultLatency = LatencyMode::kNormal;
+  constexpr const auto kDefaultTexturing = base::PreferTexturingSupport::kNo;
+
+  EXPECT_CALL(resource_manager, SetPriority(testing::_))
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, HasUHDVideoDecoder())
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, IsCategorySupported(testing::_))
+      .WillRepeatedly(testing::Return(true));
+
+  int category = 1;
+  EXPECT_CALL(
+      resource_manager,
+      GetCapableVideoCategoryId(testing::_, testing::_, testing::_, testing::_,
+                                testing::_, testing::_, testing::_))
+      .WillRepeatedly([&]() { return category++; });
+  EXPECT_CALL(resource_manager,
+              AllocateResource(testing::_, testing::_, testing::_))
+      .WillOnce([](int, int, base::ResourceManager::ReleaseCB)
+                    -> absl::optional<base::AllocatedResource> {
+        return base::AllocatedResource(base::AllocatedResource::Token{},
+                                       "testing", 1);
+      });
+
+  absl::optional<AllocatedDecoder> decoder = decoder_promotion->SelectDecoder(
+      {kDefaultCodec, kDefaultLevel, gfx::Size(320, 180), kDefaultSampling,
+       kDefaultBitDepth, kDefaultLatency, kDefaultTexturing, nullptr},
+      std::move(callbacks));
+  ASSERT_TRUE(decoder.has_value());
+
+  EXPECT_FALSE(decoder_promotion->NeedsDecoderReselection(
+      *decoder,
+      {kDefaultCodec, kDefaultLevel, gfx::Size{1920, 1080}, kDefaultSampling,
+       kDefaultBitDepth, kDefaultLatency, kDefaultTexturing, nullptr}));
+}
+
+TEST(DecoderPromotionTestReuseDecoder,
+     H264FullHDReusesWhenDecreasingLevelTo42) {
+  base::MockResourceManager resource_manager;
+  DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
+  decoder_promotion->SetResourceManagerForTesting(&resource_manager);
+
+  // Do not need to assign callback implementations, as they won't be called.
+  AllocationCallbacks callbacks;
+
+  constexpr const gfx::Size kDefaultCodedSize(1920, 1080);
+  constexpr const MediaVideoCodec kDefaultCodec = MediaVideoCodec::kCodecH264;
+  constexpr const size_t kDefaultBitDepth = 8;
+  constexpr const auto kDefaultSampling = VideoChromaSampling::k420;
+  constexpr const auto kDefaultLatency = LatencyMode::kNormal;
+  constexpr const auto kDefaultTexturing = base::PreferTexturingSupport::kNo;
+
+  EXPECT_CALL(resource_manager, SetPriority(testing::_))
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, HasUHDVideoDecoder())
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(resource_manager, IsCategorySupported(testing::_))
+      .WillRepeatedly(testing::Return(true));
+
+  int category = 1;
+  EXPECT_CALL(
+      resource_manager,
+      GetCapableVideoCategoryId(testing::_, testing::_, testing::_, testing::_,
+                                testing::_, testing::_, testing::_))
+      .WillRepeatedly([&]() { return category++; });
+  EXPECT_CALL(resource_manager,
+              AllocateResource(testing::_, testing::_, testing::_))
+      .WillOnce([](int, int, base::ResourceManager::ReleaseCB)
+                    -> absl::optional<base::AllocatedResource> {
+        return base::AllocatedResource(base::AllocatedResource::Token{},
+                                       "testing", 1);
+      });
+
+  absl::optional<AllocatedDecoder> decoder = decoder_promotion->SelectDecoder(
+      {kDefaultCodec, 50, kDefaultCodedSize, kDefaultSampling, kDefaultBitDepth,
+       kDefaultLatency, kDefaultTexturing, nullptr},
+      std::move(callbacks));
+  ASSERT_TRUE(decoder.has_value());
+
+  EXPECT_FALSE(decoder_promotion->NeedsDecoderReselection(
+      *decoder,
+      {kDefaultCodec, 42, kDefaultCodedSize, kDefaultSampling, kDefaultBitDepth,
+       kDefaultLatency, kDefaultTexturing, nullptr}));
+}
+
 }  // namespace media