1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/run_loop.h"
15 #include "base/strings/string_util.h"
16 #include "base/test/gmock_callback_support.h"
17 #include "base/test/task_environment.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/limits.h"
20 #include "media/base/media_log.h"
21 #include "media/base/media_util.h"
22 #include "media/base/mock_filters.h"
23 #include "media/base/mock_media_log.h"
24 #include "media/base/test_data_util.h"
25 #include "media/base/test_helpers.h"
26 #include "media/base/video_decoder.h"
27 #include "media/base/video_frame.h"
28 #include "media/base/video_util.h"
29 #include "media/ffmpeg/ffmpeg_common.h"
30 #include "media/filters/ffmpeg_video_decoder.h"
31 #include "testing/gmock/include/gmock/gmock.h"
34 using ::testing::AtLeast;
35 using ::testing::AtMost;
36 using ::testing::InSequence;
37 using ::testing::IsNull;
38 using ::testing::Return;
39 using ::testing::SaveArg;
40 using ::testing::StrictMock;
44 constexpr gfx::Size kCodedSize(320, 180);
45 constexpr gfx::Rect kVisibleRect(kCodedSize);
46 constexpr gfx::Size kNaturalSize = kCodedSize;
48 ACTION_P(ReturnBuffer, buffer) {
49 arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
52 MATCHER(ContainsFailedToSendLog, "") {
53 return CONTAINS_STRING(arg, "Failed to send");
56 MATCHER(ContainsFailedToDecode, "") {
57 return CONTAINS_STRING(arg, "failed to decode");
60 class FFmpegVideoDecoderTest : public testing::Test {
62 FFmpegVideoDecoderTest()
63 : decoder_(std::make_unique<FFmpegVideoDecoder>(&media_log_)) {
64 // Initialize various test buffers.
65 frame_buffer_ = std::make_unique<uint8_t[]>(kCodedSize.GetArea());
66 end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
67 i_frame_buffer_ = ReadTestDataFile("h264-320x180-frame-0");
68 corrupt_i_frame_buffer_ = ReadTestDataFile("h264-320x180-frame-0");
69 memset(corrupt_i_frame_buffer_->writable_data(), 0,
70 corrupt_i_frame_buffer_->data_size() / 2);
73 FFmpegVideoDecoderTest(const FFmpegVideoDecoderTest&) = delete;
74 FFmpegVideoDecoderTest& operator=(const FFmpegVideoDecoderTest&) = delete;
76 ~FFmpegVideoDecoderTest() override { Destroy(); }
79 InitializeWithConfig(TestVideoConfig::Normal(VideoCodec::kH264));
82 void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
85 config, false, nullptr,
87 [](bool success, DecoderStatus status) {
88 EXPECT_EQ(status.is_ok(), success);
91 base::BindRepeating(&FFmpegVideoDecoderTest::FrameReady,
92 base::Unretained(this)),
93 base::NullCallback());
94 base::RunLoop().RunUntilIdle();
97 void InitializeWithConfig(const VideoDecoderConfig& config) {
98 InitializeWithConfigWithResult(config, true);
101 void Reinitialize() {
102 InitializeWithConfig(TestVideoConfig::Large(VideoCodec::kH264));
106 decoder_->Reset(NewExpectedClosure());
107 base::RunLoop().RunUntilIdle();
112 base::RunLoop().RunUntilIdle();
115 // Sets up expectations and actions to put FFmpegVideoDecoder in an active
117 void EnterDecodingState() {
118 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
119 ASSERT_EQ(1U, output_frames_.size());
122 // Sets up expectations and actions to put FFmpegVideoDecoder in an end
124 void EnterEndOfStreamState() {
125 EXPECT_TRUE(DecodeSingleFrame(end_of_stream_buffer_).is_ok());
126 ASSERT_FALSE(output_frames_.empty());
129 typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
130 typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
132 // Decodes all buffers in |input_buffers| and push all successfully decoded
133 // output frames into |output_frames|.
134 // Returns the last decode status returned by the decoder.
135 DecoderStatus DecodeMultipleFrames(const InputBuffers& input_buffers) {
136 for (auto iter = input_buffers.begin(); iter != input_buffers.end();
138 DecoderStatus status = Decode(*iter);
139 switch (status.code()) {
140 case DecoderStatus::Codes::kOk:
142 case DecoderStatus::Codes::kAborted:
146 DCHECK(output_frames_.empty());
150 return DecoderStatus::Codes::kOk;
153 // Decodes the single compressed frame in |buffer| and writes the
154 // uncompressed output to |video_frame|. This method works with single
155 // and multithreaded decoders. End of stream buffers are used to trigger
156 // the frame to be returned in the multithreaded decoder case.
157 DecoderStatus DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer) {
158 InputBuffers input_buffers;
159 input_buffers.push_back(buffer);
160 input_buffers.push_back(end_of_stream_buffer_);
162 return DecodeMultipleFrames(input_buffers);
165 // Decodes |i_frame_buffer_| and then decodes the data contained in
166 // the file named |test_file_name|. This function expects both buffers
167 // to decode to frames that are the same size.
168 void DecodeIFrameThenTestFile(const std::string& test_file_name,
171 size_t expected_frames = 2u) {
173 scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
175 InputBuffers input_buffers;
176 input_buffers.push_back(i_frame_buffer_);
177 input_buffers.push_back(buffer);
178 input_buffers.push_back(end_of_stream_buffer_);
180 DecoderStatus status = DecodeMultipleFrames(input_buffers);
182 EXPECT_TRUE(status.is_ok());
183 ASSERT_EQ(expected_frames, output_frames_.size());
185 gfx::Size original_size = kVisibleRect.size();
186 EXPECT_EQ(original_size.width(),
187 output_frames_[0]->visible_rect().size().width());
188 EXPECT_EQ(original_size.height(),
189 output_frames_[0]->visible_rect().size().height());
190 EXPECT_EQ(expected_width,
191 output_frames_[1]->visible_rect().size().width());
192 EXPECT_EQ(expected_height,
193 output_frames_[1]->visible_rect().size().height());
196 DecoderStatus Decode(scoped_refptr<DecoderBuffer> buffer) {
197 DecoderStatus status;
198 EXPECT_CALL(*this, DecodeDone(_)).WillOnce(SaveArg<0>(&status));
200 decoder_->Decode(buffer, base::BindOnce(&FFmpegVideoDecoderTest::DecodeDone,
201 base::Unretained(this)));
203 base::RunLoop().RunUntilIdle();
208 void FrameReady(scoped_refptr<VideoFrame> frame) {
209 DCHECK(!frame->metadata().end_of_stream);
210 output_frames_.push_back(std::move(frame));
213 MOCK_METHOD1(DecodeDone, void(DecoderStatus));
215 StrictMock<MockMediaLog> media_log_;
217 base::test::SingleThreadTaskEnvironment task_environment_;
218 std::unique_ptr<FFmpegVideoDecoder> decoder_;
220 // Various buffers for testing.
221 std::unique_ptr<uint8_t[]> frame_buffer_;
222 scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
223 scoped_refptr<DecoderBuffer> i_frame_buffer_;
224 scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
226 OutputFrames output_frames_;
229 TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
233 TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
234 // Specify Theora w/o extra data so that avcodec_open2() fails.
235 VideoDecoderConfig config(VideoCodec::kTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
236 VideoDecoderConfig::AlphaMode::kIsOpaque,
237 VideoColorSpace(), kNoTransformation, kCodedSize,
238 kVisibleRect, kNaturalSize, EmptyExtraData(),
239 EncryptionScheme::kUnencrypted);
240 InitializeWithConfigWithResult(config, false);
243 TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
248 TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
250 EnterDecodingState();
254 TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
256 EnterDecodingState();
261 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
264 // Simulate decoding a single frame.
265 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
266 ASSERT_EQ(1U, output_frames_.size());
269 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_OOM) {
271 decoder_->force_allocation_error_for_testing();
273 EXPECT_FALSE(DecodeSingleFrame(i_frame_buffer_).is_ok());
274 EXPECT_TRUE(output_frames_.empty());
277 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
280 EXPECT_MEDIA_LOG(ContainsFailedToSendLog());
282 // The error is only raised on the second decode attempt, so we expect at
283 // least one successful decode but we don't expect valid frame to be decoded.
284 // During the second decode attempt an error is raised.
285 EXPECT_TRUE(Decode(corrupt_i_frame_buffer_).is_ok());
286 EXPECT_TRUE(output_frames_.empty());
287 EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
288 EXPECT_TRUE(output_frames_.empty());
290 // After a decode error occurred, all following decodes will return
291 // DecoderStatus::Codes::kFailed.
292 EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
293 EXPECT_TRUE(output_frames_.empty());
296 // A corrupt frame followed by an EOS buffer should raise a decode error.
297 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
300 EXPECT_MEDIA_LOG(ContainsFailedToDecode());
302 EXPECT_THAT(DecodeSingleFrame(corrupt_i_frame_buffer_),
303 IsDecodeErrorStatus());
306 // Decode |i_frame_buffer_| and then a smaller frame and verify the output size
308 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Smaller) {
309 DecodeIFrameThenTestFile("red-green.h264", 80, 128, /*expected_frames=*/4);
312 // Decode |i_frame_buffer_| and then a larger frame and verify the output size
314 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Larger) {
315 DecodeIFrameThenTestFile("bear-320x192-baseline-frame-0.h264", 320, 192);
318 // Test resetting when decoder has initialized but not decoded.
319 TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
324 // Test resetting when decoder has decoded single frame.
325 TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
327 EnterDecodingState();
331 // Test resetting when decoder has hit end of stream.
332 TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
334 EnterDecodingState();
335 EnterEndOfStreamState();
339 // Test destruction when decoder has initialized but not decoded.
340 TEST_F(FFmpegVideoDecoderTest, Destroy_Initialized) {
345 // Test destruction when decoder has decoded single frame.
346 TEST_F(FFmpegVideoDecoderTest, Destroy_Decoding) {
348 EnterDecodingState();
352 // Test destruction when decoder has hit end of stream.
353 TEST_F(FFmpegVideoDecoderTest, Destroy_EndOfStream) {
355 EnterDecodingState();
356 EnterEndOfStreamState();