Allow batch video decoding using standard pipeline 18/321018/4
authorJakub Gajownik <j.gajownik2@samsung.com>
Wed, 20 Nov 2024 11:06:20 +0000 (12:06 +0100)
committerInsoon Kim <is46.kim@samsung.com>
Wed, 27 Nov 2024 09:48:41 +0000 (09:48 +0000)
Ported patches from upstream Chromium:
* Allow batch decoding only when output picture buffers are pre-allocated.
  https://chromium-review.googlesource.com/c/chromium/src/+/5862888
* media: Re-enable Batch decoding in the renderer
  https://chromium-review.googlesource.com/c/chromium/src/+/5892966

Bug: https://jira-eu.sec.samsung.net/browse/VDGAME-624
Change-Id: I2045dd3cf9a61b86eae16c3187e898c9dc28d37f
Signed-off-by: Jakub Gajownik <j.gajownik2@samsung.com>
media/base/fake_demuxer_stream.cc
media/base/fake_demuxer_stream.h
media/base/media_switches.cc
media/filters/decoder_stream.cc
media/filters/decoder_stream.h
media/filters/fake_video_decoder.cc
media/filters/fake_video_decoder.h
media/filters/video_decoder_stream_unittest.cc

index f1f4264ed7f9136fef73c08476efe27f6a2ca043..27529b9758cfbf492666101d7965dbebd03e6b89 100644 (file)
@@ -79,8 +79,7 @@ void FakeDemuxerStream::Initialize() {
   next_read_num_ = 0;
 }
 
-// Only return one buffer at a time so we ignore the count.
-void FakeDemuxerStream::Read(uint32_t /*count*/, ReadCB read_cb) {
+void FakeDemuxerStream::Read(uint32_t count, ReadCB read_cb) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(!read_cb_);
 
@@ -90,7 +89,7 @@ void FakeDemuxerStream::Read(uint32_t /*count*/, ReadCB read_cb) {
     return;
 
   DCHECK(read_to_hold_ == -1 || read_to_hold_ > next_read_num_);
-  DoRead();
+  DoRead(count);
 }
 
 AudioDecoderConfig FakeDemuxerStream::audio_decoder_config() {
@@ -177,9 +176,10 @@ void FakeDemuxerStream::UpdateVideoDecoderConfig() {
   next_size_.set_height(next_size_.height() + coded_size_delta_.y());
 }
 
-void FakeDemuxerStream::DoRead() {
+void FakeDemuxerStream::DoRead(int read_count) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(read_cb_);
+  DCHECK_GE(read_count, 1);
 
   next_read_num_++;
 
@@ -197,25 +197,31 @@ void FakeDemuxerStream::DoRead() {
     return;
   }
 
-  scoped_refptr<DecoderBuffer> buffer = CreateFakeVideoBufferForTest(
-      video_decoder_config_, current_timestamp_, duration_);
-
-  // TODO(xhwang): Output out-of-order buffers if needed.
-  if (is_encrypted_) {
-    buffer->set_decrypt_config(DecryptConfig::CreateCencConfig(
-        std::string(kKeyId, kKeyId + std::size(kKeyId)),
-        std::string(kIv, kIv + std::size(kIv)), std::vector<SubsampleEntry>()));
-  }
-  buffer->set_timestamp(current_timestamp_);
-  buffer->set_duration(duration_);
-  current_timestamp_ += duration_;
+  DemuxerStream::DecoderBufferVector output_buffers;
+  for (int i = 0; i < read_count; ++i) {
+    scoped_refptr<DecoderBuffer> buffer = CreateFakeVideoBufferForTest(
+        video_decoder_config_, current_timestamp_, duration_);
+
+    // TODO(xhwang): Output out-of-order buffers if needed.
+    if (is_encrypted_) {
+      buffer->set_decrypt_config(DecryptConfig::CreateCencConfig(
+          std::string(kKeyId, kKeyId + std::size(kKeyId)),
+          std::string(kIv, kIv + std::size(kIv)),
+          std::vector<SubsampleEntry>()));
+    }
+    buffer->set_timestamp(current_timestamp_);
+    buffer->set_duration(duration_);
+    current_timestamp_ += duration_;
 
-  num_buffers_left_in_current_config_--;
-  if (num_buffers_left_in_current_config_ == 0)
-    num_configs_left_--;
+    num_buffers_left_in_current_config_--;
+    if (num_buffers_left_in_current_config_ == 0) {
+      num_configs_left_--;
+    }
 
-  num_buffers_returned_++;
-  std::move(read_cb_).Run(kOk, {std::move(buffer)});
+    num_buffers_returned_++;
+    output_buffers.emplace_back(std::move(buffer));
+  }
+  std::move(read_cb_).Run(kOk, std::move(output_buffers));
 }
 
 FakeMediaResource::FakeMediaResource(int num_video_configs,
index ac71b4d7020c2c129d3d30423dd0c7e9ac5b53f8..2d4cef8c70a828d62be2cb7e5e78daa9d85970f5 100644 (file)
@@ -90,7 +90,7 @@ class FakeDemuxerStream : public DemuxerStream {
 
  private:
   void UpdateVideoDecoderConfig();
-  void DoRead();
+  void DoRead(int read_count = 1);
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
index 59439c9d4b3413c799d25da99fd9a7d388a6846d..78e337aaaeace36601bb57119d121b8e2f139a7f 100644 (file)
@@ -1632,7 +1632,7 @@ BASE_FEATURE(kFuchsiaMediacodecVideoEncoder,
 // smaller than maximum supported decodes as advertiszed by decoder.
 BASE_FEATURE(kVideoDecodeBatching,
              "VideoDecodeBatching",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Safety switch to allow us to revert to the previous behavior of using the
 // restored bounds for PiP windows, rather than the window bounds.  If this
index bf7c86b474ca7d8db9f30a90c91ecdd6a1da0814..85a2f831b367202f9084611d283b4553587b17b8 100644 (file)
@@ -292,6 +292,17 @@ int DecoderStream<StreamType>::GetMaxDecodeRequests() const {
              : 0;
 }
 
+// A false return value indicates that the decoder is not a platform decoder, or
+// it is still unknown (e.g. during initialization).
+template <DemuxerStream::Type StreamType>
+bool DecoderStream<StreamType>::IsPlatformDecoder() const {
+  // The decoder is owned by |decoder_selector_| during reinitialization, so
+  // during that time we return false to indicate decoder type unknown.
+  return state_ != STATE_REINITIALIZING_DECODER
+             ? decoder_->IsPlatformDecoder()
+             : false;
+}
+
 template <>
 int DecoderStream<DemuxerStream::AUDIO>::GetMaxDecodeRequests() const {
   return 1;
index 1396c84cdac0955f841edf9ee8f6595572b83066..32ca332b66ae03aa326faaf2221836e7cb48c7ef 100644 (file)
@@ -174,6 +174,9 @@ class MEDIA_EXPORT DecoderStream {
   // Returns maximum concurrent decode requests for the current |decoder_|.
   int GetMaxDecodeRequests() const;
 
+  // Returns if current |decoder_| is a platform decoder.
+  bool IsPlatformDecoder() const;
+
   // Returns the maximum number of outputs we should keep ready at any one time.
   int GetMaxReadyOutputs() const;
 
index 1942372b6e32afdff4bfeabc5b3cb2df50d76015..357dea68cd21825f03fbe5397a33b499244e9dc8 100644 (file)
@@ -42,6 +42,7 @@ FakeVideoDecoder::~FakeVideoDecoder() {
     SatisfyReset();
 
   decoded_frames_.clear();
+  total_decoded_frames_ = 0;
 }
 
 void FakeVideoDecoder::EnableEncryptedConfigSupport() {
@@ -201,6 +202,7 @@ void FakeVideoDecoder::SatisfySingleDecode() {
 
   DecodeCB decode_cb = std::move(held_decode_callbacks_.front());
   held_decode_callbacks_.pop_front();
+  total_decoded_frames_++;
   RunDecodeCallback(std::move(decode_cb));
 
   if (!reset_cb_.IsNull() && held_decode_callbacks_.empty())
index 8687c82aa2a9f4544bda61f53452ac8373491a62..4bbfb590628d1d84c1b0da5ec23d53f26b6f2b5d 100644 (file)
@@ -89,6 +89,8 @@ class FakeVideoDecoder : public VideoDecoder {
 
   int total_bytes_decoded() const { return total_bytes_decoded_; }
 
+  int total_decoded_frames() const { return total_decoded_frames_; }
+
  protected:
   enum State {
     STATE_UNINITIALIZED,
@@ -142,6 +144,8 @@ class FakeVideoDecoder : public VideoDecoder {
 
   bool fail_to_initialize_;
 
+  int total_decoded_frames_ = 0;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<FakeVideoDecoder> weak_factory_{this};
 };
index e6d5edd800d0a01d54ef302c9702f1bb57d43d5f..80756f7b1d70e7d151d2dedadeecf01f26f3e339 100644 (file)
@@ -635,6 +635,12 @@ TEST_P(VideoDecoderStreamTest, Read_AfterReset) {
 // Tests that the decoder stream will switch from a software decoder to a
 // hardware decoder if the config size increases
 TEST_P(VideoDecoderStreamTest, ConfigChangeSwToHw) {
+  if (base::FeatureList::IsEnabled(kVideoDecodeBatching) &&
+      GetParam().parallel_decoding != 1) {
+    // Fake demuxer allows reading over different configs when batch decoding is
+    // enabled, so we need to skip this test.
+    return;
+  }
   EnablePlatformDecoders({1});
 
   // Create a demuxer stream with a config that increases in size
@@ -659,6 +665,12 @@ TEST_P(VideoDecoderStreamTest, ConfigChangeSwToHw) {
 // Tests that the decoder stream will switch from a hardware decoder to a
 // software decoder if the config size decreases
 TEST_P(VideoDecoderStreamTest, ConfigChangeHwToSw) {
+  if (base::FeatureList::IsEnabled(kVideoDecodeBatching) &&
+      GetParam().parallel_decoding != 1) {
+    // Fake demuxer allows reading over different configs when batch decoding is
+    // enabled, so we need to skip this test.
+    return;
+  }
   EnablePlatformDecoders({1});
 
   // Create a demuxer stream with a config that progressively decreases in size
@@ -797,6 +809,53 @@ TEST_P(VideoDecoderStreamTest, Read_BlockedDemuxerAndDecoder) {
   EXPECT_FALSE(pending_read_);
 }
 
+TEST_P(VideoDecoderStreamTest, BatchDecodingWithPlatformDecoder) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kVideoDecodeBatching);
+  int parallel_decodings = GetParam().parallel_decoding;
+
+  Initialize();
+  decoder_->SetIsPlatformDecoder(true);
+
+  // Block the decoder so that we can check the DecoderBuffer number got
+  // from a single Read() call.
+  decoder_->HoldDecode();
+  ReadOneFrame();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(parallel_decodings, demuxer_stream_->num_buffers_returned());
+
+  demuxer_stream_->HoldNextRead();
+  decoder_->SatisfyDecode();
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(decoder_->total_decoded_frames(), parallel_decodings);
+}
+
+TEST_P(VideoDecoderStreamTest, NoBatchDecodingWithNonPlatformDecoder) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kVideoDecodeBatching);
+
+  Initialize();
+  // Set the decoder as not platform decoder, so that it prevents single
+  // demuxer read to return multiple DecoderBuffers.
+  decoder_->SetIsPlatformDecoder(false);
+
+  // Block the demuxer so that we can manually unblock the first demuxer
+  // read to check the DecoderBuffer number got from a single Read() call.
+  demuxer_stream_->HoldNextRead();
+  decoder_->HoldDecode();
+  ReadOneFrame();
+  demuxer_stream_->SatisfyReadAndHoldNext();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, demuxer_stream_->num_buffers_returned());
+
+  decoder_->SatisfyDecode();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(decoder_->total_decoded_frames(), 1);
+}
+
 TEST_P(VideoDecoderStreamTest, Read_DuringEndOfStreamDecode) {
   // Test applies only when the decoder allows multiple parallel requests, and
   // they are not satisfied in a single batch.