[TTVD] Fix initialization result when starting in partial view 78/317978/2
authorJakub Gajownik <j.gajownik2@samsung.com>
Mon, 16 Sep 2024 10:12:51 +0000 (12:12 +0200)
committerBot Blink <blinkbot@samsung.com>
Mon, 23 Sep 2024 09:33:22 +0000 (09:33 +0000)
Video decoder might start in different moments, within
various application state. This change ensures that bare
initialization is finished with desired state.
Sometimes we should accept initialization request or just
do it dummy way, additionally RTC is somehow treated
differently than non-RTC pipeline. There are multiple
combinations now checked in TTvdVideoDecoder tests to
prevent regressions.

Note that this only handles initialization, further parts
(e.g config selection) will be fixes in other patches.

Bug: https://jira-eu.sec.samsung.net/browse/VDGAME-575
Change-Id: I0e822c494f21157c1454627c829847c8b0495a6e
Signed-off-by: Jakub Gajownik <j.gajownik2@samsung.com>
media/filters/tizen/ttvd_video_decoder_impl.cc
media/filters/tizen/ttvd_video_decoder_impl.h
media/filters/tizen/ttvd_video_decoder_test.cc

index 000929aa28cf556b567ad40ad36de9204dcff302..26cc4101218d3d45c3386f1e1a60e96cdc6360df 100644 (file)
@@ -323,6 +323,7 @@ void TTvdVideoDecoderImpl::Reset(base::OnceClosure closure) {
 
   switch (decoder_state_) {
     case DecoderState::kCreated:
+    case DecoderState::kReleasedBeforeInit:
       TIZEN_MEDIA_LOG(ERROR)
           << "Reset does nothing in state " << decoder_state_;
       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
@@ -416,6 +417,7 @@ void TTvdVideoDecoderImpl::Decode(scoped_refptr<DecoderBuffer> buffer,
     case DecoderState::kCreated:
     case DecoderState::kError:
     case DecoderState::kResetting:
+    case DecoderState::kReleasedBeforeInit:
       TIZEN_MEDIA_LOG(ERROR)
           << "Invalid call to decode in state: " << decoder_state_;
       decoder_state_ = DecoderState::kError;
@@ -712,14 +714,34 @@ void TTvdVideoDecoderImpl::Initialize(VideoDecoderConfig config,
                                       VideoDecoder::InitCB init_cb,
                                       VideoDecoder::OutputCB output_cb,
                                       WaitingCB waiting_cb) {
-  if (decoder_state_ == DecoderState::kResourceTaken ||
-      decoder_state_ == DecoderState::kSuspended) {
+  initialized_ = true;
+  waiting_for_key_ = false;
+  first_frame_done_ = false;
+
+  if (decoder_state_ == DecoderState::kReleasedBeforeInit) {
+    if (config.is_rtc()) {
+      // WebRTC decoding has it's own decoding pipeline allowing for SW decoding
+      // to be done seamlessly, without reloading whole player. That is why we
+      // can suspend decoder, still having software fallback and additionally
+      // we are still able to resume HW decoding when needed.
+      decoder_state_ = DecoderState::kSuspended;
+    } else {
+      // Behave like we would receive some internal error from HW decoder.
+      // It should trigger software fallback where possible or upper layer
+      // reinitialization if seamless decoder switch is not possible.
+      decoder_state_ = DecoderState::kResourceTaken;
+    }
+  }
+
+  if (decoder_state_ == DecoderState::kResourceTaken) {
     std::move(init_cb).Run(DecoderStatus::Codes::kFailedToCreateDecoder);
     return;
   }
 
-  waiting_for_key_ = false;
-  first_frame_done_ = false;
+  if (decoder_state_ == DecoderState::kSuspended) {
+    std::move(init_cb).Run(DecoderStatus::Codes::kOk);
+    return;
+  }
 
   if (decoder_state_ != DecoderState::kCreated) {
     if (low_delay != low_delay_) {
@@ -977,6 +999,14 @@ void TTvdVideoDecoderImpl::Resume() {
 void TTvdVideoDecoderImpl::ReleaseResources() {
   TIZEN_MEDIA_LOG(INFO) << "Release decoder in state: " << decoder_state_;
 
+  if (!initialized_) {
+    // Current configuration is not yet known, so we need to used dedicated
+    // state to track this. Note that during |Initialize| it should change
+    // to either of below values.
+    decoder_state_ = DecoderState::kReleasedBeforeInit;
+    return;
+  }
+
   if (config_.is_rtc()) {
     // WebRTC decoding has it's own decoding pipeline allowing for SW decoding
     // to be done seamlessly, without reloading whole player. That is why we
@@ -986,7 +1016,7 @@ void TTvdVideoDecoderImpl::ReleaseResources() {
   } else {
     // Behave like we would receive some internal error from HW decoder.
     // It should trigger software fallback where possible or upper layer
-    // reinitialization is seamless decoder switch is not possible.
+    // reinitialization if seamless decoder switch is not possible.
     decoder_state_ = DecoderState::kResourceTaken;
   }
 
@@ -1697,6 +1727,7 @@ void TTvdVideoDecoderImpl::OnError(DecoderStatus error_code) {
     case DecoderState::kResourceTaken:
     case DecoderState::kLazyInitializing:
     case DecoderState::kSuspended:
+    case DecoderState::kReleasedBeforeInit:
       TIZEN_MEDIA_LOG(ERROR)
           << "Facade error in unexpected state: " << decoder_state_;
       return;
@@ -2151,6 +2182,9 @@ std::ostream& operator<<(std::ostream& o,
     case TTvdVideoDecoderImpl::DecoderState::kSuspended:
       o << "Suspended";
       break;
+    case TTvdVideoDecoderImpl::DecoderState::kReleasedBeforeInit:
+      o << "ReleasedBeforeInit";
+      break;
   }
   return o;
 }
index 2a9aac6b96be5ac383a9a7ead8286695ae808b73..b546f9873106b2ab4b643bb52ede2268e12e566b 100644 (file)
@@ -81,6 +81,7 @@ class MEDIA_EXPORT TTvdVideoDecoderImpl {
     kSwitchingDecoder,
     kError,
     kSuspended,
+    kReleasedBeforeInit,
   };
 
   using GetCommandbufferCB =
@@ -271,6 +272,10 @@ class MEDIA_EXPORT TTvdVideoDecoderImpl {
 
   MediaVideoCodec codec_;
 
+  // Tracks if |Initialize| was ever called for this instance. It exists
+  /// because it's hard to track using |decoder_state_|.
+  bool initialized_ = false;
+
   // Determines whether decoder should return decoded frame without
   // further |Decode| calls. Note that B-frames are not allowed if
   // it is enabled.
index 448e6ea5644f31cb1c1570a62aff166e8bb05cdc..158f29b7b34ce8019865431592c3ae1890e248fd 100644 (file)
@@ -5,9 +5,12 @@
 #include "media/filters/tizen/ttvd_video_decoder.h"
 
 #include "absl/types/optional.h"
+#include "base/test/task_environment.h"
 #include "base/threading/thread.h"
+#include "base/tizen/global_resource_manager.h"
 #include "base/tizen/provider_callbacks_helper.h"
 #include "base/tizen/resource_manager.h"
+#include "media/base/test_helpers.h"
 #include "media/filters/tizen/decoder_promotion.h"
 #include "media/filters/tizen/media_video_codec.h"
 #include "media/gpu/test/fake_command_buffer_helper.h"
@@ -85,4 +88,100 @@ TEST(TTvdVideoDecoderTest, CreateWithEmptyFacade) {
   gpu_thread.FlushForTesting();
 }
 
+struct InitData {
+  bool is_rtc;
+  bool should_succeed;
+  std::vector<suspend_resume::State> operations;
+};
+
+constexpr const bool kRtc = true;
+constexpr const bool kNormal = false;
+constexpr const bool kInitSuccess = true;
+constexpr const bool kInitError = false;
+
+struct InitData kPartialViewTests[] = {
+    {kRtc, kInitSuccess, {suspend_resume::State::RESUMED}},
+    {kNormal, kInitSuccess, {suspend_resume::State::RESUMED}},
+    {kRtc, kInitSuccess, {suspend_resume::State::PARTIAL}},
+    {kNormal, kInitError, {suspend_resume::State::PARTIAL}},
+    {kRtc, kInitSuccess, {suspend_resume::State::SUSPENDED}},
+    {kNormal, kInitSuccess, {suspend_resume::State::SUSPENDED}},
+    {kRtc,
+     kInitSuccess,
+     {suspend_resume::State::PARTIAL, suspend_resume::State::RESUMED}},
+    {kNormal,
+     kInitSuccess,
+     {suspend_resume::State::PARTIAL, suspend_resume::State::RESUMED}},
+    {kRtc,
+     kInitSuccess,
+     {suspend_resume::State::PARTIAL, suspend_resume::State::SUSPENDED,
+      suspend_resume::State::PARTIAL}},
+    {kNormal,
+     kInitError,
+     {suspend_resume::State::PARTIAL, suspend_resume::State::SUSPENDED,
+      suspend_resume::State::PARTIAL}},
+};
+
+class TTvdVideoDecoderTestInitialize : public testing::TestWithParam<InitData> {
+};
+
+TEST_P(TTvdVideoDecoderTestInitialize, InitializePartialViewBeforeCreation) {
+  const auto init_data = GetParam();
+
+  FakeResourceManager fake_resource_manager;
+  SetGlobalResourceManagerForTesting(&fake_resource_manager);
+  DecoderPromotion* decoder_promotion = DecoderPromotion::GetInstance();
+  decoder_promotion->SetResourceManagerForTesting(&fake_resource_manager);
+
+  base::test::TaskEnvironment task_environment{
+      base::test::TaskEnvironment::MainThreadType::IO};
+
+  base::Thread gpu_thread("gpu_thread");
+  ASSERT_TRUE(gpu_thread.StartAndWaitForTesting());
+
+  gpu::GpuDriverBugWorkarounds gpu_workarounds;
+
+  for (auto state : init_data.operations) {
+    suspend_resume::NotifyStateChange(state);
+  }
+
+  TTvdVideoDecoder video_decoder(
+      base::BindRepeating(
+          [](scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+              -> scoped_refptr<CommandBufferHelper> {
+            return base::MakeRefCounted<FakeCommandBufferHelper>(task_runner);
+          },
+          gpu_thread.task_runner()),
+      gpu_thread.task_runner(),
+      base::BindRepeating(
+          [](const gpu::GpuDriverBugWorkarounds*)
+              -> std::unique_ptr<DecoderFacadeVideo> { return nullptr; }),
+      gpu_workarounds);
+
+  VideoDecoderConfig config = TestVideoConfig::NormalH264();
+  if (init_data.is_rtc) {
+    config.set_is_rtc(true);
+  }
+  constexpr const bool kLowLatency = true;
+  CdmContext* kNullCdmContextLowLatency = nullptr;
+  video_decoder.Initialize(
+      config, kLowLatency, kNullCdmContextLowLatency,
+      base::BindOnce(
+          [](bool should_succeed, base::OnceClosure quit_closure,
+             DecoderStatus status) {
+            EXPECT_EQ(status.is_ok(), should_succeed)
+                << "Returned code: " << static_cast<int>(status.code());
+            std::move(quit_closure).Run();
+          },
+          init_data.should_succeed, task_environment.QuitClosure()),
+      base::DoNothing(), base::DoNothing());
+
+  task_environment.RunUntilQuit();
+  gpu_thread.FlushForTesting();
+}
+
+INSTANTIATE_TEST_SUITE_P(MultipleStartingStates,
+                         TTvdVideoDecoderTestInitialize,
+                         testing::ValuesIn(kPartialViewTests));
+
 }  // namespace media