OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 <d3d9.h> | |
6 #include <dxva2api.h> | |
7 #include <mfapi.h> | |
8 | |
9 #include "base/file_path.h" | 5 #include "base/file_path.h" |
10 #include "base/file_util.h" | 6 #include "base/file_util.h" |
11 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
12 #include "base/path_service.h" | 8 #include "base/path_service.h" |
13 #include "base/scoped_comptr_win.h" | 9 #include "base/ref_counted.h" |
14 #include "base/scoped_ptr.h" | 10 #include "base/scoped_ptr.h" |
15 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/time.h" |
16 #include "media/base/data_buffer.h" | 13 #include "media/base/data_buffer.h" |
17 #include "media/base/video_frame.h" | 14 #include "media/base/video_frame.h" |
18 #include "media/mf/d3d_util.h" | 15 #include "media/filters/video_decode_engine.h" |
19 #include "media/mf/file_reader_util.h" | 16 #include "media/mf/file_reader_util.h" |
20 #include "media/mf/mft_h264_decoder.h" | 17 #include "media/mf/mft_h264_decoder.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
22 | 19 |
| 20 using base::TimeDelta; |
| 21 |
23 namespace media { | 22 namespace media { |
24 | 23 |
25 static const int kDecoderMaxWidth = 1920; | 24 static const int kDecoderMaxWidth = 1920; |
26 static const int kDecoderMaxHeight = 1088; | 25 static const int kDecoderMaxHeight = 1088; |
27 | 26 |
28 class FakeMftReader { | 27 class BaseMftReader : public base::RefCountedThreadSafe<BaseMftReader> { |
| 28 public: |
| 29 virtual ~BaseMftReader() {} |
| 30 virtual void ReadCallback(scoped_refptr<DataBuffer>* input) = 0; |
| 31 }; |
| 32 |
| 33 class FakeMftReader : public BaseMftReader { |
29 public: | 34 public: |
30 FakeMftReader() : frames_remaining_(20) {} | 35 FakeMftReader() : frames_remaining_(20) {} |
31 explicit FakeMftReader(int count) : frames_remaining_(count) {} | 36 explicit FakeMftReader(int count) : frames_remaining_(count) {} |
32 ~FakeMftReader() {} | 37 virtual ~FakeMftReader() {} |
33 | 38 |
34 // Provides garbage input to the decoder. | 39 // Provides garbage input to the decoder. |
35 void ReadCallback(scoped_refptr<DataBuffer>* input) { | 40 virtual void ReadCallback(scoped_refptr<DataBuffer>* input) { |
36 if (frames_remaining_ > 0) { | 41 if (frames_remaining_ > 0) { |
37 int sz = 4096; | 42 int sz = 4096; |
38 uint8* buf = new uint8[sz]; | 43 uint8* buf = new uint8[sz]; |
39 memset(buf, 42, sz); | 44 memset(buf, 42, sz); |
40 *input = new DataBuffer(buf, sz); | 45 *input = new DataBuffer(buf, sz); |
41 (*input)->SetDuration(base::TimeDelta::FromMicroseconds(5000)); | 46 (*input)->SetDuration(base::TimeDelta::FromMicroseconds(5000)); |
42 (*input)->SetTimestamp( | 47 (*input)->SetTimestamp( |
43 base::TimeDelta::FromMicroseconds( | 48 base::TimeDelta::FromMicroseconds( |
44 50000000 - frames_remaining_ * 10000)); | 49 50000000 - frames_remaining_ * 10000)); |
45 --frames_remaining_; | 50 --frames_remaining_; |
46 } else { | 51 } else { |
47 // Emulate end of stream on the last "frame". | 52 // Emulate end of stream on the last "frame". |
48 *input = new DataBuffer(0); | 53 *input = new DataBuffer(0); |
49 } | 54 } |
50 } | 55 } |
51 int frames_remaining() const { return frames_remaining_; } | 56 int frames_remaining() const { return frames_remaining_; } |
52 | 57 |
53 private: | 58 private: |
54 int frames_remaining_; | 59 int frames_remaining_; |
55 }; | 60 }; |
56 | 61 |
57 class FakeMftRenderer : public base::RefCountedThreadSafe<FakeMftRenderer> { | 62 class FFmpegFileReaderWrapper : public BaseMftReader { |
58 public: | 63 public: |
59 explicit FakeMftRenderer(scoped_refptr<MftH264Decoder> decoder) | 64 FFmpegFileReaderWrapper() {} |
60 : decoder_(decoder), | 65 virtual ~FFmpegFileReaderWrapper() {} |
61 count_(0), | 66 bool InitReader(const std::string& filename) { |
62 flush_countdown_(0) { | 67 reader_.reset(new FFmpegFileReader(filename)); |
| 68 if (!reader_.get() || !reader_->Initialize()) { |
| 69 reader_.reset(); |
| 70 return false; |
| 71 } |
| 72 return true; |
63 } | 73 } |
64 | 74 virtual void ReadCallback(scoped_refptr<DataBuffer>* input) { |
65 virtual ~FakeMftRenderer() {} | 75 if (reader_.get()) { |
66 | 76 reader_->Read(input); |
67 virtual void WriteCallback(scoped_refptr<VideoFrame> frame) { | |
68 static_cast<IMFMediaBuffer*>(frame->private_buffer())->Release(); | |
69 ++count_; | |
70 if (flush_countdown_ > 0) { | |
71 if (--flush_countdown_ == 0) { | |
72 decoder_->Flush(); | |
73 } | |
74 } | 77 } |
75 MessageLoop::current()->PostTask( | |
76 FROM_HERE, | |
77 NewRunnableMethod(decoder_.get(), &MftH264Decoder::GetOutput)); | |
78 } | 78 } |
79 | 79 bool GetWidth(int* width) { |
80 virtual void Start() { | 80 if (!reader_.get()) |
81 MessageLoop::current()->PostTask( | 81 return false; |
82 FROM_HERE, | 82 return reader_->GetWidth(width); |
83 NewRunnableMethod(decoder_.get(), &MftH264Decoder::GetOutput)); | |
84 } | 83 } |
85 | 84 bool GetHeight(int* height) { |
86 virtual void OnDecodeError(MftH264Decoder::Error error) { | 85 if (!reader_.get()) |
87 MessageLoop::current()->Quit(); | 86 return false; |
| 87 return reader_->GetHeight(height); |
88 } | 88 } |
89 | 89 scoped_ptr<FFmpegFileReader> reader_; |
90 virtual void SetFlushCountdown(int countdown) { | |
91 flush_countdown_ = countdown; | |
92 } | |
93 | |
94 int count() const { return count_; } | |
95 | |
96 protected: | |
97 scoped_refptr<MftH264Decoder> decoder_; | |
98 int count_; | |
99 int flush_countdown_; | |
100 }; | 90 }; |
101 | 91 |
102 class MftH264DecoderTest : public testing::Test { | 92 class MftH264DecoderTest : public testing::Test { |
103 public: | 93 public: |
104 MftH264DecoderTest() {} | 94 MftH264DecoderTest() {} |
105 virtual ~MftH264DecoderTest() {} | 95 virtual ~MftH264DecoderTest() {} |
106 | 96 |
107 protected: | 97 protected: |
108 virtual void SetUp() {} | 98 virtual void SetUp() {} |
109 virtual void TearDown() {} | 99 virtual void TearDown() {} |
110 }; | 100 }; |
111 | 101 |
| 102 class SimpleMftH264DecoderHandler : public VideoDecodeEngine::EventHandler { |
| 103 public: |
| 104 SimpleMftH264DecoderHandler() |
| 105 : init_count_(0), |
| 106 uninit_count_(0), |
| 107 flush_count_(0), |
| 108 format_change_count_(0), |
| 109 empty_buffer_callback_count_(0), |
| 110 fill_buffer_callback_count_(0) { |
| 111 memset(&info_, 0, sizeof(info_)); |
| 112 } |
| 113 virtual ~SimpleMftH264DecoderHandler() {} |
| 114 virtual void OnInitializeComplete(const VideoCodecInfo& info) { |
| 115 info_ = info; |
| 116 init_count_++; |
| 117 } |
| 118 virtual void OnUninitializeComplete() { |
| 119 uninit_count_++; |
| 120 } |
| 121 virtual void OnFlushComplete() { |
| 122 flush_count_++; |
| 123 } |
| 124 virtual void OnSeekComplete() {} |
| 125 virtual void OnError() {} |
| 126 virtual void OnFormatChange(VideoStreamInfo stream_info) { |
| 127 format_change_count_++; |
| 128 info_.stream_info_ = stream_info; |
| 129 } |
| 130 virtual void OnEmptyBufferCallback(scoped_refptr<Buffer> buffer) { |
| 131 if (reader_.get() && decoder_.get()) { |
| 132 empty_buffer_callback_count_++; |
| 133 scoped_refptr<DataBuffer> input; |
| 134 reader_->ReadCallback(&input); |
| 135 decoder_->EmptyThisBuffer(input); |
| 136 } |
| 137 } |
| 138 virtual void OnFillBufferCallback(scoped_refptr<VideoFrame> frame) { |
| 139 fill_buffer_callback_count_++; |
| 140 current_frame_ = frame; |
| 141 } |
| 142 void SetReader(scoped_refptr<BaseMftReader> reader) { |
| 143 reader_ = reader; |
| 144 } |
| 145 void SetDecoder(scoped_refptr<MftH264Decoder> decoder) { |
| 146 decoder_ = decoder; |
| 147 } |
| 148 |
| 149 int init_count_; |
| 150 int uninit_count_; |
| 151 int flush_count_; |
| 152 int format_change_count_; |
| 153 int empty_buffer_callback_count_; |
| 154 int fill_buffer_callback_count_; |
| 155 VideoCodecInfo info_; |
| 156 scoped_refptr<BaseMftReader> reader_; |
| 157 scoped_refptr<MftH264Decoder> decoder_; |
| 158 scoped_refptr<VideoFrame> current_frame_; |
| 159 }; |
| 160 |
112 // A simple test case for init/deinit of MF/COM libraries. | 161 // A simple test case for init/deinit of MF/COM libraries. |
113 TEST_F(MftH264DecoderTest, SimpleInit) { | 162 TEST_F(MftH264DecoderTest, LibraryInit) { |
114 EXPECT_HRESULT_SUCCEEDED( | 163 EXPECT_TRUE(MftH264Decoder::StartupComLibraries()); |
115 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); | 164 MftH264Decoder::ShutdownComLibraries(); |
116 EXPECT_HRESULT_SUCCEEDED(MFStartup(MF_VERSION, MFSTARTUP_FULL)); | 165 } |
117 EXPECT_HRESULT_SUCCEEDED(MFShutdown()); | 166 |
118 CoUninitialize(); | 167 TEST_F(MftH264DecoderTest, DecoderUninitializedAtFirst) { |
119 } | |
120 | |
121 TEST_F(MftH264DecoderTest, InitWithDxvaButNoD3dDevice) { | |
122 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); | 168 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
123 ASSERT_TRUE(decoder.get() != NULL); | 169 ASSERT_TRUE(decoder.get()); |
124 FakeMftReader reader; | 170 EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
125 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | 171 } |
126 EXPECT_FALSE( | 172 |
127 decoder->Init(NULL, 6, 7, 111, 222, 3, 1, | 173 TEST_F(MftH264DecoderTest, DecoderInitMissingArgs) { |
128 NewCallback(&reader, &FakeMftReader::ReadCallback), | 174 VideoCodecConfig config; |
129 NewCallback(renderer.get(), | 175 config.width_ = 800; |
130 &FakeMftRenderer::WriteCallback), | 176 config.height_ = 600; |
131 NewCallback(renderer.get(), | 177 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
132 &FakeMftRenderer::OnDecodeError))); | 178 ASSERT_TRUE(decoder.get()); |
133 } | 179 decoder->Initialize(NULL, NULL, config); |
134 | 180 EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
135 TEST_F(MftH264DecoderTest, InitMissingCallbacks) { | 181 } |
136 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 182 |
137 ASSERT_TRUE(decoder.get() != NULL); | 183 TEST_F(MftH264DecoderTest, DecoderInitNoDxva) { |
138 EXPECT_FALSE(decoder->Init(NULL, 1, 3, 111, 222, 56, 34, NULL, NULL, NULL)); | 184 MessageLoop loop; |
| 185 SimpleMftH264DecoderHandler handler; |
| 186 VideoCodecConfig config; |
| 187 config.width_ = 800; |
| 188 config.height_ = 600; |
| 189 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
| 190 ASSERT_TRUE(decoder.get()); |
| 191 decoder->Initialize(&loop, &handler, config); |
| 192 EXPECT_EQ(1, handler.init_count_); |
| 193 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 194 decoder->Uninitialize(); |
| 195 } |
| 196 |
| 197 TEST_F(MftH264DecoderTest, DecoderInitDxva) { |
| 198 MessageLoop loop; |
| 199 SimpleMftH264DecoderHandler handler; |
| 200 VideoCodecConfig config; |
| 201 config.width_ = 800; |
| 202 config.height_ = 600; |
| 203 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
| 204 ASSERT_TRUE(decoder.get()); |
| 205 decoder->Initialize(&loop, &handler, config); |
| 206 EXPECT_EQ(1, handler.init_count_); |
| 207 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 208 decoder->Uninitialize(); |
| 209 } |
| 210 |
| 211 TEST_F(MftH264DecoderTest, DecoderUninit) { |
| 212 MessageLoop loop; |
| 213 SimpleMftH264DecoderHandler handler; |
| 214 VideoCodecConfig config; |
| 215 config.width_ = 800; |
| 216 config.height_ = 600; |
| 217 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
| 218 ASSERT_TRUE(decoder.get()); |
| 219 decoder->Initialize(&loop, &handler, config); |
| 220 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 221 decoder->Uninitialize(); |
| 222 EXPECT_EQ(1, handler.uninit_count_); |
| 223 EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
| 224 } |
| 225 |
| 226 TEST_F(MftH264DecoderTest, UninitBeforeInit) { |
| 227 MessageLoop loop; |
| 228 SimpleMftH264DecoderHandler handler; |
| 229 VideoCodecConfig config; |
| 230 config.width_ = 800; |
| 231 config.height_ = 600; |
| 232 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
| 233 ASSERT_TRUE(decoder.get()); |
| 234 decoder->Uninitialize(); |
| 235 EXPECT_EQ(0, handler.uninit_count_); |
139 } | 236 } |
140 | 237 |
141 TEST_F(MftH264DecoderTest, InitWithNegativeDimensions) { | 238 TEST_F(MftH264DecoderTest, InitWithNegativeDimensions) { |
142 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 239 MessageLoop loop; |
143 ASSERT_TRUE(decoder.get() != NULL); | 240 SimpleMftH264DecoderHandler handler; |
144 FakeMftReader reader; | 241 VideoCodecConfig config; |
145 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | 242 config.width_ = -123; |
146 EXPECT_TRUE(decoder->Init(NULL, 0, 6, -123, -456, 22, 4787, | 243 config.height_ = -456; |
147 NewCallback(&reader, &FakeMftReader::ReadCallback), | 244 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
148 NewCallback(renderer.get(), | 245 ASSERT_TRUE(decoder.get()); |
149 &FakeMftRenderer::WriteCallback), | 246 decoder->Initialize(&loop, &handler, config); |
150 NewCallback(renderer.get(), | 247 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
151 &FakeMftRenderer::OnDecodeError))); | 248 EXPECT_EQ(kDecoderMaxWidth, handler.info_.stream_info_.surface_width_); |
152 | 249 EXPECT_EQ(kDecoderMaxHeight, handler.info_.stream_info_.surface_height_); |
153 // By default, decoder should "guess" the dimensions to be the maximum. | 250 decoder->Uninitialize(); |
154 EXPECT_EQ(kDecoderMaxWidth, decoder->width()); | |
155 EXPECT_EQ(kDecoderMaxHeight, decoder->height()); | |
156 } | 251 } |
157 | 252 |
158 TEST_F(MftH264DecoderTest, InitWithTooHighDimensions) { | 253 TEST_F(MftH264DecoderTest, InitWithTooHighDimensions) { |
159 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 254 MessageLoop loop; |
160 ASSERT_TRUE(decoder.get() != NULL); | 255 SimpleMftH264DecoderHandler handler; |
161 FakeMftReader reader; | 256 VideoCodecConfig config; |
162 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | 257 config.width_ = kDecoderMaxWidth + 1; |
163 EXPECT_TRUE(decoder->Init(NULL, 0, 0, | 258 config.height_ = kDecoderMaxHeight + 1; |
164 kDecoderMaxWidth + 1, kDecoderMaxHeight + 1, | 259 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
165 0, 0, | 260 ASSERT_TRUE(decoder.get()); |
166 NewCallback(&reader, &FakeMftReader::ReadCallback), | 261 decoder->Initialize(&loop, &handler, config); |
167 NewCallback(renderer.get(), | 262 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
168 &FakeMftRenderer::WriteCallback), | 263 EXPECT_EQ(kDecoderMaxWidth, handler.info_.stream_info_.surface_width_); |
169 NewCallback(renderer.get(), | 264 EXPECT_EQ(kDecoderMaxHeight, handler.info_.stream_info_.surface_height_); |
170 &FakeMftRenderer::OnDecodeError))); | 265 decoder->Uninitialize(); |
171 | 266 } |
172 // Decoder should truncate the dimensions to the maximum supported. | 267 |
173 EXPECT_EQ(kDecoderMaxWidth, decoder->width()); | 268 TEST_F(MftH264DecoderTest, DrainOnEmptyBuffer) { |
174 EXPECT_EQ(kDecoderMaxHeight, decoder->height()); | 269 MessageLoop loop; |
175 } | 270 SimpleMftH264DecoderHandler handler; |
176 | 271 VideoCodecConfig config; |
177 TEST_F(MftH264DecoderTest, InitWithNormalDimensions) { | 272 config.width_ = 1024; |
178 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 273 config.height_ = 768; |
179 ASSERT_TRUE(decoder.get() != NULL); | 274 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
180 FakeMftReader reader; | 275 ASSERT_TRUE(decoder.get()); |
181 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | 276 decoder->Initialize(&loop, &handler, config); |
182 int width = 1024, height = 768; | 277 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
183 EXPECT_TRUE(decoder->Init(NULL, 0, 0, width, height, 0, 0, | 278 scoped_refptr<Buffer> buffer(new DataBuffer(0)); |
184 NewCallback(&reader, &FakeMftReader::ReadCallback), | 279 |
185 NewCallback(renderer.get(), | 280 // Decoder should switch to drain mode because of this NULL buffer, and then |
186 &FakeMftRenderer::WriteCallback), | 281 // switch to kStopped when it says it needs more input during drain mode. |
187 NewCallback(renderer.get(), | 282 decoder->EmptyThisBuffer(buffer); |
188 &FakeMftRenderer::OnDecodeError))); | 283 EXPECT_EQ(MftH264Decoder::kStopped, decoder->state()); |
189 | 284 |
190 EXPECT_EQ(width, decoder->width()); | 285 // Should have called back with one empty frame. |
191 EXPECT_EQ(height, decoder->height()); | 286 EXPECT_EQ(1, handler.fill_buffer_callback_count_); |
192 } | 287 ASSERT_TRUE(handler.current_frame_.get()); |
193 | 288 EXPECT_EQ(VideoFrame::EMPTY, handler.current_frame_->format()); |
194 // SendDrainMessage() is not a public method. Nonetheless it does not hurt | 289 decoder->Uninitialize(); |
195 // to test that the decoder should not do things before it is initialized. | 290 } |
196 TEST_F(MftH264DecoderTest, SendDrainMessageBeforeInitDeathTest) { | 291 |
197 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | |
198 ASSERT_TRUE(decoder.get() != NULL); | |
199 EXPECT_DEATH({ decoder->SendDrainMessage(); }, ".*initialized_.*"); | |
200 } | |
201 | |
202 // Tests draining after init, but before any input is sent. | |
203 TEST_F(MftH264DecoderTest, SendDrainMessageAtInit) { | |
204 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | |
205 ASSERT_TRUE(decoder.get() != NULL); | |
206 FakeMftReader reader; | |
207 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
208 ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, | |
209 NewCallback(&reader, &FakeMftReader::ReadCallback), | |
210 NewCallback(renderer.get(), | |
211 &FakeMftRenderer::WriteCallback), | |
212 NewCallback(renderer.get(), | |
213 &FakeMftRenderer::OnDecodeError))); | |
214 EXPECT_TRUE(decoder->SendDrainMessage()); | |
215 EXPECT_TRUE(decoder->drain_message_sent_); | |
216 } | |
217 | |
218 TEST_F(MftH264DecoderTest, DrainOnEndOfInputStream) { | |
219 MessageLoop loop; | |
220 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | |
221 ASSERT_TRUE(decoder.get() != NULL); | |
222 | |
223 // No frames, outputs a NULL indicating end-of-stream | |
224 FakeMftReader reader(0); | |
225 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
226 ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, | |
227 NewCallback(&reader, &FakeMftReader::ReadCallback), | |
228 NewCallback(renderer.get(), | |
229 &FakeMftRenderer::WriteCallback), | |
230 NewCallback(renderer.get(), | |
231 &FakeMftRenderer::OnDecodeError))); | |
232 MessageLoop::current()->PostTask( | |
233 FROM_HERE, | |
234 NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); | |
235 MessageLoop::current()->Run(); | |
236 EXPECT_TRUE(decoder->drain_message_sent()); | |
237 } | |
238 | |
239 // 100 input garbage samples should be enough to test whether the decoder | |
240 // will output decoded garbage frames. | |
241 TEST_F(MftH264DecoderTest, NoOutputOnGarbageInput) { | 292 TEST_F(MftH264DecoderTest, NoOutputOnGarbageInput) { |
242 MessageLoop loop; | 293 // 100 samples of garbage. |
243 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 294 const int kNumFrames = 100; |
244 ASSERT_TRUE(decoder.get() != NULL); | 295 scoped_refptr<FakeMftReader> reader(new FakeMftReader(kNumFrames)); |
245 int num_frames = 100; | 296 ASSERT_TRUE(reader.get()); |
246 FakeMftReader reader(num_frames); | 297 |
247 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | 298 MessageLoop loop; |
248 ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, | 299 SimpleMftH264DecoderHandler handler; |
249 NewCallback(&reader, &FakeMftReader::ReadCallback), | 300 VideoCodecConfig config; |
250 NewCallback(renderer.get(), | 301 config.width_ = 1024; |
251 &FakeMftRenderer::WriteCallback), | 302 config.height_ = 768; |
252 NewCallback(renderer.get(), | 303 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
253 &FakeMftRenderer::OnDecodeError))); | 304 ASSERT_TRUE(decoder.get()); |
254 MessageLoop::current()->PostTask( | 305 decoder->Initialize(&loop, &handler, config); |
255 FROM_HERE, NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); | 306 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
256 MessageLoop::current()->Run(); | 307 handler.SetReader(reader); |
257 | 308 handler.SetDecoder(decoder); |
258 // Decoder should accept corrupt input data and silently ignore it. | 309 while (MftH264Decoder::kStopped != decoder->state()) { |
259 EXPECT_EQ(num_frames, decoder->frames_read()); | 310 scoped_refptr<VideoFrame> frame; |
260 | 311 decoder->FillThisBuffer(frame); |
261 // Decoder should not have output anything if input is corrupt. | 312 } |
262 EXPECT_EQ(0, decoder->frames_decoded()); | 313 |
263 EXPECT_EQ(0, renderer->count()); | 314 // Output callback should only be invoked once - the empty frame to indicate |
| 315 // end of stream. |
| 316 EXPECT_EQ(1, handler.fill_buffer_callback_count_); |
| 317 ASSERT_TRUE(handler.current_frame_.get()); |
| 318 EXPECT_EQ(VideoFrame::EMPTY, handler.current_frame_->format()); |
| 319 |
| 320 // One extra count because of the end of stream NULL sample. |
| 321 EXPECT_EQ(kNumFrames, handler.empty_buffer_callback_count_ - 1); |
| 322 decoder->Uninitialize(); |
| 323 } |
| 324 |
| 325 TEST_F(MftH264DecoderTest, FlushAtStart) { |
| 326 MessageLoop loop; |
| 327 SimpleMftH264DecoderHandler handler; |
| 328 VideoCodecConfig config; |
| 329 config.width_ = 1024; |
| 330 config.height_ = 768; |
| 331 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
| 332 ASSERT_TRUE(decoder.get()); |
| 333 decoder->Initialize(&loop, &handler, config); |
| 334 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 335 decoder->Flush(); |
| 336 |
| 337 // Flush should succeed even if input/output are empty. |
| 338 EXPECT_EQ(1, handler.flush_count_); |
| 339 decoder->Uninitialize(); |
| 340 } |
| 341 |
| 342 TEST_F(MftH264DecoderTest, NoFlushAtStopped) { |
| 343 scoped_refptr<BaseMftReader> reader(new FakeMftReader()); |
| 344 ASSERT_TRUE(reader.get()); |
| 345 |
| 346 MessageLoop loop; |
| 347 SimpleMftH264DecoderHandler handler; |
| 348 VideoCodecConfig config; |
| 349 config.width_ = 1024; |
| 350 config.height_ = 768; |
| 351 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
| 352 ASSERT_TRUE(decoder.get()); |
| 353 decoder->Initialize(&loop, &handler, config); |
| 354 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 355 handler.SetReader(reader); |
| 356 handler.SetDecoder(decoder); |
| 357 while (MftH264Decoder::kStopped != decoder->state()) { |
| 358 scoped_refptr<VideoFrame> frame; |
| 359 decoder->FillThisBuffer(frame); |
| 360 } |
| 361 EXPECT_EQ(0, handler.flush_count_); |
| 362 int old_flush_count = handler.flush_count_; |
| 363 decoder->Flush(); |
| 364 EXPECT_EQ(old_flush_count, handler.flush_count_); |
| 365 decoder->Uninitialize(); |
264 } | 366 } |
265 | 367 |
266 FilePath GetVideoFilePath(const std::string& file_name) { | 368 FilePath GetVideoFilePath(const std::string& file_name) { |
267 FilePath path; | 369 FilePath path; |
268 PathService::Get(base::DIR_SOURCE_ROOT, &path); | 370 PathService::Get(base::DIR_SOURCE_ROOT, &path); |
269 path = path.AppendASCII("media") | 371 path = path.AppendASCII("media") |
270 .AppendASCII("test") | 372 .AppendASCII("test") |
271 .AppendASCII("data") | 373 .AppendASCII("data") |
272 .AppendASCII(file_name.c_str()); | 374 .AppendASCII(file_name.c_str()); |
273 return path; | 375 return path; |
274 } | 376 } |
275 | 377 |
276 // Decodes media/test/data/bear.1280x720.mp4 which is expected to be a valid | 378 void DecodeValidVideo(const std::string& filename, int num_frames, bool dxva) { |
277 // H.264 video. | 379 scoped_refptr<FFmpegFileReaderWrapper> reader(new FFmpegFileReaderWrapper()); |
278 TEST_F(MftH264DecoderTest, DecodeValidVideoDxva) { | 380 ASSERT_TRUE(reader.get()); |
| 381 FilePath path = GetVideoFilePath(filename); |
| 382 ASSERT_TRUE(file_util::PathExists(path)); |
| 383 ASSERT_TRUE(reader->InitReader(WideToASCII(path.value()))); |
| 384 int actual_width; |
| 385 int actual_height; |
| 386 ASSERT_TRUE(reader->GetWidth(&actual_width)); |
| 387 ASSERT_TRUE(reader->GetHeight(&actual_height)); |
| 388 |
279 MessageLoop loop; | 389 MessageLoop loop; |
280 FilePath path = GetVideoFilePath("bear.1280x720.mp4"); | 390 SimpleMftH264DecoderHandler handler; |
281 ASSERT_TRUE(file_util::PathExists(path)); | 391 VideoCodecConfig config; |
| 392 config.width_ = 1; |
| 393 config.height_ = 1; |
| 394 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(dxva)); |
| 395 ASSERT_TRUE(decoder.get()); |
| 396 decoder->Initialize(&loop, &handler, config); |
| 397 EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
| 398 handler.SetReader(reader); |
| 399 handler.SetDecoder(decoder); |
| 400 while (MftH264Decoder::kStopped != decoder->state()) { |
| 401 scoped_refptr<VideoFrame> frame; |
| 402 decoder->FillThisBuffer(frame); |
| 403 } |
282 | 404 |
283 ScopedComPtr<IDirect3D9> d3d9; | 405 // We expect a format change when decoder receives enough data to determine |
284 ScopedComPtr<IDirect3DDevice9> device; | 406 // the actual frame width/height. |
285 ScopedComPtr<IDirect3DDeviceManager9> dev_manager; | 407 EXPECT_GT(handler.format_change_count_, 0); |
286 dev_manager.Attach(CreateD3DDevManager(GetDesktopWindow(), | 408 EXPECT_EQ(actual_width, handler.info_.stream_info_.surface_width_); |
287 d3d9.Receive(), | 409 EXPECT_EQ(actual_height, handler.info_.stream_info_.surface_height_); |
288 device.Receive())); | 410 EXPECT_GE(handler.empty_buffer_callback_count_, num_frames); |
289 ASSERT_TRUE(dev_manager.get() != NULL); | 411 EXPECT_EQ(num_frames, handler.fill_buffer_callback_count_ - 1); |
290 | 412 decoder->Uninitialize(); |
291 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); | |
292 ASSERT_TRUE(decoder.get() != NULL); | |
293 scoped_ptr<FFmpegFileReader> reader( | |
294 new FFmpegFileReader(WideToASCII(path.value()))); | |
295 ASSERT_TRUE(reader->Initialize()); | |
296 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
297 ASSERT_TRUE(decoder->Init(dev_manager.get(), 0, 0, 111, 222, 0, 0, | |
298 NewCallback(reader.get(), &FFmpegFileReader::Read), | |
299 NewCallback(renderer.get(), | |
300 &FakeMftRenderer::WriteCallback), | |
301 NewCallback(renderer.get(), | |
302 &FakeMftRenderer::OnDecodeError))); | |
303 MessageLoop::current()->PostTask( | |
304 FROM_HERE, | |
305 NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); | |
306 MessageLoop::current()->Run(); | |
307 | |
308 // If the video is valid, then it should output frames. However, for some | |
309 // videos, the number of frames decoded is one-off. | |
310 EXPECT_EQ(82, decoder->frames_read()); | |
311 EXPECT_LE(decoder->frames_read() - decoder->frames_decoded(), 1); | |
312 } | 413 } |
313 | 414 |
314 TEST_F(MftH264DecoderTest, FlushAtInit) { | 415 TEST_F(MftH264DecoderTest, DecodeValidVideoDxva) { |
315 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | 416 DecodeValidVideo("bear.1280x720.mp4", 82, true); |
316 ASSERT_TRUE(decoder.get() != NULL); | |
317 FakeMftReader reader; | |
318 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
319 ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, | |
320 NewCallback(&reader, &FakeMftReader::ReadCallback), | |
321 NewCallback(renderer.get(), | |
322 &FakeMftRenderer::WriteCallback), | |
323 NewCallback(renderer.get(), | |
324 &FakeMftRenderer::OnDecodeError))); | |
325 EXPECT_TRUE(decoder->Flush()); | |
326 } | 417 } |
327 | 418 |
328 TEST_F(MftH264DecoderTest, FlushAtEnd) { | 419 TEST_F(MftH264DecoderTest, DecodeValidVideoNoDxva) { |
329 MessageLoop loop; | 420 DecodeValidVideo("bear.1280x720.mp4", 82, false); |
330 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); | |
331 ASSERT_TRUE(decoder.get() != NULL); | |
332 | |
333 // No frames, outputs a NULL indicating end-of-stream | |
334 FakeMftReader reader(0); | |
335 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
336 ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, | |
337 NewCallback(&reader, &FakeMftReader::ReadCallback), | |
338 NewCallback(renderer.get(), | |
339 &FakeMftRenderer::WriteCallback), | |
340 NewCallback(renderer.get(), | |
341 &FakeMftRenderer::OnDecodeError))); | |
342 MessageLoop::current()->PostTask( | |
343 FROM_HERE, | |
344 NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); | |
345 MessageLoop::current()->Run(); | |
346 EXPECT_TRUE(decoder->Flush()); | |
347 } | |
348 | |
349 TEST_F(MftH264DecoderTest, FlushAtMiddle) { | |
350 MessageLoop loop; | |
351 FilePath path = GetVideoFilePath("bear.1280x720.mp4"); | |
352 ASSERT_TRUE(file_util::PathExists(path)); | |
353 | |
354 ScopedComPtr<IDirect3D9> d3d9; | |
355 ScopedComPtr<IDirect3DDevice9> device; | |
356 ScopedComPtr<IDirect3DDeviceManager9> dev_manager; | |
357 dev_manager.Attach(CreateD3DDevManager(GetDesktopWindow(), | |
358 d3d9.Receive(), | |
359 device.Receive())); | |
360 ASSERT_TRUE(dev_manager.get() != NULL); | |
361 | |
362 scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); | |
363 ASSERT_TRUE(decoder.get() != NULL); | |
364 scoped_ptr<FFmpegFileReader> reader( | |
365 new FFmpegFileReader(WideToASCII(path.value()))); | |
366 ASSERT_TRUE(reader->Initialize()); | |
367 scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); | |
368 ASSERT_TRUE(renderer.get()); | |
369 | |
370 // Flush after obtaining 40 decode frames. There are no more key frames after | |
371 // the first one, so we expect it to stop outputting frames after flush. | |
372 int flush_at_nth_decoded_frame = 40; | |
373 renderer->SetFlushCountdown(flush_at_nth_decoded_frame); | |
374 ASSERT_TRUE(decoder->Init(dev_manager.get(), 0, 0, 111, 222, 0, 0, | |
375 NewCallback(reader.get(), &FFmpegFileReader::Read), | |
376 NewCallback(renderer.get(), | |
377 &FakeMftRenderer::WriteCallback), | |
378 NewCallback(renderer.get(), | |
379 &FakeMftRenderer::OnDecodeError))); | |
380 MessageLoop::current()->PostTask( | |
381 FROM_HERE, | |
382 NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); | |
383 MessageLoop::current()->Run(); | |
384 EXPECT_EQ(82, decoder->frames_read()); | |
385 EXPECT_EQ(decoder->frames_decoded(), flush_at_nth_decoded_frame); | |
386 } | 421 } |
387 | 422 |
388 } // namespace media | 423 } // namespace media |
OLD | NEW |