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