OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <list> | |
5 #include <string> | 6 #include <string> |
6 #include <vector> | 7 #include <vector> |
7 | 8 |
8 #include "base/bind.h" | 9 #include "base/bind.h" |
9 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
10 #include "base/memory/singleton.h" | 11 #include "base/memory/singleton.h" |
11 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
12 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
13 #include "media/base/decoder_buffer.h" | 14 #include "media/base/decoder_buffer.h" |
14 #include "media/base/gmock_callback_support.h" | 15 #include "media/base/gmock_callback_support.h" |
15 #include "media/base/limits.h" | 16 #include "media/base/limits.h" |
16 #include "media/base/mock_filters.h" | 17 #include "media/base/mock_filters.h" |
17 #include "media/base/test_data_util.h" | 18 #include "media/base/test_data_util.h" |
18 #include "media/base/test_helpers.h" | 19 #include "media/base/test_helpers.h" |
19 #include "media/base/video_decoder.h" | 20 #include "media/base/video_decoder.h" |
20 #include "media/base/video_frame.h" | 21 #include "media/base/video_frame.h" |
21 #include "media/base/video_util.h" | 22 #include "media/base/video_util.h" |
22 #include "media/ffmpeg/ffmpeg_common.h" | 23 #include "media/ffmpeg/ffmpeg_common.h" |
23 #include "media/filters/ffmpeg_glue.h" | 24 #include "media/filters/ffmpeg_glue.h" |
24 #include "media/filters/ffmpeg_video_decoder.h" | 25 #include "media/filters/ffmpeg_video_decoder.h" |
25 #include "testing/gmock/include/gmock/gmock.h" | 26 #include "testing/gmock/include/gmock/gmock.h" |
26 | 27 |
27 using ::testing::_; | 28 using ::testing::_; |
28 using ::testing::AtLeast; | 29 using ::testing::AtLeast; |
30 using ::testing::AtMost; | |
29 using ::testing::InSequence; | 31 using ::testing::InSequence; |
30 using ::testing::IsNull; | 32 using ::testing::IsNull; |
31 using ::testing::Return; | 33 using ::testing::Return; |
32 using ::testing::SaveArg; | 34 using ::testing::SaveArg; |
33 using ::testing::StrictMock; | 35 using ::testing::StrictMock; |
34 | 36 |
35 namespace media { | 37 namespace media { |
36 | 38 |
37 static const VideoFrame::Format kVideoFormat = VideoFrame::YV12; | 39 static const VideoFrame::Format kVideoFormat = VideoFrame::YV12; |
38 static const gfx::Size kCodedSize(320, 240); | 40 static const gfx::Size kCodedSize(320, 240); |
39 static const gfx::Rect kVisibleRect(320, 240); | 41 static const gfx::Rect kVisibleRect(320, 240); |
40 static const gfx::Size kNaturalSize(320, 240); | 42 static const gfx::Size kNaturalSize(320, 240); |
41 | 43 |
42 ACTION_P(ReturnBuffer, buffer) { | 44 ACTION_P(ReturnBuffer, buffer) { |
43 arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); | 45 arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); |
44 } | 46 } |
45 | 47 |
48 ACTION_P(PushBackTo, output) { | |
49 output->push_back(arg0); | |
50 } | |
51 | |
46 class FFmpegVideoDecoderTest : public testing::Test { | 52 class FFmpegVideoDecoderTest : public testing::Test { |
47 public: | 53 public: |
48 FFmpegVideoDecoderTest() | 54 FFmpegVideoDecoderTest() |
49 : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())), | 55 : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())), |
50 decode_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady, | 56 decode_cb_(base::Bind(&FFmpegVideoDecoderTest::DecodeDone, |
51 base::Unretained(this))) { | 57 base::Unretained(this))) { |
52 FFmpegGlue::InitializeFFmpeg(); | 58 FFmpegGlue::InitializeFFmpeg(); |
53 | 59 |
54 // Initialize various test buffers. | 60 // Initialize various test buffers. |
55 frame_buffer_.reset(new uint8[kCodedSize.GetArea()]); | 61 frame_buffer_.reset(new uint8[kCodedSize.GetArea()]); |
56 end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer(); | 62 end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer(); |
57 i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240"); | 63 i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240"); |
58 corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame"); | 64 corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame"); |
59 } | 65 } |
60 | 66 |
61 virtual ~FFmpegVideoDecoderTest() { | 67 virtual ~FFmpegVideoDecoderTest() { |
62 Stop(); | 68 Stop(); |
63 } | 69 } |
64 | 70 |
65 void Initialize() { | 71 void Initialize() { |
66 InitializeWithConfig(TestVideoConfig::Normal()); | 72 InitializeWithConfig(TestVideoConfig::Normal()); |
67 } | 73 } |
68 | 74 |
69 void InitializeWithConfigAndStatus(const VideoDecoderConfig& config, | 75 void InitializeWithConfigAndStatus(const VideoDecoderConfig& config, |
70 PipelineStatus status) { | 76 PipelineStatus status) { |
71 decoder_->Initialize(config, false, NewExpectedStatusCB(status)); | 77 decoder_->Initialize(config, false, NewExpectedStatusCB(status), |
78 base::Bind(&FFmpegVideoDecoderTest::FrameReady, | |
79 base::Unretained(this))); | |
72 message_loop_.RunUntilIdle(); | 80 message_loop_.RunUntilIdle(); |
73 } | 81 } |
74 | 82 |
75 void InitializeWithConfig(const VideoDecoderConfig& config) { | 83 void InitializeWithConfig(const VideoDecoderConfig& config) { |
76 InitializeWithConfigAndStatus(config, PIPELINE_OK); | 84 InitializeWithConfigAndStatus(config, PIPELINE_OK); |
77 } | 85 } |
78 | 86 |
79 void Reinitialize() { | 87 void Reinitialize() { |
80 InitializeWithConfig(TestVideoConfig::Large()); | 88 InitializeWithConfig(TestVideoConfig::Large()); |
81 } | 89 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
114 } | 122 } |
115 | 123 |
116 typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers; | 124 typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers; |
117 typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames; | 125 typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames; |
118 | 126 |
119 // Decodes all buffers in |input_buffers| and push all successfully decoded | 127 // Decodes all buffers in |input_buffers| and push all successfully decoded |
120 // output frames (excluding EOS frames) into |output_frames|. | 128 // output frames (excluding EOS frames) into |output_frames|. |
121 // Returns the last decode status returned by the decoder. | 129 // Returns the last decode status returned by the decoder. |
122 VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers, | 130 VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers, |
123 OutputFrames* output_frames) { | 131 OutputFrames* output_frames) { |
132 output_frames->clear(); | |
xhwang
2014/06/05 21:53:51
DCHECK(output_frames->empty())?
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
124 InputBuffers::const_iterator input_iter = input_buffers.begin(); | 133 InputBuffers::const_iterator input_iter = input_buffers.begin(); |
125 | 134 |
126 for (;;) { | 135 while (output_frames->empty() || !output_frames->back()->end_of_stream()) { |
127 // Prepare input buffer. | 136 // Prepare input buffer. |
128 scoped_refptr<DecoderBuffer> buffer; | 137 scoped_refptr<DecoderBuffer> buffer; |
129 if (input_iter != input_buffers.end()) { | 138 if (input_iter != input_buffers.end()) { |
130 buffer = *input_iter; | 139 buffer = *input_iter; |
131 ++input_iter; | 140 ++input_iter; |
132 } else { | 141 } else { |
133 buffer = end_of_stream_buffer_; | 142 buffer = end_of_stream_buffer_; |
134 } | 143 } |
135 | 144 |
136 VideoDecoder::Status status; | 145 VideoDecoder::Status status; |
137 scoped_refptr<VideoFrame> frame; | 146 Decode(buffer, &status, output_frames); |
138 Decode(buffer, &status, &frame); | |
139 | 147 |
140 switch (status) { | 148 switch (status) { |
141 case VideoDecoder::kOk: | 149 case VideoDecoder::kOk: |
142 DCHECK(frame); | 150 break; |
143 if (!frame->end_of_stream()) { | |
144 output_frames->push_back(frame); | |
145 continue; | |
146 } else { // EOS | |
147 return status; | |
148 } | |
149 case VideoDecoder::kNotEnoughData: | |
150 DCHECK(!frame); | |
151 continue; | |
152 case VideoDecoder::kAborted: | 151 case VideoDecoder::kAborted: |
153 NOTREACHED(); | 152 NOTREACHED(); |
154 case VideoDecoder::kDecodeError: | 153 case VideoDecoder::kDecodeError: |
155 case VideoDecoder::kDecryptError: | 154 case VideoDecoder::kDecryptError: |
156 DCHECK(!frame); | 155 DCHECK(output_frames->empty()); |
157 return status; | 156 return status; |
158 } | 157 } |
159 } | 158 } |
159 | |
160 // Remove the EOS frame. | |
161 output_frames->erase(--output_frames->end()); | |
162 | |
163 return VideoDecoder::kOk; | |
160 } | 164 } |
161 | 165 |
162 // Decodes the single compressed frame in |buffer| and writes the | 166 // Decodes the single compressed frame in |buffer| and writes the |
163 // uncompressed output to |video_frame|. This method works with single | 167 // uncompressed output to |video_frame|. This method works with single |
164 // and multithreaded decoders. End of stream buffers are used to trigger | 168 // and multithreaded decoders. End of stream buffers are used to trigger |
165 // the frame to be returned in the multithreaded decoder case. | 169 // the frame to be returned in the multithreaded decoder case. |
166 void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer, | 170 void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer, |
167 VideoDecoder::Status* status, | 171 VideoDecoder::Status* status, |
168 scoped_refptr<VideoFrame>* video_frame) { | 172 scoped_refptr<VideoFrame>* video_frame) { |
xhwang
2014/06/05 21:53:51
should this be OutputFrames as well? In theory, we
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
169 InputBuffers input_buffers; | 173 InputBuffers input_buffers; |
170 input_buffers.push_back(buffer); | 174 input_buffers.push_back(buffer); |
171 | 175 |
172 OutputFrames output_frames; | 176 OutputFrames output_frames; |
173 *status = DecodeMultipleFrames(input_buffers, &output_frames); | 177 *status = DecodeMultipleFrames(input_buffers, &output_frames); |
174 | 178 |
175 if (*status != VideoDecoder::kOk) | 179 if (*status != VideoDecoder::kOk) |
176 return; | 180 return; |
177 | 181 |
178 ASSERT_LE(output_frames.size(), 1U); | 182 ASSERT_LE(output_frames.size(), 1U); |
(...skipping 29 matching lines...) Expand all Loading... | |
208 EXPECT_EQ(original_size.height(), | 212 EXPECT_EQ(original_size.height(), |
209 output_frames[0]->visible_rect().size().height()); | 213 output_frames[0]->visible_rect().size().height()); |
210 EXPECT_EQ(expected_width, | 214 EXPECT_EQ(expected_width, |
211 output_frames[1]->visible_rect().size().width()); | 215 output_frames[1]->visible_rect().size().width()); |
212 EXPECT_EQ(expected_height, | 216 EXPECT_EQ(expected_height, |
213 output_frames[1]->visible_rect().size().height()); | 217 output_frames[1]->visible_rect().size().height()); |
214 } | 218 } |
215 | 219 |
216 void Decode(const scoped_refptr<DecoderBuffer>& buffer, | 220 void Decode(const scoped_refptr<DecoderBuffer>& buffer, |
217 VideoDecoder::Status* status, | 221 VideoDecoder::Status* status, |
218 scoped_refptr<VideoFrame>* video_frame) { | 222 OutputFrames* video_frames) { |
219 EXPECT_CALL(*this, FrameReady(_, _)) | 223 EXPECT_CALL(*this, FrameReady(_)) |
220 .WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame))); | 224 .WillRepeatedly(PushBackTo(video_frames)); |
225 EXPECT_CALL(*this, DecodeDone(_)).WillOnce(SaveArg<0>(status)); | |
221 | 226 |
222 decoder_->Decode(buffer, decode_cb_); | 227 decoder_->Decode(buffer, decode_cb_); |
223 | 228 |
224 message_loop_.RunUntilIdle(); | 229 message_loop_.RunUntilIdle(); |
225 } | 230 } |
226 | 231 |
227 MOCK_METHOD2(FrameReady, void(VideoDecoder::Status, | 232 MOCK_METHOD1(FrameReady, void(const scoped_refptr<VideoFrame>&)); |
228 const scoped_refptr<VideoFrame>&)); | 233 MOCK_METHOD1(DecodeDone, void(VideoDecoder::Status)); |
229 | 234 |
230 base::MessageLoop message_loop_; | 235 base::MessageLoop message_loop_; |
231 scoped_ptr<FFmpegVideoDecoder> decoder_; | 236 scoped_ptr<FFmpegVideoDecoder> decoder_; |
232 | 237 |
233 VideoDecoder::DecodeCB decode_cb_; | 238 VideoDecoder::DecodeCB decode_cb_; |
234 | 239 |
235 // Various buffers for testing. | 240 // Various buffers for testing. |
236 scoped_ptr<uint8_t[]> frame_buffer_; | 241 scoped_ptr<uint8_t[]> frame_buffer_; |
237 scoped_refptr<DecoderBuffer> end_of_stream_buffer_; | 242 scoped_refptr<DecoderBuffer> end_of_stream_buffer_; |
238 scoped_refptr<DecoderBuffer> i_frame_buffer_; | 243 scoped_refptr<DecoderBuffer> i_frame_buffer_; |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
384 ASSERT_EQ(2U, output_frames.size()); | 389 ASSERT_EQ(2U, output_frames.size()); |
385 | 390 |
386 EXPECT_FALSE(output_frames[0]->end_of_stream()); | 391 EXPECT_FALSE(output_frames[0]->end_of_stream()); |
387 EXPECT_FALSE(output_frames[1]->end_of_stream()); | 392 EXPECT_FALSE(output_frames[1]->end_of_stream()); |
388 } | 393 } |
389 | 394 |
390 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) { | 395 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) { |
391 Initialize(); | 396 Initialize(); |
392 | 397 |
393 VideoDecoder::Status status; | 398 VideoDecoder::Status status; |
394 scoped_refptr<VideoFrame> frame; | 399 OutputFrames frames; |
395 | 400 |
396 // The error is only raised on the second decode attempt, so we expect at | 401 // The error is only raised on the second decode attempt, so we expect at |
397 // least one successful decode but we don't expect valid frame to be decoded. | 402 // least one successful decode but we don't expect valid frame to be decoded. |
398 // During the second decode attempt an error is raised. | 403 // During the second decode attempt an error is raised. |
399 Decode(corrupt_i_frame_buffer_, &status, &frame); | 404 Decode(corrupt_i_frame_buffer_, &status, &frames); |
400 DCHECK(!frame); | 405 EXPECT_TRUE(frames.empty()); |
401 DCHECK_EQ(VideoDecoder::kNotEnoughData, status); | 406 EXPECT_EQ(VideoDecoder::kOk, status); |
402 Decode(i_frame_buffer_, &status, &frame); | 407 Decode(i_frame_buffer_, &status, &frames); |
403 DCHECK(!frame); | 408 EXPECT_TRUE(frames.empty()); |
404 DCHECK_EQ(VideoDecoder::kDecodeError, status); | 409 EXPECT_EQ(VideoDecoder::kDecodeError, status); |
405 | 410 |
406 // After a decode error occurred, all following decodes will return | 411 // After a decode error occurred, all following decodes will return |
407 // kDecodeError. | 412 // kDecodeError. |
408 Decode(i_frame_buffer_, &status, &frame); | 413 Decode(i_frame_buffer_, &status, &frames); |
409 DCHECK(!frame); | 414 EXPECT_TRUE(frames.empty()); |
410 DCHECK_EQ(VideoDecoder::kDecodeError, status); | 415 EXPECT_EQ(VideoDecoder::kDecodeError, status); |
411 } | 416 } |
412 | 417 |
413 // Multi-threaded decoders have different behavior than single-threaded | 418 // Multi-threaded decoders have different behavior than single-threaded |
414 // decoders at the end of the stream. Multithreaded decoders hide errors | 419 // decoders at the end of the stream. Multithreaded decoders hide errors |
415 // that happen on the last |codec_context_->thread_count| frames to avoid | 420 // that happen on the last |codec_context_->thread_count| frames to avoid |
416 // prematurely signalling EOS. This test just exposes that behavior so we can | 421 // prematurely signalling EOS. This test just exposes that behavior so we can |
417 // detect if it changes. | 422 // detect if it changes. |
418 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) { | 423 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) { |
419 Initialize(); | 424 Initialize(); |
420 | 425 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
487 | 492 |
488 // Test stopping when decoder has hit end of stream. | 493 // Test stopping when decoder has hit end of stream. |
489 TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) { | 494 TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) { |
490 Initialize(); | 495 Initialize(); |
491 EnterDecodingState(); | 496 EnterDecodingState(); |
492 EnterEndOfStreamState(); | 497 EnterEndOfStreamState(); |
493 Stop(); | 498 Stop(); |
494 } | 499 } |
495 | 500 |
496 } // namespace media | 501 } // namespace media |
OLD | NEW |