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_);
return;
DCHECK(read_to_hold_ == -1 || read_to_hold_ > next_read_num_);
- DoRead();
+ DoRead(count);
}
AudioDecoderConfig FakeDemuxerStream::audio_decoder_config() {
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_++;
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,
private:
void UpdateVideoDecoderConfig();
- void DoRead();
+ void DoRead(int read_count = 1);
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// 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
: 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;
// 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;
SatisfyReset();
decoded_frames_.clear();
+ total_decoded_frames_ = 0;
}
void FakeVideoDecoder::EnableEncryptedConfigSupport() {
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())
int total_bytes_decoded() const { return total_bytes_decoded_; }
+ int total_decoded_frames() const { return total_decoded_frames_; }
+
protected:
enum State {
STATE_UNINITIALIZED,
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};
};
// 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
// 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
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.