Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1591)

Unified Diff: media/filters/video_frame_stream_unittest.cc

Issue 2837613004: media: Support better decoder switching (Closed)
Patch Set: Mock*Decoder name Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/filters/video_frame_stream_unittest.cc
diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc
index 88b5af8ae3b4f733e3bc6c39fd7d298ff1b8673b..2baf2347be21a6a1f3fd4e873dc535cfd2c79bab 100644
--- a/media/filters/video_frame_stream_unittest.cc
+++ b/media/filters/video_frame_stream_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -70,34 +71,11 @@ class VideoFrameStreamTest
pending_stop_(false),
num_decoded_bytes_unreported_(0),
has_no_key_(false) {
- int decoding_delay = GetParam().decoding_delay;
- int parallel_decoding = GetParam().parallel_decoding;
- BytesDecodedCB bytes_decoded_cb = base::Bind(
- &VideoFrameStreamTest::OnBytesDecoded, base::Unretained(this));
-
- // Provide 3 decoders to test fallback cases.
- // TODO(xhwang): We should test the case where only certain decoder
- // supports encrypted streams. Currently this is hard to test because we use
- // parameterized tests which need to pass in all combinations.
- ScopedVector<VideoDecoder> decoders;
- for (int i = 0; i < 3; ++i) {
- FakeVideoDecoder* decoder =
- new FakeVideoDecoder(GetDecoderName(i), decoding_delay,
- parallel_decoding, bytes_decoded_cb);
-
- if (GetParam().is_encrypted && !GetParam().has_decryptor)
- decoder->EnableEncryptedConfigSupport();
-
- decoders.push_back(decoder);
-
- // Keep a copy of the raw pointers so we can change the behavior of each
- // decoder.
- decoders_.push_back(decoder);
- }
-
video_frame_stream_.reset(new VideoFrameStream(
- message_loop_.task_runner(), std::move(decoders), &media_log_));
-
+ message_loop_.task_runner(),
+ base::Bind(&VideoFrameStreamTest::CreateVideoDecodersForTest,
+ base::Unretained(this)),
+ &media_log_));
video_frame_stream_->set_decoder_change_observer_for_testing(base::Bind(
&VideoFrameStreamTest::OnDecoderChanged, base::Unretained(this)));
@@ -135,19 +113,95 @@ class VideoFrameStreamTest
DCHECK(!pending_stop_);
}
- MOCK_METHOD0(OnWaitingForDecryptionKey, void(void));
-
- void OnStatistics(const PipelineStatistics& statistics) {
- num_decoded_bytes_unreported_ -= statistics.video_bytes_decoded;
- }
-
void OnBytesDecoded(int count) {
num_decoded_bytes_unreported_ += count;
}
- void SimulateDecoderInitFailure(const std::vector<int>& decoder_indices) {
- for (const auto& i : decoder_indices)
+ // Callback to create a list of decoders for the DecoderSelector to select
+ // from. Decoder selection happens
+ // - on the initial selection in Initialize(),
+ // - on decoder reinitialization failure, which can be simulated by calling
+ // decoder_->SimulateFailureToInit(), and
+ // - on decode error of the first buffer, which can be simulated by calling
+ // decoder_->SimulateError() before reading the first frame.
+ ScopedVector<VideoDecoder> CreateVideoDecodersForTest() {
+ // Previously decoders could have been destroyed on decoder reselection.
+ decoders_.clear();
+
+ // Provide 3 decoders to test fallback cases.
+ // TODO(xhwang): We should test the case where only certain decoder
+ // supports encrypted streams. Currently this is hard to test because we use
+ // parameterized tests which need to pass in all combinations.
+ ScopedVector<VideoDecoder> decoders;
+ for (int i = 0; i < 3; ++i) {
+ FakeVideoDecoder* decoder =
+ new FakeVideoDecoder(GetDecoderName(i), GetParam().decoding_delay,
+ GetParam().parallel_decoding,
+ base::Bind(&VideoFrameStreamTest::OnBytesDecoded,
+ base::Unretained(this)));
+
+ if (GetParam().is_encrypted && !GetParam().has_decryptor)
+ decoder->EnableEncryptedConfigSupport();
+
+ decoders.push_back(decoder);
+
+ // Keep a copy of the raw pointers so we can change the behavior of each
+ // decoder.
+ decoders_.push_back(decoder);
+ }
+
+ for (const auto& i : decoder_indices_to_fail_init_)
decoders_[i]->SimulateFailureToInit();
+
+ for (const auto& i : decoder_indices_to_hold_init_)
+ decoders_[i]->HoldNextInit();
+
+ for (const auto& i : decoder_indices_to_hold_decode_)
+ decoders_[i]->HoldDecode();
+
+ decoder_indices_to_fail_init_.clear();
+ decoder_indices_to_hold_init_.clear();
+ decoder_indices_to_hold_decode_.clear();
+
+ return decoders;
+ }
+
+ // On next decoder selection, fail initialization on decoders specified by
+ // |decoder_indices|.
+ void FailDecoderInitOnSelection(const std::vector<int>& decoder_indices) {
+ decoder_indices_to_fail_init_ = decoder_indices;
+ }
+
+ // On next decoder selection, hold initialization on decoders specified by
+ // |decoder_indices|.
+ void HoldDecoderInitOnSelection(const std::vector<int>& decoder_indices) {
+ decoder_indices_to_hold_init_ = decoder_indices;
+ }
+
+ // After next decoder selection, hold decode on decoders specified by
+ // |decoder_indices|. This is needed because after decoder selection decode
+ // may be resumed immediately and it'll be too late to hold decode then.
+ void HoldDecodeAfterSelection(const std::vector<int>& decoder_indices) {
+ decoder_indices_to_hold_decode_ = decoder_indices;
+ }
+
+ // Updates the |decoder_| currently being used by VideoFrameStream.
+ void OnDecoderChanged(VideoDecoder* decoder) {
+ if (!decoder) {
+ decoder_ = nullptr;
+ return;
+ }
+
+ std::string name = decoder->GetDisplayName();
+ ASSERT_TRUE(GetDecoderName(0) == name || GetDecoderName(1) == name ||
+ GetDecoderName(2) == name);
+ decoder_ = static_cast<FakeVideoDecoder*>(decoder);
+ }
+
+ MOCK_METHOD0(OnWaitingForDecryptionKey, void(void));
+
+ void OnStatistics(const PipelineStatistics& statistics) {
+ num_decoded_bytes_unreported_ -= statistics.video_bytes_decoded;
}
void OnInitialized(bool success) {
@@ -173,18 +227,6 @@ class VideoFrameStreamTest
base::RunLoop().RunUntilIdle();
}
- void OnDecoderChanged(VideoDecoder* decoder) {
- if (!decoder) {
- decoder_ = nullptr;
- return;
- }
-
- std::string name = decoder->GetDisplayName();
- ASSERT_TRUE(GetDecoderName(0) == name || GetDecoderName(1) == name ||
- GetDecoderName(2) == name);
- decoder_ = static_cast<FakeVideoDecoder*>(decoder);
- }
-
// Fake Decrypt() function used by DecryptingDemuxerStream. It does nothing
// but removes the DecryptConfig to make the buffer unencrypted.
void Decrypt(Decryptor::StreamType stream_type,
@@ -374,6 +416,10 @@ class VideoFrameStreamTest
// once on a config change. They are owned by |video_frame_stream_|.
std::vector<FakeVideoDecoder*> decoders_;
+ std::vector<int> decoder_indices_to_fail_init_;
+ std::vector<int> decoder_indices_to_hold_init_;
+ std::vector<int> decoder_indices_to_hold_decode_;
+
// The current decoder used by |video_frame_stream_|.
FakeVideoDecoder* decoder_;
@@ -423,13 +469,13 @@ TEST_P(VideoFrameStreamTest, Initialization) {
}
TEST_P(VideoFrameStreamTest, AllDecoderInitializationFails) {
- SimulateDecoderInitFailure({0, 1, 2});
+ FailDecoderInitOnSelection({0, 1, 2});
Initialize();
EXPECT_FALSE(is_initialized_);
}
TEST_P(VideoFrameStreamTest, PartialDecoderInitializationFails) {
- SimulateDecoderInitFailure({0, 1});
+ FailDecoderInitOnSelection({0, 1});
Initialize();
EXPECT_TRUE(is_initialized_);
}
@@ -660,7 +706,7 @@ TEST_P(VideoFrameStreamTest, Destroy_BeforeInitialization) {
}
TEST_P(VideoFrameStreamTest, Destroy_DuringInitialization) {
- decoders_[0]->HoldNextInit();
+ HoldDecoderInitOnSelection({0});
Initialize();
}
@@ -744,15 +790,17 @@ TEST_P(VideoFrameStreamTest, Destroy_AfterRead_AfterReset) {
Reset();
}
-// The following tests cover the fallback logic.
+// The following tests cover the fallback logic after reinitialization error or
+// decode error of the first buffer after initialization.
-TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitialDecodeError) {
+TEST_P(VideoFrameStreamTest, FallbackDecoder_DecodeError) {
Initialize();
decoder_->SimulateError();
ReadOneFrame();
// |video_frame_stream_| should have fallen back to a new decoder.
ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
+
ASSERT_FALSE(pending_read_);
ASSERT_EQ(VideoFrameStream::OK, last_read_status_);
@@ -824,7 +872,7 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_DoesReinitializeStompPendingRead) {
// Force an error to occur on the first decode, but ensure it isn't propagated
// until after the next read has been started.
decoder_->SimulateError();
- decoders_[1]->HoldDecode();
+ HoldDecodeAfterSelection({1});
// Complete the fallback to the second decoder with the read still pending.
base::RunLoop().RunUntilIdle();
@@ -844,30 +892,55 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_DoesReinitializeStompPendingRead) {
ASSERT_GT(decoder_->total_bytes_decoded(), first_decoded_bytes);
}
-TEST_P(VideoFrameStreamTest,
- FallbackDecoder_SelectedOnInitialDecodeError_Twice) {
+TEST_P(VideoFrameStreamTest, FallbackDecoder_DecodeErrorTwice) {
Initialize();
+
+ // Simulate decode error to trigger the fallback path.
decoder_->SimulateError();
- decoders_[1]->HoldNextInit();
+ // Decoder 0 should be blacklisted and never tried. Hold decode on decoder 1
+ // and simulate decode error again.
+ HoldDecodeAfterSelection({1});
ReadOneFrame();
-
- decoders_[1]->SatisfyInit();
- decoders_[1]->SimulateError();
+ ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
+ decoder_->SimulateError();
+ decoder_->SatisfyDecode();
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName());
+ // Only one fallback is allowed so we are not falling back to other decoders.
+ ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
+ EXPECT_FALSE(pending_read_);
+ ASSERT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
+}
- // |video_frame_stream_| should have fallen back to |decoders_[2]|.
- ASSERT_FALSE(pending_read_);
- ASSERT_EQ(VideoFrameStream::OK, last_read_status_);
+TEST_P(VideoFrameStreamTest,
+ FallbackDecoder_DecodeErrorTwice_AfterReinitialization) {
+ Initialize();
- // Can't check previously selected decoder(s) right now, they might have been
- // destroyed already.
- ASSERT_GT(decoder_->total_bytes_decoded(), 0);
+ // Simulate decode error to trigger the fallback path.
+ decoder_->SimulateError();
+ ReadOneFrame();
+ ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
- // Verify no frame was dropped.
- ReadAllFrames();
+ // Simulate reinitialize error of decoder 1.
+ decoder_->SimulateFailureToInit();
+
+ // Decoder 0 should be selected again. Simulate immediate decode error after
+ // reinitialization.
+ HoldDecodeAfterSelection({0});
+ ReadUntilDecoderReinitialized();
+ ASSERT_EQ(GetDecoderName(0), decoder_->GetDisplayName());
+ decoder_->SimulateError();
+ decoder_->SatisfyDecode();
+ base::RunLoop().RunUntilIdle();
+
+ // VideoDecoderStream has produced video frames, so we are not trying fallback
+ // again.
+ // TODO(xhwang): Revisit this behavior, e.g. always try to fallback if a newly
+ // selected decoder has not produced any video frames before.
+ ASSERT_EQ(GetDecoderName(0), decoder_->GetDisplayName());
+ EXPECT_FALSE(pending_read_);
+ ASSERT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
}
TEST_P(VideoFrameStreamTest, FallbackDecoder_ConfigChangeClearsPendingBuffers) {
@@ -925,6 +998,7 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) {
GetParam().parallel_decoding > 1) {
return;
}
+
Initialize();
// Block on demuxer read and decoder decode so we can step through.
@@ -956,7 +1030,10 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) {
++demuxer_reads_satisfied;
decoder_->SimulateError();
- decoders_[1]->HoldNextInit();
+
+ HoldDecoderInitOnSelection({1});
+ HoldDecodeAfterSelection({1});
+
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pending_read_);
@@ -964,7 +1041,6 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) {
video_frame_stream_->get_pending_buffers_size_for_testing());
decoders_[1]->SatisfyInit();
- decoders_[1]->HoldDecode();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
@@ -981,8 +1057,8 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) {
decoder_->SatisfyDecode();
base::RunLoop().RunUntilIdle();
- // Make sure all buffers consumed by |decoders_[1]| have come from the
- // fallback. Pending buffers should not have been cleared yet.
+ // Make sure all buffers consumed by |decoders_| have come from the fallback.
+ // Pending buffers should not have been cleared yet.
EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing());
EXPECT_EQ(demuxer_reads_satisfied,
video_frame_stream_->get_pending_buffers_size_for_testing());
@@ -1005,12 +1081,13 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) {
TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnDecodeThenInitErrors) {
Initialize();
decoder_->SimulateError();
- SimulateDecoderInitFailure({1});
+ FailDecoderInitOnSelection({1});
ReadOneFrame();
+ // Decoder 0 should be blacklisted, and decoder 1 fails to initialize, so
+ // |video_frame_stream_| should have fallen back to decoder 2.
ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName());
- // |video_frame_stream_| should have fallen back to |decoders_[2]|
ASSERT_FALSE(pending_read_);
ASSERT_EQ(VideoFrameStream::OK, last_read_status_);
@@ -1023,7 +1100,7 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnDecodeThenInitErrors) {
}
TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitThenDecodeErrors) {
- SimulateDecoderInitFailure({0});
+ FailDecoderInitOnSelection({0});
Initialize();
ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
@@ -1032,8 +1109,8 @@ TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitThenDecodeErrors) {
decoder_->SimulateError();
base::RunLoop().RunUntilIdle();
- // |video_frame_stream_| should have fallen back to |decoders_[2]|
- ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName());
+ // |video_frame_stream_| should have fallen back to decoder 0.
+ ASSERT_EQ(GetDecoderName(0), decoder_->GetDisplayName());
ASSERT_FALSE(pending_read_);
ASSERT_EQ(VideoFrameStream::OK, last_read_status_);
@@ -1094,7 +1171,7 @@ TEST_P(VideoFrameStreamTest, DecoderErrorWhenNotReading) {
EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
}
-TEST_P(VideoFrameStreamTest, FallbackDecoderSelectedOnFailureToReinitialize) {
+TEST_P(VideoFrameStreamTest, ReinitializeFailure_Once) {
Initialize();
decoder_->SimulateFailureToInit();
ReadUntilDecoderReinitialized();
@@ -1103,24 +1180,51 @@ TEST_P(VideoFrameStreamTest, FallbackDecoderSelectedOnFailureToReinitialize) {
ASSERT_GT(decoder_->total_bytes_decoded(), 0);
}
-TEST_P(VideoFrameStreamTest,
- FallbackDecoderSelectedOnFailureToReinitialize_Twice) {
+TEST_P(VideoFrameStreamTest, ReinitializeFailure_Twice) {
Initialize();
+
+ // Trigger reinitialization error, and fallback to decoder 1.
decoder_->SimulateFailureToInit();
ReadUntilDecoderReinitialized();
ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName());
+
ReadOneFrame();
+
+ // Trigger reinitialization error again, and fallback back to decoder 0.
+ decoder_->SimulateFailureToInit();
+ ReadUntilDecoderReinitialized();
+ ASSERT_EQ(GetDecoderName(0), decoder_->GetDisplayName());
+ ReadAllFrames();
+}
+
+TEST_P(VideoFrameStreamTest, ReinitializeFailure_OneUnsupportedDecoder) {
+ Initialize();
+
+ // The current decoder will fail to reinitialize and will be blacklisted.
decoder_->SimulateFailureToInit();
+
+ // Decoder 1 will also fail to initialize on decoder selection.
+ FailDecoderInitOnSelection({1});
+
ReadUntilDecoderReinitialized();
+
+ // As a result, decoder 2 will be selected.
ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName());
+
ReadAllFrames();
}
-TEST_P(VideoFrameStreamTest, DecodeErrorAfterFallbackDecoderSelectionFails) {
+TEST_P(VideoFrameStreamTest, ReinitializeFailure_NoSupportedDecoder) {
Initialize();
+
+ // The current decoder will fail to reinitialize and will be blacklisted.
decoder_->SimulateFailureToInit();
- SimulateDecoderInitFailure({1, 2});
+
+ // Decoder 1 and 2 will also fail to initialize on decoder selection.
+ FailDecoderInitOnSelection({1, 2});
+
ReadUntilDecoderReinitialized();
+
// The error will surface from Read() as DECODE_ERROR.
while (last_read_status_ == VideoFrameStream::OK) {
ReadOneFrame();
@@ -1134,7 +1238,7 @@ TEST_P(VideoFrameStreamTest, Destroy_DuringFallbackDecoderSelection) {
Initialize();
decoder_->SimulateFailureToInit();
EnterPendingState(DECODER_REINIT);
- decoders_[1]->HoldNextInit();
+ HoldDecoderInitOnSelection({1});
SatisfyPendingCallback(DECODER_REINIT);
}

Powered by Google App Engine
This is Rietveld 408576698