1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6 #include <vector>
7
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/run_loop.h"
11 #include "base/test/task_environment.h"
12 #include "build/build_config.h"
13 #include "media/base/decoder_buffer.h"
14 #include "media/base/limits.h"
15 #include "media/base/test_data_util.h"
16 #include "media/base/test_helpers.h"
17 #include "media/base/video_frame.h"
18 #include "media/ffmpeg/ffmpeg_common.h"
19 #include "media/filters/in_memory_url_protocol.h"
20 #include "media/filters/vpx_video_decoder.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22
23 using ::testing::_;
24
25 namespace media {
26
27 class VpxVideoDecoderTest : public testing::Test {
28 public:
VpxVideoDecoderTest()29 VpxVideoDecoderTest()
30 : decoder_(new VpxVideoDecoder()),
31 i_frame_buffer_(ReadTestDataFile("vp9-I-frame-320x240")) {}
32
~VpxVideoDecoderTest()33 ~VpxVideoDecoderTest() override { Destroy(); }
34
Initialize()35 void Initialize() {
36 InitializeWithConfig(TestVideoConfig::Normal(kCodecVP9));
37 }
38
InitializeWithConfigWithResult(const VideoDecoderConfig & config,bool success)39 void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
40 bool success) {
41 decoder_->Initialize(config, false, nullptr,
42 base::BindOnce(
43 [](bool success, Status status) {
44 EXPECT_EQ(status.is_ok(), success);
45 },
46 success),
47 base::BindRepeating(&VpxVideoDecoderTest::FrameReady,
48 base::Unretained(this)),
49 base::NullCallback());
50 base::RunLoop().RunUntilIdle();
51 }
52
InitializeWithConfig(const VideoDecoderConfig & config)53 void InitializeWithConfig(const VideoDecoderConfig& config) {
54 InitializeWithConfigWithResult(config, true);
55 }
56
Reinitialize()57 void Reinitialize() {
58 InitializeWithConfig(TestVideoConfig::Large(kCodecVP9));
59 }
60
Reset()61 void Reset() {
62 decoder_->Reset(NewExpectedClosure());
63 base::RunLoop().RunUntilIdle();
64 }
65
Destroy()66 void Destroy() {
67 decoder_.reset();
68 base::RunLoop().RunUntilIdle();
69 }
70
71 // Sets up expectations and actions to put VpxVideoDecoder in an active
72 // decoding state.
ExpectDecodingState()73 void ExpectDecodingState() {
74 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
75 ASSERT_EQ(1U, output_frames_.size());
76 }
77
78 // Sets up expectations and actions to put VpxVideoDecoder in an end
79 // of stream state.
ExpectEndOfStreamState()80 void ExpectEndOfStreamState() {
81 EXPECT_TRUE(DecodeSingleFrame(DecoderBuffer::CreateEOSBuffer()).is_ok());
82 ASSERT_FALSE(output_frames_.empty());
83 }
84
85 using InputBuffers = std::vector<scoped_refptr<DecoderBuffer>>;
86 using OutputFrames = std::vector<scoped_refptr<VideoFrame>>;
87
88 // Decodes all buffers in |input_buffers| and push all successfully decoded
89 // output frames into |output_frames|.
90 // Returns the last decode status returned by the decoder.
DecodeMultipleFrames(const InputBuffers & input_buffers)91 Status DecodeMultipleFrames(const InputBuffers& input_buffers) {
92 for (auto iter = input_buffers.begin(); iter != input_buffers.end();
93 ++iter) {
94 Status status = Decode(*iter);
95 switch (status.code()) {
96 case StatusCode::kOk:
97 break;
98 case StatusCode::kAborted:
99 NOTREACHED();
100 FALLTHROUGH;
101 default:
102 DCHECK(output_frames_.empty());
103 return status;
104 }
105 }
106 return OkStatus();
107 }
108
109 // Decodes the single compressed frame in |buffer| and writes the
110 // uncompressed output to |video_frame|. This method works with single
111 // and multithreaded decoders. End of stream buffers are used to trigger
112 // the frame to be returned in the multithreaded decoder case.
DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer)113 Status DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer) {
114 InputBuffers input_buffers;
115 input_buffers.push_back(std::move(buffer));
116 input_buffers.push_back(DecoderBuffer::CreateEOSBuffer());
117
118 return DecodeMultipleFrames(input_buffers);
119 }
120
121 // Decodes |i_frame_buffer_| and then decodes the data contained in
122 // the file named |test_file_name|. This function expects both buffers
123 // to decode to frames that are the same size.
DecodeIFrameThenTestFile(const std::string & test_file_name,const gfx::Size & expected_size)124 void DecodeIFrameThenTestFile(const std::string& test_file_name,
125 const gfx::Size& expected_size) {
126 Initialize();
127 scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
128
129 InputBuffers input_buffers;
130 input_buffers.push_back(i_frame_buffer_);
131 input_buffers.push_back(buffer);
132 input_buffers.push_back(DecoderBuffer::CreateEOSBuffer());
133
134 Status status = DecodeMultipleFrames(input_buffers);
135
136 EXPECT_TRUE(status.is_ok());
137 ASSERT_EQ(2U, output_frames_.size());
138
139 gfx::Size original_size = TestVideoConfig::NormalCodedSize();
140 EXPECT_EQ(original_size.width(),
141 output_frames_[0]->visible_rect().size().width());
142 EXPECT_EQ(original_size.height(),
143 output_frames_[0]->visible_rect().size().height());
144 EXPECT_EQ(expected_size.width(),
145 output_frames_[1]->visible_rect().size().width());
146 EXPECT_EQ(expected_size.height(),
147 output_frames_[1]->visible_rect().size().height());
148 }
149
Decode(scoped_refptr<DecoderBuffer> buffer)150 Status Decode(scoped_refptr<DecoderBuffer> buffer) {
151 Status status;
152 EXPECT_CALL(*this, DecodeDone(_)).WillOnce(testing::SaveArg<0>(&status));
153
154 decoder_->Decode(std::move(buffer),
155 base::BindOnce(&VpxVideoDecoderTest::DecodeDone,
156 base::Unretained(this)));
157 base::RunLoop().RunUntilIdle();
158
159 return status;
160 }
161
FrameReady(scoped_refptr<VideoFrame> frame)162 void FrameReady(scoped_refptr<VideoFrame> frame) {
163 DCHECK(!frame->metadata()->end_of_stream);
164 output_frames_.push_back(std::move(frame));
165 }
166
167 MOCK_METHOD1(DecodeDone, void(Status));
168
169 base::test::TaskEnvironment task_env_;
170 std::unique_ptr<VideoDecoder> decoder_;
171
172 scoped_refptr<DecoderBuffer> i_frame_buffer_;
173 OutputFrames output_frames_;
174
175 private:
176 DISALLOW_COPY_AND_ASSIGN(VpxVideoDecoderTest);
177 };
178
TEST_F(VpxVideoDecoderTest,Initialize_Normal)179 TEST_F(VpxVideoDecoderTest, Initialize_Normal) {
180 Initialize();
181 }
182
TEST_F(VpxVideoDecoderTest,Reinitialize_AfterReset)183 TEST_F(VpxVideoDecoderTest, Reinitialize_AfterReset) {
184 Initialize();
185 ExpectDecodingState();
186 Reset();
187 Reinitialize();
188 }
189
TEST_F(VpxVideoDecoderTest,DecodeFrame_Normal)190 TEST_F(VpxVideoDecoderTest, DecodeFrame_Normal) {
191 Initialize();
192
193 // Simulate decoding a single frame.
194 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
195 ASSERT_EQ(1U, output_frames_.size());
196 }
197
198 // Decode |i_frame_buffer_| and then a frame with a larger width and verify
199 // the output size was adjusted.
TEST_F(VpxVideoDecoderTest,DecodeFrame_LargerWidth)200 TEST_F(VpxVideoDecoderTest, DecodeFrame_LargerWidth) {
201 DecodeIFrameThenTestFile("vp9-I-frame-1280x720", gfx::Size(1280, 720));
202 }
203
204 // Decode |i_frame_buffer_| and then a frame with a larger width and verify
205 // the output size was adjusted.
TEST_F(VpxVideoDecoderTest,Offloaded_DecodeFrame_LargerWidth)206 TEST_F(VpxVideoDecoderTest, Offloaded_DecodeFrame_LargerWidth) {
207 decoder_.reset(new OffloadingVpxVideoDecoder());
208 DecodeIFrameThenTestFile("vp9-I-frame-1280x720", gfx::Size(1280, 720));
209 }
210
211 // Test resetting when decoder has initialized but not decoded.
TEST_F(VpxVideoDecoderTest,Reset_Initialized)212 TEST_F(VpxVideoDecoderTest, Reset_Initialized) {
213 Initialize();
214 Reset();
215 }
216
217 // Test resetting when decoder has decoded single frame.
TEST_F(VpxVideoDecoderTest,Reset_Decoding)218 TEST_F(VpxVideoDecoderTest, Reset_Decoding) {
219 Initialize();
220 ExpectDecodingState();
221 Reset();
222 }
223
224 // Test resetting when decoder has hit end of stream.
TEST_F(VpxVideoDecoderTest,Reset_EndOfStream)225 TEST_F(VpxVideoDecoderTest, Reset_EndOfStream) {
226 Initialize();
227 ExpectDecodingState();
228 ExpectEndOfStreamState();
229 Reset();
230 }
231
232 // Test destruction when decoder has initialized but not decoded.
TEST_F(VpxVideoDecoderTest,Destroy_Initialized)233 TEST_F(VpxVideoDecoderTest, Destroy_Initialized) {
234 Initialize();
235 Destroy();
236 }
237
238 // Test destruction when decoder has decoded single frame.
TEST_F(VpxVideoDecoderTest,Destroy_Decoding)239 TEST_F(VpxVideoDecoderTest, Destroy_Decoding) {
240 Initialize();
241 ExpectDecodingState();
242 Destroy();
243 }
244
245 // Test destruction when decoder has hit end of stream.
TEST_F(VpxVideoDecoderTest,Destroy_EndOfStream)246 TEST_F(VpxVideoDecoderTest, Destroy_EndOfStream) {
247 Initialize();
248 ExpectDecodingState();
249 ExpectEndOfStreamState();
250 Destroy();
251 }
252
TEST_F(VpxVideoDecoderTest,SimpleFrameReuse)253 TEST_F(VpxVideoDecoderTest, SimpleFrameReuse) {
254 Initialize();
255 Decode(i_frame_buffer_);
256
257 ASSERT_EQ(1u, output_frames_.size());
258 scoped_refptr<VideoFrame> frame = std::move(output_frames_.front());
259 const uint8_t* old_y_data = frame->data(VideoFrame::kYPlane);
260 output_frames_.pop_back();
261
262 // Clear frame reference to return the frame to the pool.
263 frame = nullptr;
264
265 // Since we're decoding I-frames which are marked as having dependent frames,
266 // libvpx will still have a ref on the previous buffer. So verify we see an
267 // increase to two frames.
268 Decode(i_frame_buffer_);
269 EXPECT_NE(old_y_data, output_frames_.front()->data(VideoFrame::kYPlane));
270
271 // Issuing another decode should reuse the first buffer now that the refs have
272 // been dropped by the previous decode.
273 Decode(i_frame_buffer_);
274
275 ASSERT_EQ(2u, output_frames_.size());
276 EXPECT_EQ(old_y_data, output_frames_.back()->data(VideoFrame::kYPlane));
277 }
278
TEST_F(VpxVideoDecoderTest,SimpleFormatChange)279 TEST_F(VpxVideoDecoderTest, SimpleFormatChange) {
280 scoped_refptr<DecoderBuffer> large_frame =
281 ReadTestDataFile("vp9-I-frame-1280x720");
282
283 Initialize();
284 Decode(i_frame_buffer_);
285 Decode(i_frame_buffer_);
286 output_frames_.clear();
287 base::RunLoop().RunUntilIdle();
288 Decode(large_frame);
289 }
290
TEST_F(VpxVideoDecoderTest,FrameValidAfterPoolDestruction)291 TEST_F(VpxVideoDecoderTest, FrameValidAfterPoolDestruction) {
292 Initialize();
293 Decode(i_frame_buffer_);
294 Destroy();
295
296 // Write to the Y plane. The memory tools should detect a
297 // use-after-free if the storage was actually removed by pool destruction.
298 memset(output_frames_.front()->data(VideoFrame::kYPlane), 0xff,
299 output_frames_.front()->rows(VideoFrame::kYPlane) *
300 output_frames_.front()->stride(VideoFrame::kYPlane));
301 }
302
303 // The test stream uses profile 2, which needs high bit depth support in libvpx.
304 // On ARM we fail to decode the final, duplicate frame, so there is no point in
305 // running this test (https://crbug.com/864458).
306 #if !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY)
TEST_F(VpxVideoDecoderTest,MemoryPoolAllowsMultipleDisplay)307 TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) {
308 // Initialize with dummy data, we could read it from the test clip, but it's
309 // not necessary for this test.
310 Initialize();
311
312 scoped_refptr<DecoderBuffer> data =
313 ReadTestDataFile("vp9-duplicate-frame.webm");
314 InMemoryUrlProtocol protocol(data->data(), data->data_size(), false);
315 FFmpegGlue glue(&protocol);
316 ASSERT_TRUE(glue.OpenContext());
317
318 AVPacket packet = {};
319 while (av_read_frame(glue.format_context(), &packet) >= 0) {
320 Status decode_status =
321 Decode(DecoderBuffer::CopyFrom(packet.data, packet.size));
322 av_packet_unref(&packet);
323 if (!decode_status.is_ok())
324 break;
325 }
326
327 ASSERT_EQ(output_frames_.size(), 26u);
328
329 // The final frame is a duplicate of the third-from-final one.
330 scoped_refptr<VideoFrame> last_frame = output_frames_[25];
331 scoped_refptr<VideoFrame> dupe_frame = output_frames_[23];
332
333 EXPECT_EQ(last_frame->data(VideoFrame::kYPlane),
334 dupe_frame->data(VideoFrame::kYPlane));
335 EXPECT_EQ(last_frame->data(VideoFrame::kUPlane),
336 dupe_frame->data(VideoFrame::kUPlane));
337 EXPECT_EQ(last_frame->data(VideoFrame::kVPlane),
338 dupe_frame->data(VideoFrame::kVPlane));
339
340 // This will release all frames held by the memory pool, but should not
341 // release |last_frame| since we still have a ref despite sharing the same
342 // memory as |dupe_frame|.
343 output_frames_.clear();
344 dupe_frame = nullptr;
345 Destroy();
346
347 // ASAN will be very unhappy with this line if the above is incorrect.
348 memset(last_frame->data(VideoFrame::kYPlane), 0,
349 last_frame->row_bytes(VideoFrame::kYPlane));
350 }
351 #endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY)
352
353 } // namespace media
354