Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: media/filters/ffmpeg_video_decoder_unittest.cc

Issue 297553002: Add callback in VideoDecoder and AudioDecoder to return decoded frames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698