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