[TTVD] Support suspend&resume in Tizen hardware encoder 13/315413/5
authorJakub Gajownik <j.gajownik2@samsung.com>
Mon, 29 Jul 2024 09:44:57 +0000 (11:44 +0200)
committerBot Blink <blinkbot@samsung.com>
Wed, 31 Jul 2024 16:48:29 +0000 (16:48 +0000)
Before this patch, when application using hardware video
encoder was suspended, it still actively used resource.
This is problematic, if another application would also need
access encoder. Then, old application would get internal
error by resource conflict, making future usage of encoder
impossible due to this error.

After this patch, when application is going to be suspended
encoder is released. When it's resuming, encoder is prepared
again.

Bug: https://jira-eu.sec.samsung.net/browse/VDGAME-536
Change-Id: Idc9074308c8bbb8ca8db712b5ed05c8b2bd75821
Signed-off-by: Jakub Gajownik <j.gajownik2@samsung.com>
media/gpu/tizen/tizen_video_encode_accelerator.cc
media/gpu/tizen/tizen_video_encode_accelerator.h
media/gpu/tizen/tizen_video_encode_accelerator_helper.cc

index 19cdfbcecca7632636aafcff33102e936b6e90c1..38b988b34aeecd8c0148d188a3b58ea98f97a913 100644 (file)
@@ -50,6 +50,7 @@ std::ostream& operator<<(std::ostream& os, EncoderState encoder_state) {
     DECLARE_CASE_VALUE(EncoderState::kError);
     DECLARE_CASE_VALUE(EncoderState::kPreparing);
     DECLARE_CASE_VALUE(EncoderState::kUninitialized);
+    DECLARE_CASE_VALUE(EncoderState::kSuspended);
   }
   return os;
 }
@@ -291,11 +292,15 @@ TizenVideoEncodeAccelerator::TizenVideoEncodeAccelerator()
     return;
   }
   encoder_ = Encoder(encoder);
+
+  last_state_ = suspend_resume::RegisterClient(this).value_or(
+      suspend_resume::State::RESUMED);
 }
 
 TizenVideoEncodeAccelerator::~TizenVideoEncodeAccelerator() {
   TIZEN_MEDIA_LOG(INFO) << "Destructing";
   CHECK(task_runner_->BelongsToCurrentThread());
+  suspend_resume::UnregisterClient(this);
 }
 
 bool TizenVideoEncodeAccelerator::Initialize(
@@ -538,7 +543,8 @@ void TizenVideoEncodeAccelerator::RequestEncodingParametersChange(
   TIZEN_MEDIA_LOG(INFO) << bitrate.ToString();
 
   if (encoder_state_ != EncoderState::kPreparing &&
-      encoder_state_ != EncoderState::kEncoding) {
+      encoder_state_ != EncoderState::kEncoding &&
+      encoder_state_ != EncoderState::kSuspended) {
     TIZEN_MEDIA_LOG(ERROR)
         << "Cannot update stream bitrate - encoder in a wrong state: "
         << encoder_state_;
@@ -562,6 +568,36 @@ void TizenVideoEncodeAccelerator::Destroy() {
   delete this;
 }
 
+void TizenVideoEncodeAccelerator::OnStateChange(
+    suspend_resume::State new_state) {
+  last_state_ = new_state;
+
+  switch (new_state) {
+    case suspend_resume::State::RESUMED: {
+      if (!GetTaskRunner()->PostTask(
+              FROM_HERE, base::BindOnce(&TizenVideoEncodeAccelerator::Resume,
+                                        GetWeakPtr()))) {
+        TIZEN_MEDIA_LOG(ERROR) << "Failed to post |Resume| task";
+      }
+      break;
+    }
+    case suspend_resume::State::PARTIAL:
+    case suspend_resume::State::SUSPENDED: {
+      if (!GetTaskRunner()->PostTask(
+              FROM_HERE, base::BindOnce(&TizenVideoEncodeAccelerator::Suspend,
+                                        GetWeakPtr()))) {
+        TIZEN_MEDIA_LOG(ERROR) << "Failed to post |Suspend| task";
+      }
+      break;
+    }
+    default: {
+      TIZEN_MEDIA_LOG(WARNING)
+          << "Unrecognized state change in TizenVideoEncodeAccelerator";
+      break;
+    }
+  }
+}
+
 void TizenVideoEncodeAccelerator::OnDataFromEncoder(MediaPacketType packet,
                                                     uint32_t stream_id) {
   TIZEN_MEDIA_LOG(VERBOSE);
@@ -669,10 +705,18 @@ void TizenVideoEncodeAccelerator::OnEncoderBufferStatusChanged(
   CHECK(task_runner_->BelongsToCurrentThread());
 }
 
-void TizenVideoEncodeAccelerator::OnErrorFromEncoder() {
+void TizenVideoEncodeAccelerator::OnErrorFromEncoder(encoder_error_e error) {
   TIZEN_MEDIA_LOG(INFO);
   CHECK(task_runner_->BelongsToCurrentThread());
 
+  // Do not report resource conflict when encoder is already suspended. There
+  // might be race between suspend and conflict notifications. After application
+  // resume it should be reclaimed and work normally.
+  if (encoder_state_ == EncoderState::kSuspended &&
+      error == ENCODER_ERROR_RESOURCE_CONFLICT) {
+    return;
+  }
+
   ReportErrorToClient();
 }
 
@@ -700,6 +744,11 @@ void TizenVideoEncodeAccelerator::Encode(scoped_refptr<VideoFrame> frame,
   TIZEN_MEDIA_LOG(VERBOSE);
   CHECK(task_runner_->BelongsToCurrentThread());
 
+  // When encoder is suspended it's fine to not return data at all.
+  if (encoder_state_ == EncoderState::kSuspended) {
+    return;
+  }
+
   if (encoder_state_ != EncoderState::kEncoding) {
     TIZEN_MEDIA_LOG(ERROR) << "Encoding frame failed - wrong encoder state";
     return;
@@ -927,15 +976,20 @@ void TizenVideoEncodeAccelerator::ResetInternal() {
   }
 
   if (encoder_state_ != EncoderState::kPreparing &&
-      encoder_state_ != EncoderState::kEncoding) {
+      encoder_state_ != EncoderState::kEncoding &&
+      encoder_state_ != EncoderState::kSuspended) {
     TIZEN_MEDIA_LOG(INFO) << "Cannot close in state = " << encoder_state_;
     return;
   }
 
-  StopInternal();
+  // When encoder is already suspended, there is no need for stopping or
+  // clearing collections, it's already done.
+  if (encoder_state_ != EncoderState::kSuspended) {
+    StopInternal();
 
-  frames_to_encode_ = {};
-  frames_with_data_in_encoder_.clear();
+    frames_to_encode_ = {};
+    frames_with_data_in_encoder_.clear();
+  }
 
   encoder_state_ = EncoderState::kUninitialized;
 }
@@ -978,4 +1032,43 @@ void TizenVideoEncodeAccelerator::StartInternal() {
   encoder_state_ = EncoderState::kEncoding;
 }
 
+void TizenVideoEncodeAccelerator::Resume() {
+  TIZEN_MEDIA_LOG(INFO) << "Resume called in state: " << encoder_state_;
+  switch (encoder_state_) {
+    case EncoderState::kUninitialized:
+    case EncoderState::kError:
+    case EncoderState::kPreparing:
+    case EncoderState::kEncoding:
+      // Nothing to do.
+      break;
+    case EncoderState::kSuspended:
+      encoder_state_ = EncoderState::kPreparing;
+      StartInternal();
+      if (encoder_state_ != EncoderState::kEncoding) {
+        TIZEN_MEDIA_LOG(ERROR) << "Could not resume encoder";
+      }
+      break;
+  }
+}
+
+void TizenVideoEncodeAccelerator::Suspend() {
+  TIZEN_MEDIA_LOG(INFO) << "Suspend called in state: " << encoder_state_;
+  switch (encoder_state_) {
+    case EncoderState::kUninitialized:
+    case EncoderState::kError:
+    case EncoderState::kPreparing:
+      // Nothing to do.
+      break;
+    case EncoderState::kEncoding:
+      StopInternal();
+      frames_to_encode_ = {};
+      frames_with_data_in_encoder_.clear();
+      encoder_state_ = EncoderState::kSuspended;
+      break;
+    case EncoderState::kSuspended:
+      TIZEN_MEDIA_LOG(WARNING) << "Encoder already suspended";
+      break;
+  }
+}
+
 }  // namespace media
index f7ac0c75967aceba2fda27afcd38c825745ae6a6..7abfe5d64e5b6c97a73e2ca3285d562805f23058 100644 (file)
@@ -17,6 +17,7 @@
 #include "media/gpu/tizen/tizen_video_encode_accelerator_utils.h"
 #include "media/video/h264_parser.h"
 #include "media/video/video_encode_accelerator.h"
+#include "services/suspend_resume/public/cpp/suspend_resume.h"
 #include "ui/gfx/client_native_pixmap_factory.h"
 #include "ui/gfx/range/range.h"
 #include "ui/gfx/tbm_surface.h"
@@ -26,7 +27,8 @@ namespace media {
 
 using HardwareBuffers = std::vector<std::unique_ptr<gfx::TizenGpuBuffer>>;
 
-class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
+class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator,
+                                    public suspend_resume::SuspendResumeClient {
  public:
   TizenVideoEncodeAccelerator();
   ~TizenVideoEncodeAccelerator() override;
@@ -52,6 +54,9 @@ class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
     return client_native_pixmap_factory_.get();
   }
 
+  // suspend_resume::SuspendResumeClient implementation.
+  void OnStateChange(suspend_resume::State new_state) override;
+
   // Enum representing possible states of platform encoder.
   // It allows preventing calling platform encoder in wrong state.
   enum class EncoderState {
@@ -59,6 +64,7 @@ class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
     kError,
     kPreparing,
     kEncoding,
+    kSuspended,
   };
 
  private:
@@ -78,7 +84,7 @@ class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
   // Callbacks from platform encoder, already dispatched to proper thread.
   void OnDataFromEncoder(MediaPacketType packet, uint32_t stream_id);
   void OnEncoderBufferStatusChanged(encoder_buffer_status_e status);
-  void OnErrorFromEncoder();
+  void OnErrorFromEncoder(encoder_error_e error);
   void OnPacketProcessedByEncoder(base::TimeDelta timestamp);
 
   void SchedulePacketSubmit();
@@ -108,6 +114,9 @@ class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
       const scoped_refptr<VideoFrame>& video_frame,
       const MediaFormatType& media_format);
 
+  void Resume();
+  void Suspend();
+
   EncoderState encoder_state_{EncoderState::kUninitialized};
   Client* client_{nullptr};
 
@@ -126,6 +135,9 @@ class TizenVideoEncodeAccelerator : public VideoEncodeAccelerator {
   // possible that during processing one error another one will be reported.
   bool is_processing_platform_error_{false};
 
+  // Should always hold last known status of visibility.
+  suspend_resume::State last_state_;
+
   // |unique_ptr| with custom deleter used for preventing leak of |encoder_h|
   // pointer.
   using Encoder =
index 030405994c2fa0091a57154342957cba2ff762dd..c8c6bab89bd0f664716cf57bebd17311ef4f17d8 100644 (file)
@@ -83,7 +83,7 @@ void TizenVideoEncodeAcceleratorHelper::OnErrorFromEncoderCallback(
   if (!tizen_vea->GetTaskRunner()->PostTask(
           FROM_HERE,
           base::BindOnce(&TizenVideoEncodeAccelerator::OnErrorFromEncoder,
-                         tizen_vea->GetWeakPtr()))) {
+                         tizen_vea->GetWeakPtr(), error))) {
     TIZEN_MEDIA_LOG_PTR(ERROR, userdata)
         << "Failed to post `OnErrorFromEncoder` task";
   }