| 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 |