Index: media/mf/test/mft_h264_decoder_unittest.cc |
=================================================================== |
--- media/mf/test/mft_h264_decoder_unittest.cc (revision 57106) |
+++ media/mf/test/mft_h264_decoder_unittest.cc (working copy) |
@@ -2,37 +2,42 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include <d3d9.h> |
-#include <dxva2api.h> |
-#include <mfapi.h> |
- |
#include "base/file_path.h" |
#include "base/file_util.h" |
#include "base/message_loop.h" |
#include "base/path_service.h" |
-#include "base/scoped_comptr_win.h" |
+#include "base/ref_counted.h" |
#include "base/scoped_ptr.h" |
#include "base/string_util.h" |
+#include "base/time.h" |
#include "media/base/data_buffer.h" |
#include "media/base/video_frame.h" |
-#include "media/mf/d3d_util.h" |
+#include "media/filters/video_decode_engine.h" |
#include "media/mf/file_reader_util.h" |
#include "media/mf/mft_h264_decoder.h" |
#include "testing/gtest/include/gtest/gtest.h" |
+using base::TimeDelta; |
+ |
namespace media { |
static const int kDecoderMaxWidth = 1920; |
static const int kDecoderMaxHeight = 1088; |
-class FakeMftReader { |
+class BaseMftReader : public base::RefCountedThreadSafe<BaseMftReader> { |
public: |
+ virtual ~BaseMftReader() {} |
+ virtual void ReadCallback(scoped_refptr<DataBuffer>* input) = 0; |
+}; |
+ |
+class FakeMftReader : public BaseMftReader { |
+ public: |
FakeMftReader() : frames_remaining_(20) {} |
explicit FakeMftReader(int count) : frames_remaining_(count) {} |
- ~FakeMftReader() {} |
+ virtual ~FakeMftReader() {} |
// Provides garbage input to the decoder. |
- void ReadCallback(scoped_refptr<DataBuffer>* input) { |
+ virtual void ReadCallback(scoped_refptr<DataBuffer>* input) { |
if (frames_remaining_ > 0) { |
int sz = 4096; |
uint8* buf = new uint8[sz]; |
@@ -54,49 +59,34 @@ |
int frames_remaining_; |
}; |
-class FakeMftRenderer : public base::RefCountedThreadSafe<FakeMftRenderer> { |
+class FFmpegFileReaderWrapper : public BaseMftReader { |
public: |
- explicit FakeMftRenderer(scoped_refptr<MftH264Decoder> decoder) |
- : decoder_(decoder), |
- count_(0), |
- flush_countdown_(0) { |
+ FFmpegFileReaderWrapper() {} |
+ virtual ~FFmpegFileReaderWrapper() {} |
+ bool InitReader(const std::string& filename) { |
+ reader_.reset(new FFmpegFileReader(filename)); |
+ if (!reader_.get() || !reader_->Initialize()) { |
+ reader_.reset(); |
+ return false; |
+ } |
+ return true; |
} |
- |
- virtual ~FakeMftRenderer() {} |
- |
- virtual void WriteCallback(scoped_refptr<VideoFrame> frame) { |
- static_cast<IMFMediaBuffer*>(frame->private_buffer())->Release(); |
- ++count_; |
- if (flush_countdown_ > 0) { |
- if (--flush_countdown_ == 0) { |
- decoder_->Flush(); |
- } |
+ virtual void ReadCallback(scoped_refptr<DataBuffer>* input) { |
+ if (reader_.get()) { |
+ reader_->Read(input); |
} |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(decoder_.get(), &MftH264Decoder::GetOutput)); |
} |
- |
- virtual void Start() { |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(decoder_.get(), &MftH264Decoder::GetOutput)); |
+ bool GetWidth(int* width) { |
+ if (!reader_.get()) |
+ return false; |
+ return reader_->GetWidth(width); |
} |
- |
- virtual void OnDecodeError(MftH264Decoder::Error error) { |
- MessageLoop::current()->Quit(); |
+ bool GetHeight(int* height) { |
+ if (!reader_.get()) |
+ return false; |
+ return reader_->GetHeight(height); |
} |
- |
- virtual void SetFlushCountdown(int countdown) { |
- flush_countdown_ = countdown; |
- } |
- |
- int count() const { return count_; } |
- |
- protected: |
- scoped_refptr<MftH264Decoder> decoder_; |
- int count_; |
- int flush_countdown_; |
+ scoped_ptr<FFmpegFileReader> reader_; |
}; |
class MftH264DecoderTest : public testing::Test { |
@@ -109,160 +99,272 @@ |
virtual void TearDown() {} |
}; |
+class SimpleMftH264DecoderHandler : public VideoDecodeEngine::EventHandler { |
+ public: |
+ SimpleMftH264DecoderHandler() |
+ : init_count_(0), |
+ uninit_count_(0), |
+ flush_count_(0), |
+ format_change_count_(0), |
+ empty_buffer_callback_count_(0), |
+ fill_buffer_callback_count_(0) { |
+ memset(&info_, 0, sizeof(info_)); |
+ } |
+ virtual ~SimpleMftH264DecoderHandler() {} |
+ virtual void OnInitializeComplete(const VideoCodecInfo& info) { |
+ info_ = info; |
+ init_count_++; |
+ } |
+ virtual void OnUninitializeComplete() { |
+ uninit_count_++; |
+ } |
+ virtual void OnFlushComplete() { |
+ flush_count_++; |
+ } |
+ virtual void OnSeekComplete() {} |
+ virtual void OnError() {} |
+ virtual void OnFormatChange(VideoStreamInfo stream_info) { |
+ format_change_count_++; |
+ info_.stream_info_ = stream_info; |
+ } |
+ virtual void OnEmptyBufferCallback(scoped_refptr<Buffer> buffer) { |
+ if (reader_.get() && decoder_.get()) { |
+ empty_buffer_callback_count_++; |
+ scoped_refptr<DataBuffer> input; |
+ reader_->ReadCallback(&input); |
+ decoder_->EmptyThisBuffer(input); |
+ } |
+ } |
+ virtual void OnFillBufferCallback(scoped_refptr<VideoFrame> frame) { |
+ fill_buffer_callback_count_++; |
+ current_frame_ = frame; |
+ } |
+ void SetReader(scoped_refptr<BaseMftReader> reader) { |
+ reader_ = reader; |
+ } |
+ void SetDecoder(scoped_refptr<MftH264Decoder> decoder) { |
+ decoder_ = decoder; |
+ } |
+ |
+ int init_count_; |
+ int uninit_count_; |
+ int flush_count_; |
+ int format_change_count_; |
+ int empty_buffer_callback_count_; |
+ int fill_buffer_callback_count_; |
+ VideoCodecInfo info_; |
+ scoped_refptr<BaseMftReader> reader_; |
+ scoped_refptr<MftH264Decoder> decoder_; |
+ scoped_refptr<VideoFrame> current_frame_; |
+}; |
+ |
// A simple test case for init/deinit of MF/COM libraries. |
-TEST_F(MftH264DecoderTest, SimpleInit) { |
- EXPECT_HRESULT_SUCCEEDED( |
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); |
- EXPECT_HRESULT_SUCCEEDED(MFStartup(MF_VERSION, MFSTARTUP_FULL)); |
- EXPECT_HRESULT_SUCCEEDED(MFShutdown()); |
- CoUninitialize(); |
+TEST_F(MftH264DecoderTest, LibraryInit) { |
+ EXPECT_TRUE(MftH264Decoder::StartupComLibraries()); |
+ MftH264Decoder::ShutdownComLibraries(); |
} |
-TEST_F(MftH264DecoderTest, InitWithDxvaButNoD3dDevice) { |
+TEST_F(MftH264DecoderTest, DecoderUninitializedAtFirst) { |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- EXPECT_FALSE( |
- decoder->Init(NULL, 6, 7, 111, 222, 3, 1, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
+ ASSERT_TRUE(decoder.get()); |
+ EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
} |
-TEST_F(MftH264DecoderTest, InitMissingCallbacks) { |
+TEST_F(MftH264DecoderTest, DecoderInitMissingArgs) { |
+ VideoCodecConfig config; |
+ config.width_ = 800; |
+ config.height_ = 600; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- EXPECT_FALSE(decoder->Init(NULL, 1, 3, 111, 222, 56, 34, NULL, NULL, NULL)); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(NULL, NULL, config); |
+ EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
} |
-TEST_F(MftH264DecoderTest, InitWithNegativeDimensions) { |
+TEST_F(MftH264DecoderTest, DecoderInitNoDxva) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 800; |
+ config.height_ = 600; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- EXPECT_TRUE(decoder->Init(NULL, 0, 6, -123, -456, 22, 4787, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(1, handler.init_count_); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ decoder->Uninitialize(); |
+} |
- // By default, decoder should "guess" the dimensions to be the maximum. |
- EXPECT_EQ(kDecoderMaxWidth, decoder->width()); |
- EXPECT_EQ(kDecoderMaxHeight, decoder->height()); |
+TEST_F(MftH264DecoderTest, DecoderInitDxva) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 800; |
+ config.height_ = 600; |
+ scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(1, handler.init_count_); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ decoder->Uninitialize(); |
} |
-TEST_F(MftH264DecoderTest, InitWithTooHighDimensions) { |
+TEST_F(MftH264DecoderTest, DecoderUninit) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 800; |
+ config.height_ = 600; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- EXPECT_TRUE(decoder->Init(NULL, 0, 0, |
- kDecoderMaxWidth + 1, kDecoderMaxHeight + 1, |
- 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- |
- // Decoder should truncate the dimensions to the maximum supported. |
- EXPECT_EQ(kDecoderMaxWidth, decoder->width()); |
- EXPECT_EQ(kDecoderMaxHeight, decoder->height()); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ decoder->Uninitialize(); |
+ EXPECT_EQ(1, handler.uninit_count_); |
+ EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); |
} |
-TEST_F(MftH264DecoderTest, InitWithNormalDimensions) { |
+TEST_F(MftH264DecoderTest, UninitBeforeInit) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 800; |
+ config.height_ = 600; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- int width = 1024, height = 768; |
- EXPECT_TRUE(decoder->Init(NULL, 0, 0, width, height, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- |
- EXPECT_EQ(width, decoder->width()); |
- EXPECT_EQ(height, decoder->height()); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Uninitialize(); |
+ EXPECT_EQ(0, handler.uninit_count_); |
} |
-// SendDrainMessage() is not a public method. Nonetheless it does not hurt |
-// to test that the decoder should not do things before it is initialized. |
-TEST_F(MftH264DecoderTest, SendDrainMessageBeforeInitDeathTest) { |
+TEST_F(MftH264DecoderTest, InitWithNegativeDimensions) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = -123; |
+ config.height_ = -456; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- EXPECT_DEATH({ decoder->SendDrainMessage(); }, ".*initialized_.*"); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ EXPECT_EQ(kDecoderMaxWidth, handler.info_.stream_info_.surface_width_); |
+ EXPECT_EQ(kDecoderMaxHeight, handler.info_.stream_info_.surface_height_); |
+ decoder->Uninitialize(); |
} |
-// Tests draining after init, but before any input is sent. |
-TEST_F(MftH264DecoderTest, SendDrainMessageAtInit) { |
+TEST_F(MftH264DecoderTest, InitWithTooHighDimensions) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = kDecoderMaxWidth + 1; |
+ config.height_ = kDecoderMaxHeight + 1; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- EXPECT_TRUE(decoder->SendDrainMessage()); |
- EXPECT_TRUE(decoder->drain_message_sent_); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ EXPECT_EQ(kDecoderMaxWidth, handler.info_.stream_info_.surface_width_); |
+ EXPECT_EQ(kDecoderMaxHeight, handler.info_.stream_info_.surface_height_); |
+ decoder->Uninitialize(); |
} |
-TEST_F(MftH264DecoderTest, DrainOnEndOfInputStream) { |
+TEST_F(MftH264DecoderTest, DrainOnEmptyBuffer) { |
MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 1024; |
+ config.height_ = 768; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ scoped_refptr<Buffer> buffer(new DataBuffer(0)); |
- // No frames, outputs a NULL indicating end-of-stream |
- FakeMftReader reader(0); |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); |
- MessageLoop::current()->Run(); |
- EXPECT_TRUE(decoder->drain_message_sent()); |
+ // Decoder should switch to drain mode because of this NULL buffer, and then |
+ // switch to kStopped when it says it needs more input during drain mode. |
+ decoder->EmptyThisBuffer(buffer); |
+ EXPECT_EQ(MftH264Decoder::kStopped, decoder->state()); |
+ |
+ // Should have called back with one empty frame. |
+ EXPECT_EQ(1, handler.fill_buffer_callback_count_); |
+ ASSERT_TRUE(handler.current_frame_.get()); |
+ EXPECT_EQ(VideoFrame::EMPTY, handler.current_frame_->format()); |
+ decoder->Uninitialize(); |
} |
-// 100 input garbage samples should be enough to test whether the decoder |
-// will output decoded garbage frames. |
TEST_F(MftH264DecoderTest, NoOutputOnGarbageInput) { |
+ // 100 samples of garbage. |
+ const int kNumFrames = 100; |
+ scoped_refptr<FakeMftReader> reader(new FakeMftReader(kNumFrames)); |
+ ASSERT_TRUE(reader.get()); |
+ |
MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 1024; |
+ config.height_ = 768; |
scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- int num_frames = 100; |
- FakeMftReader reader(num_frames); |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); |
- MessageLoop::current()->Run(); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ handler.SetReader(reader); |
+ handler.SetDecoder(decoder); |
+ while (MftH264Decoder::kStopped != decoder->state()) { |
+ scoped_refptr<VideoFrame> frame; |
+ decoder->FillThisBuffer(frame); |
+ } |
- // Decoder should accept corrupt input data and silently ignore it. |
- EXPECT_EQ(num_frames, decoder->frames_read()); |
+ // Output callback should only be invoked once - the empty frame to indicate |
+ // end of stream. |
+ EXPECT_EQ(1, handler.fill_buffer_callback_count_); |
+ ASSERT_TRUE(handler.current_frame_.get()); |
+ EXPECT_EQ(VideoFrame::EMPTY, handler.current_frame_->format()); |
- // Decoder should not have output anything if input is corrupt. |
- EXPECT_EQ(0, decoder->frames_decoded()); |
- EXPECT_EQ(0, renderer->count()); |
+ // One extra count because of the end of stream NULL sample. |
+ EXPECT_EQ(kNumFrames, handler.empty_buffer_callback_count_ - 1); |
+ decoder->Uninitialize(); |
} |
+TEST_F(MftH264DecoderTest, FlushAtStart) { |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 1024; |
+ config.height_ = 768; |
+ scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ decoder->Flush(); |
+ |
+ // Flush should succeed even if input/output are empty. |
+ EXPECT_EQ(1, handler.flush_count_); |
+ decoder->Uninitialize(); |
+} |
+ |
+TEST_F(MftH264DecoderTest, NoFlushAtStopped) { |
+ scoped_refptr<BaseMftReader> reader(new FakeMftReader()); |
+ ASSERT_TRUE(reader.get()); |
+ |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 1024; |
+ config.height_ = 768; |
+ scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ handler.SetReader(reader); |
+ handler.SetDecoder(decoder); |
+ while (MftH264Decoder::kStopped != decoder->state()) { |
+ scoped_refptr<VideoFrame> frame; |
+ decoder->FillThisBuffer(frame); |
+ } |
+ EXPECT_EQ(0, handler.flush_count_); |
+ int old_flush_count = handler.flush_count_; |
+ decoder->Flush(); |
+ EXPECT_EQ(old_flush_count, handler.flush_count_); |
+ decoder->Uninitialize(); |
+} |
+ |
FilePath GetVideoFilePath(const std::string& file_name) { |
FilePath path; |
PathService::Get(base::DIR_SOURCE_ROOT, &path); |
@@ -273,116 +375,49 @@ |
return path; |
} |
-// Decodes media/test/data/bear.1280x720.mp4 which is expected to be a valid |
-// H.264 video. |
-TEST_F(MftH264DecoderTest, DecodeValidVideoDxva) { |
- MessageLoop loop; |
- FilePath path = GetVideoFilePath("bear.1280x720.mp4"); |
+void DecodeValidVideo(const std::string& filename, int num_frames, bool dxva) { |
+ scoped_refptr<FFmpegFileReaderWrapper> reader(new FFmpegFileReaderWrapper()); |
+ ASSERT_TRUE(reader.get()); |
+ FilePath path = GetVideoFilePath(filename); |
ASSERT_TRUE(file_util::PathExists(path)); |
+ ASSERT_TRUE(reader->InitReader(WideToASCII(path.value()))); |
+ int actual_width; |
+ int actual_height; |
+ ASSERT_TRUE(reader->GetWidth(&actual_width)); |
+ ASSERT_TRUE(reader->GetHeight(&actual_height)); |
- ScopedComPtr<IDirect3D9> d3d9; |
- ScopedComPtr<IDirect3DDevice9> device; |
- ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
- dev_manager.Attach(CreateD3DDevManager(GetDesktopWindow(), |
- d3d9.Receive(), |
- device.Receive())); |
- ASSERT_TRUE(dev_manager.get() != NULL); |
+ MessageLoop loop; |
+ SimpleMftH264DecoderHandler handler; |
+ VideoCodecConfig config; |
+ config.width_ = 1; |
+ config.height_ = 1; |
+ scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(dxva)); |
+ ASSERT_TRUE(decoder.get()); |
+ decoder->Initialize(&loop, &handler, config); |
+ EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); |
+ handler.SetReader(reader); |
+ handler.SetDecoder(decoder); |
+ while (MftH264Decoder::kStopped != decoder->state()) { |
+ scoped_refptr<VideoFrame> frame; |
+ decoder->FillThisBuffer(frame); |
+ } |
- scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- scoped_ptr<FFmpegFileReader> reader( |
- new FFmpegFileReader(WideToASCII(path.value()))); |
- ASSERT_TRUE(reader->Initialize()); |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(dev_manager.get(), 0, 0, 111, 222, 0, 0, |
- NewCallback(reader.get(), &FFmpegFileReader::Read), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); |
- MessageLoop::current()->Run(); |
- |
- // If the video is valid, then it should output frames. However, for some |
- // videos, the number of frames decoded is one-off. |
- EXPECT_EQ(82, decoder->frames_read()); |
- EXPECT_LE(decoder->frames_read() - decoder->frames_decoded(), 1); |
+ // We expect a format change when decoder receives enough data to determine |
+ // the actual frame width/height. |
+ EXPECT_GT(handler.format_change_count_, 0); |
+ EXPECT_EQ(actual_width, handler.info_.stream_info_.surface_width_); |
+ EXPECT_EQ(actual_height, handler.info_.stream_info_.surface_height_); |
+ EXPECT_GE(handler.empty_buffer_callback_count_, num_frames); |
+ EXPECT_EQ(num_frames, handler.fill_buffer_callback_count_ - 1); |
+ decoder->Uninitialize(); |
} |
-TEST_F(MftH264DecoderTest, FlushAtInit) { |
- scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- FakeMftReader reader; |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- EXPECT_TRUE(decoder->Flush()); |
+TEST_F(MftH264DecoderTest, DecodeValidVideoDxva) { |
+ DecodeValidVideo("bear.1280x720.mp4", 82, true); |
} |
-TEST_F(MftH264DecoderTest, FlushAtEnd) { |
- MessageLoop loop; |
- scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(false)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- |
- // No frames, outputs a NULL indicating end-of-stream |
- FakeMftReader reader(0); |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(decoder->Init(NULL, 0, 0, 111, 222, 0, 0, |
- NewCallback(&reader, &FakeMftReader::ReadCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); |
- MessageLoop::current()->Run(); |
- EXPECT_TRUE(decoder->Flush()); |
+TEST_F(MftH264DecoderTest, DecodeValidVideoNoDxva) { |
+ DecodeValidVideo("bear.1280x720.mp4", 82, false); |
} |
-TEST_F(MftH264DecoderTest, FlushAtMiddle) { |
- MessageLoop loop; |
- FilePath path = GetVideoFilePath("bear.1280x720.mp4"); |
- ASSERT_TRUE(file_util::PathExists(path)); |
- |
- ScopedComPtr<IDirect3D9> d3d9; |
- ScopedComPtr<IDirect3DDevice9> device; |
- ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
- dev_manager.Attach(CreateD3DDevManager(GetDesktopWindow(), |
- d3d9.Receive(), |
- device.Receive())); |
- ASSERT_TRUE(dev_manager.get() != NULL); |
- |
- scoped_refptr<MftH264Decoder> decoder(new MftH264Decoder(true)); |
- ASSERT_TRUE(decoder.get() != NULL); |
- scoped_ptr<FFmpegFileReader> reader( |
- new FFmpegFileReader(WideToASCII(path.value()))); |
- ASSERT_TRUE(reader->Initialize()); |
- scoped_refptr<FakeMftRenderer> renderer(new FakeMftRenderer(decoder)); |
- ASSERT_TRUE(renderer.get()); |
- |
- // Flush after obtaining 40 decode frames. There are no more key frames after |
- // the first one, so we expect it to stop outputting frames after flush. |
- int flush_at_nth_decoded_frame = 40; |
- renderer->SetFlushCountdown(flush_at_nth_decoded_frame); |
- ASSERT_TRUE(decoder->Init(dev_manager.get(), 0, 0, 111, 222, 0, 0, |
- NewCallback(reader.get(), &FFmpegFileReader::Read), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::WriteCallback), |
- NewCallback(renderer.get(), |
- &FakeMftRenderer::OnDecodeError))); |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- NewRunnableMethod(renderer.get(), &FakeMftRenderer::Start)); |
- MessageLoop::current()->Run(); |
- EXPECT_EQ(82, decoder->frames_read()); |
- EXPECT_EQ(decoder->frames_decoded(), flush_at_nth_decoded_frame); |
-} |
- |
} // namespace media |