1 // Copyright 2019 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.
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/hash/md5.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_piece.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "media/base/decoder_buffer.h"
18 #include "media/base/limits.h"
19 #include "media/base/mock_media_log.h"
20 #include "media/base/test_data_util.h"
21 #include "media/base/test_helpers.h"
22 #include "media/base/video_frame.h"
23 #include "media/ffmpeg/ffmpeg_common.h"
24 #include "media/filters/gav1_video_decoder.h"
25 #include "media/filters/in_memory_url_protocol.h"
26 #include "testing/gmock/include/gmock/gmock.h"
34 MATCHER(ContainsDecoderErrorLog, "") {
35 return CONTAINS_STRING(arg, "libgav1::Decoder::DequeueFrame failed");
38 // Similar to VideoFrame::HashFrameForTesting(), but uses visible_data() and
39 // visible_rect() instead of data() and coded_size() to determine the region to
42 // The VideoFrame objects created by Gav1VideoDecoder have extended pixels
43 // outside the visible_rect(). Those extended pixels are for libgav1 internal
44 // use and are not part of the actual video frames. Unlike
45 // VideoFrame::HashFrameForTesting(), this function excludes the extended pixels
46 // and hashes only the actual video frames.
47 void HashFrameVisibleRectForTesting(base::MD5Context* context,
48 const VideoFrame& frame) {
50 for (size_t plane = 0; plane < VideoFrame::NumPlanes(frame.format());
52 int rows = frame.Rows(plane, frame.format(), frame.visible_rect().height());
53 for (int row = 0; row < rows; ++row) {
55 frame.RowBytes(plane, frame.format(), frame.visible_rect().width());
56 base::MD5Update(context, base::StringPiece(reinterpret_cast<const char*>(
57 frame.visible_data(plane) +
58 frame.stride(plane) * row),
66 class Gav1VideoDecoderTest : public testing::Test {
68 Gav1VideoDecoderTest()
69 : decoder_(new Gav1VideoDecoder(&media_log_)),
70 i_frame_buffer_(ReadTestDataFile("av1-I-frame-320x240")) {}
72 Gav1VideoDecoderTest(const Gav1VideoDecoderTest&) = delete;
73 Gav1VideoDecoderTest& operator=(const Gav1VideoDecoderTest&) = delete;
75 ~Gav1VideoDecoderTest() override { Destroy(); }
78 InitializeWithConfig(TestVideoConfig::Normal(VideoCodec::kAV1));
81 void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
83 decoder_->Initialize(config, false, nullptr,
85 [](bool success, DecoderStatus status) {
86 EXPECT_EQ(status.is_ok(), success);
89 base::BindRepeating(&Gav1VideoDecoderTest::FrameReady,
90 base::Unretained(this)),
91 base::NullCallback());
92 base::RunLoop().RunUntilIdle();
95 void InitializeWithConfig(const VideoDecoderConfig& config) {
96 InitializeWithConfigWithResult(config, true);
100 InitializeWithConfig(TestVideoConfig::Large(VideoCodec::kAV1));
104 decoder_->Reset(NewExpectedClosure());
105 base::RunLoop().RunUntilIdle();
110 base::RunLoop().RunUntilIdle();
113 // Sets up expectations and actions to put Gav1VideoDecoder in an active
115 void ExpectDecodingState() {
116 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
117 ASSERT_EQ(1U, output_frames_.size());
120 // Sets up expectations and actions to put Gav1VideoDecoder in an end
122 void ExpectEndOfStreamState() {
123 EXPECT_TRUE(DecodeSingleFrame(DecoderBuffer::CreateEOSBuffer()).is_ok());
124 ASSERT_FALSE(output_frames_.empty());
127 using InputBuffers = std::vector<scoped_refptr<DecoderBuffer>>;
128 using OutputFrames = std::vector<scoped_refptr<VideoFrame>>;
130 // Decodes all buffers in |input_buffers| and push all successfully decoded
131 // output frames into |output_frames|. Returns the last decode status returned
133 DecoderStatus DecodeMultipleFrames(const InputBuffers& input_buffers) {
134 for (auto iter = input_buffers.begin(); iter != input_buffers.end();
136 DecoderStatus status = Decode(*iter);
137 switch (status.code()) {
138 case DecoderStatus::Codes::kOk:
140 case DecoderStatus::Codes::kAborted:
144 DCHECK(output_frames_.empty());
148 return DecoderStatus::Codes::kOk;
151 // Decodes the single compressed frame in |buffer|.
152 DecoderStatus DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer) {
153 InputBuffers input_buffers;
154 input_buffers.push_back(std::move(buffer));
155 return DecodeMultipleFrames(input_buffers);
158 // Decodes |i_frame_buffer_| and then decodes the data contained in the file
159 // named |test_file_name|. This function expects both buffers to decode to
160 // frames that are the same size.
161 void DecodeIFrameThenTestFile(const std::string& test_file_name,
162 const gfx::Size& expected_size) {
164 scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
166 InputBuffers input_buffers;
167 input_buffers.push_back(i_frame_buffer_);
168 input_buffers.push_back(buffer);
169 input_buffers.push_back(DecoderBuffer::CreateEOSBuffer());
171 EXPECT_TRUE(DecodeMultipleFrames(input_buffers).is_ok());
172 ASSERT_EQ(2U, output_frames_.size());
174 gfx::Size original_size = TestVideoConfig::NormalCodedSize();
175 EXPECT_EQ(original_size.width(),
176 output_frames_[0]->visible_rect().size().width());
177 EXPECT_EQ(original_size.height(),
178 output_frames_[0]->visible_rect().size().height());
179 EXPECT_EQ(expected_size.width(),
180 output_frames_[1]->visible_rect().size().width());
181 EXPECT_EQ(expected_size.height(),
182 output_frames_[1]->visible_rect().size().height());
185 DecoderStatus Decode(scoped_refptr<DecoderBuffer> buffer) {
186 DecoderStatus status;
187 EXPECT_CALL(*this, DecodeDone(_)).WillOnce(testing::SaveArg<0>(&status));
189 decoder_->Decode(std::move(buffer),
190 base::BindOnce(&Gav1VideoDecoderTest::DecodeDone,
191 base::Unretained(this)));
192 base::RunLoop().RunUntilIdle();
197 void FrameReady(scoped_refptr<VideoFrame> frame) {
198 DCHECK(!frame->metadata().end_of_stream);
199 output_frames_.push_back(std::move(frame));
202 std::string GetVideoFrameHash(const VideoFrame& frame) {
203 base::MD5Context md5_context;
204 base::MD5Init(&md5_context);
205 HashFrameVisibleRectForTesting(&md5_context, frame);
206 base::MD5Digest digest;
207 base::MD5Final(&digest, &md5_context);
208 return base::MD5DigestToBase16(digest);
211 MOCK_METHOD1(DecodeDone, void(DecoderStatus));
213 testing::StrictMock<MockMediaLog> media_log_;
215 base::test::SingleThreadTaskEnvironment task_environment_;
216 std::unique_ptr<Gav1VideoDecoder> decoder_;
218 scoped_refptr<DecoderBuffer> i_frame_buffer_;
219 OutputFrames output_frames_;
222 TEST_F(Gav1VideoDecoderTest, Initialize_Normal) {
226 TEST_F(Gav1VideoDecoderTest, Reinitialize_Normal) {
231 TEST_F(Gav1VideoDecoderTest, Reinitialize_AfterDecodeFrame) {
233 ExpectDecodingState();
237 TEST_F(Gav1VideoDecoderTest, Reinitialize_AfterReset) {
239 ExpectDecodingState();
244 TEST_F(Gav1VideoDecoderTest, DecodeFrame_Normal) {
247 // Simulate decoding a single frame.
248 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
249 ASSERT_EQ(1U, output_frames_.size());
251 const auto& frame = output_frames_.front();
252 EXPECT_EQ(PIXEL_FORMAT_I420, frame->format());
253 EXPECT_EQ("589dc641b7742ffe7a2b0d4c16aa3e86", GetVideoFrameHash(*frame));
256 TEST_F(Gav1VideoDecoderTest, DecodeFrame_8bitMono) {
259 DecodeSingleFrame(ReadTestDataFile("av1-monochrome-I-frame-320x240-8bpp"))
261 ASSERT_EQ(1U, output_frames_.size());
263 const auto& frame = output_frames_.front();
264 EXPECT_EQ(PIXEL_FORMAT_I420, frame->format());
265 EXPECT_EQ("eeba03dcc9c22c4632bf74b481db36b2", GetVideoFrameHash(*frame));
268 TEST_F(Gav1VideoDecoderTest, DecodeFrame_10bitMono) {
270 EXPECT_TRUE(DecodeSingleFrame(
271 ReadTestDataFile("av1-monochrome-I-frame-320x240-10bpp"))
273 ASSERT_EQ(1U, output_frames_.size());
275 const auto& frame = output_frames_.front();
276 EXPECT_EQ(PIXEL_FORMAT_YUV420P10, frame->format());
277 EXPECT_EQ("026c1fed9e161f09d816ac7278458a80", GetVideoFrameHash(*frame));
280 // libgav1 does not support bit depth 12.
281 TEST_F(Gav1VideoDecoderTest, DISABLED_DecodeFrame_12bitMono) {
283 EXPECT_TRUE(DecodeSingleFrame(
284 ReadTestDataFile("av1-monochrome-I-frame-320x240-12bpp"))
286 ASSERT_EQ(1U, output_frames_.size());
288 const auto& frame = output_frames_.front();
289 EXPECT_EQ(PIXEL_FORMAT_YUV420P12, frame->format());
290 EXPECT_EQ("32115092dc00fbe86823b0b714a0f63e", GetVideoFrameHash(*frame));
293 // Decode |i_frame_buffer_| and then a frame with a larger width and verify
294 // the output size was adjusted.
295 TEST_F(Gav1VideoDecoderTest, DecodeFrame_LargerWidth) {
296 DecodeIFrameThenTestFile("av1-I-frame-1280x720", gfx::Size(1280, 720));
299 // Decode a VP9 frame which should trigger a decoder error.
300 TEST_F(Gav1VideoDecoderTest, DecodeFrame_Error) {
302 EXPECT_MEDIA_LOG(ContainsDecoderErrorLog());
303 DecodeSingleFrame(ReadTestDataFile("vp9-I-frame-320x240"));
306 // Test resetting when decoder has initialized but not decoded.
307 TEST_F(Gav1VideoDecoderTest, Reset_Initialized) {
312 // Test resetting when decoder has decoded single frame.
313 TEST_F(Gav1VideoDecoderTest, Reset_Decoding) {
315 ExpectDecodingState();
319 // Test resetting when decoder has hit end of stream.
320 TEST_F(Gav1VideoDecoderTest, Reset_EndOfStream) {
322 ExpectDecodingState();
323 ExpectEndOfStreamState();
327 // Test destruction when decoder has initialized but not decoded.
328 TEST_F(Gav1VideoDecoderTest, Destroy_Initialized) {
333 // Test destruction when decoder has decoded single frame.
334 TEST_F(Gav1VideoDecoderTest, Destroy_Decoding) {
336 ExpectDecodingState();
340 // Test destruction when decoder has hit end of stream.
341 TEST_F(Gav1VideoDecoderTest, Destroy_EndOfStream) {
343 ExpectDecodingState();
344 ExpectEndOfStreamState();
348 TEST_F(Gav1VideoDecoderTest, FrameValidAfterPoolDestruction) {
350 Decode(i_frame_buffer_);
353 ASSERT_FALSE(output_frames_.empty());
355 // Write to the Y plane. The memory tools should detect a
356 // use-after-free if the storage was actually removed by pool destruction.
357 memset(output_frames_.front()->writable_data(VideoFrame::kYPlane), 0xff,
358 output_frames_.front()->rows(VideoFrame::kYPlane) *
359 output_frames_.front()->stride(VideoFrame::kYPlane));