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