diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 05:46:50 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 05:46:50 +0000 |
commit | d741b57d94f8d8ec49cc597588ca2780d38c06d9 (patch) | |
tree | 039cac25a8f85ca5d4da053fac22469d225a20e8 /media/video | |
parent | 1ffca254514b4da587e46d1367b217eca22d5932 (diff) | |
download | chromium_src-d741b57d94f8d8ec49cc597588ca2780d38c06d9.zip chromium_src-d741b57d94f8d8ec49cc597588ca2780d38c06d9.tar.gz chromium_src-d741b57d94f8d8ec49cc597588ca2780d38c06d9.tar.bz2 |
Revert "Move MFT H264 video decoder implementation and connect it to GpuVideoDecoder"
This reverts commit 8462483619e253dca3717d51e340ed839a6cab1d.
Revert "Fix build failure."
This reverts commit 8dcd363637ac2e59f9472c0793a0b1fd3c2ad4c7.
TBR=scherkus
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/3601019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61922 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/video')
-rw-r--r-- | media/video/mft_h264_decode_engine.cc | 696 | ||||
-rw-r--r-- | media/video/mft_h264_decode_engine.h | 102 | ||||
-rw-r--r-- | media/video/mft_h264_decode_engine_context.cc | 179 | ||||
-rw-r--r-- | media/video/mft_h264_decode_engine_context.h | 70 | ||||
-rw-r--r-- | media/video/mft_h264_decode_engine_unittest.cc | 410 |
5 files changed, 249 insertions, 1208 deletions
diff --git a/media/video/mft_h264_decode_engine.cc b/media/video/mft_h264_decode_engine.cc deleted file mode 100644 index ea59ad6..0000000 --- a/media/video/mft_h264_decode_engine.cc +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/video/mft_h264_decode_engine.h" - -#include <d3d9.h> -#include <dxva2api.h> -#include <initguid.h> -#include <mfapi.h> -// Placed after mfapi.h to avoid linking strmiids.lib for MR_BUFFER_SERVICE. -#include <evr.h> -#include <mferror.h> -#include <wmcodecdsp.h> - -#include "base/time.h" -#include "base/message_loop.h" -#include "media/base/limits.h" -#include "media/video/video_decode_context.h" - -#pragma comment(lib, "dxva2.lib") -#pragma comment(lib, "d3d9.lib") -#pragma comment(lib, "mf.lib") -#pragma comment(lib, "mfplat.lib") - -using base::TimeDelta; - -namespace { - -// Creates an empty Media Foundation sample with no buffers. -static IMFSample* CreateEmptySample() { - HRESULT hr; - ScopedComPtr<IMFSample> sample; - hr = MFCreateSample(sample.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Unable to create an empty sample"; - return NULL; - } - return sample.Detach(); -} - -// Creates a Media Foundation sample with one buffer of length |buffer_length| -// on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. -// If |align| is 0, then no alignment is specified. -static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { - CHECK_GT(buffer_length, 0); - ScopedComPtr<IMFSample> sample; - sample.Attach(CreateEmptySample()); - if (!sample.get()) - return NULL; - ScopedComPtr<IMFMediaBuffer> buffer; - HRESULT hr; - if (align == 0) { - // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer - // with the align argument being 0. - hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); - } else { - hr = MFCreateAlignedMemoryBuffer(buffer_length, - align - 1, - buffer.Receive()); - } - if (FAILED(hr)) { - LOG(ERROR) << "Unable to create an empty buffer"; - return NULL; - } - hr = sample->AddBuffer(buffer.get()); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to add empty buffer to sample"; - return NULL; - } - return sample.Detach(); -} - -// Creates a Media Foundation sample with one buffer containing a copy of the -// given Annex B stream data. -// If duration and sample time are not known, provide 0. -// |min_size| specifies the minimum size of the buffer (might be required by -// the decoder for input). The times here should be given in 100ns units. -// |alignment| specifies the buffer in the sample to be aligned. If no -// alignment is required, provide 0 or 1. -static IMFSample* CreateInputSample(const uint8* stream, int size, - int64 timestamp, int64 duration, - int min_size, int alignment) { - CHECK(stream); - CHECK_GT(size, 0); - ScopedComPtr<IMFSample> sample; - sample.Attach(CreateEmptySampleWithBuffer(std::max(min_size, size), - alignment)); - if (!sample.get()) { - LOG(ERROR) << "Failed to create empty buffer for input"; - return NULL; - } - HRESULT hr; - if (duration > 0) { - hr = sample->SetSampleDuration(duration); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set sample duration"; - return NULL; - } - } - if (timestamp > 0) { - hr = sample->SetSampleTime(timestamp); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set sample time"; - return NULL; - } - } - ScopedComPtr<IMFMediaBuffer> buffer; - hr = sample->GetBufferByIndex(0, buffer.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to get buffer in sample"; - return NULL; - } - DWORD max_length, current_length; - uint8* destination; - hr = buffer->Lock(&destination, &max_length, ¤t_length); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to lock buffer"; - return NULL; - } - CHECK_EQ(current_length, 0u); - CHECK_GE(static_cast<int>(max_length), size); - memcpy(destination, stream, size); - CHECK(SUCCEEDED(buffer->Unlock())); - hr = buffer->SetCurrentLength(size); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set current length to " << size; - return NULL; - } - LOG(INFO) << __FUNCTION__ << " wrote " << size << " bytes into input sample"; - return sample.Detach(); -} - -const GUID ConvertVideoFrameFormatToGuid(media::VideoFrame::Format format) { - switch (format) { - case media::VideoFrame::NV12: - return MFVideoFormat_NV12; - case media::VideoFrame::YV12: - return MFVideoFormat_YV12; - default: - NOTREACHED() << "Unsupported VideoFrame format"; - return GUID_NULL; - } - NOTREACHED(); - return GUID_NULL; -} - -} // namespace - -namespace media { - -// public methods - -MftH264DecodeEngine::MftH264DecodeEngine(bool use_dxva) - : use_dxva_(use_dxva), - state_(kUninitialized), - event_handler_(NULL) { - memset(&input_stream_info_, 0, sizeof(input_stream_info_)); - memset(&output_stream_info_, 0, sizeof(output_stream_info_)); - memset(&config_, 0, sizeof(config_)); - memset(&info_, 0, sizeof(info_)); -} - -MftH264DecodeEngine::~MftH264DecodeEngine() { -} - -void MftH264DecodeEngine::Initialize( - MessageLoop* message_loop, - VideoDecodeEngine::EventHandler* event_handler, - VideoDecodeContext* context, - const VideoCodecConfig& config) { - DCHECK(!use_dxva_ || context); - if (state_ != kUninitialized) { - LOG(ERROR) << "Initialize: invalid state"; - return; - } - if (!message_loop || !event_handler) { - LOG(ERROR) << "MftH264DecodeEngine::Initialize: parameters cannot be NULL"; - return; - } - context_ = context; - config_ = config; - event_handler_ = event_handler; - info_.provides_buffers = true; - - if (use_dxva_) { - info_.stream_info.surface_format = VideoFrame::NV12; - // TODO(hclam): Need to correct this since this is not really GL texture. - // We should just remove surface_type from stream_info. - info_.stream_info.surface_type = VideoFrame::TYPE_GL_TEXTURE; - } else { - info_.stream_info.surface_format = VideoFrame::YV12; - info_.stream_info.surface_type = VideoFrame::TYPE_SYSTEM_MEMORY; - } - - // codec_info.stream_info_.surface_width_/height_ are initialized - // in InitInternal(). - info_.success = InitInternal(); - if (info_.success) { - state_ = kNormal; - AllocFramesFromContext(); - } else { - LOG(ERROR) << "MftH264DecodeEngine::Initialize failed"; - event_handler_->OnInitializeComplete(info_); - } -} - -void MftH264DecodeEngine::Uninitialize() { - if (state_ == kUninitialized) { - LOG(ERROR) << "Uninitialize: invalid state"; - return; - } - - // TODO(hclam): Call ShutdownComLibraries only after MFT is released. - decode_engine_.Release(); - ShutdownComLibraries(); - state_ = kUninitialized; - event_handler_->OnUninitializeComplete(); -} - -void MftH264DecodeEngine::Flush() { - if (state_ != kNormal) { - LOG(ERROR) << "Flush: invalid state"; - return; - } - state_ = kFlushing; - if (!SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH)) { - LOG(WARNING) << "MftH264DecodeEngine::Flush failed to send message"; - } - state_ = kNormal; - event_handler_->OnFlushComplete(); -} - -void MftH264DecodeEngine::Seek() { - if (state_ != kNormal) { - LOG(ERROR) << "Seek: invalid state"; - return; - } - - // TODO(hclam): Seriously the logic in VideoRendererBase is flawed that we - // have to perform the following hack to get playback going. - for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) { - event_handler_->ConsumeVideoFrame(output_frames_[0]); - } - - // Seek not implemented. - event_handler_->OnSeekComplete(); -} - -void MftH264DecodeEngine::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { - if (state_ == kUninitialized) { - LOG(ERROR) << "ConsumeVideoSample: invalid state"; - } - ScopedComPtr<IMFSample> sample; - if (!buffer->IsEndOfStream()) { - sample.Attach( - CreateInputSample(buffer->GetData(), - buffer->GetDataSize(), - buffer->GetTimestamp().InMicroseconds() * 10, - buffer->GetDuration().InMicroseconds() * 10, - input_stream_info_.cbSize, - input_stream_info_.cbAlignment)); - if (!sample.get()) { - LOG(ERROR) << "Failed to create an input sample"; - } else { - if (FAILED(decode_engine_->ProcessInput(0, sample.get(), 0))) { - event_handler_->OnError(); - } - } - } else { - if (state_ != MftH264DecodeEngine::kEosDrain) { - // End of stream, send drain messages. - if (!SendMFTMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM) || - !SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN)) { - LOG(ERROR) << "Failed to send EOS / drain messages to MFT"; - event_handler_->OnError(); - } else { - state_ = MftH264DecodeEngine::kEosDrain; - } - } - } - DoDecode(); -} - -void MftH264DecodeEngine::ProduceVideoFrame(scoped_refptr<VideoFrame> frame) { - if (state_ == kUninitialized) { - LOG(ERROR) << "ProduceVideoFrame: invalid state"; - return; - } - event_handler_->ProduceVideoSample(NULL); -} - -// private methods - -// static -bool MftH264DecodeEngine::StartupComLibraries() { - HRESULT hr; - hr = CoInitializeEx(NULL, - COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - if (FAILED(hr)) { - LOG(ERROR) << "CoInit fail"; - return false; - } - - hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - if (FAILED(hr)) { - LOG(ERROR) << "MFStartup fail"; - CoUninitialize(); - return false; - } - return true; -} - -// static -void MftH264DecodeEngine::ShutdownComLibraries() { - HRESULT hr; - hr = MFShutdown(); - if (FAILED(hr)) { - LOG(WARNING) << "Warning: MF failed to shutdown"; - } - CoUninitialize(); -} - -bool MftH264DecodeEngine::EnableDxva() { - IDirect3DDevice9* device = static_cast<IDirect3DDevice9*>( - context_->GetDevice()); - ScopedComPtr<IDirect3DDeviceManager9> device_manager; - UINT dev_manager_reset_token = 0; - HRESULT hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, - device_manager.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Couldn't create D3D Device manager"; - return false; - } - - hr = device_manager->ResetDevice(device, dev_manager_reset_token); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to reset device"; - return false; - } - - hr = decode_engine_->ProcessMessage( - MFT_MESSAGE_SET_D3D_MANAGER, - reinterpret_cast<ULONG_PTR>(device_manager.get())); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set D3D9 device manager to decoder " - << std::hex << hr; - return false; - } - - return true; -} - -bool MftH264DecodeEngine::InitInternal() { - if (!StartupComLibraries()) - return false; - if (!InitDecodeEngine()) - return false; - if (!GetStreamsInfoAndBufferReqs()) - return false; - return SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING); -} - -bool MftH264DecodeEngine::InitDecodeEngine() { - // TODO(jiesun): use MFEnum to get decoder CLSID. - HRESULT hr = CoCreateInstance(__uuidof(CMSH264DecoderMFT), - NULL, - CLSCTX_INPROC_SERVER, - __uuidof(IMFTransform), - reinterpret_cast<void**>( - decode_engine_.Receive())); - if (FAILED(hr) || !decode_engine_.get()) { - LOG(ERROR) << "CoCreateInstance failed " << std::hex << std::showbase << hr; - return false; - } - if (!CheckDecodeEngineDxvaSupport()) - return false; - if (use_dxva_ && !EnableDxva()) - return false; - return SetDecodeEngineMediaTypes(); -} - -void MftH264DecodeEngine::AllocFramesFromContext() { - if (!use_dxva_) - return; - - // TODO(imcheng): Pass in an actual task. (From EventHandler?) - context_->ReleaseAllVideoFrames(); - output_frames_.clear(); - context_->AllocateVideoFrames( - 1, info_.stream_info.surface_width, info_.stream_info.surface_height, - VideoFrame::RGBA, &output_frames_, - NewRunnableMethod(this, &MftH264DecodeEngine::OnAllocFramesDone)); -} - -void MftH264DecodeEngine::OnAllocFramesDone() { - event_handler_->OnInitializeComplete(info_); -} - -bool MftH264DecodeEngine::CheckDecodeEngineDxvaSupport() { - ScopedComPtr<IMFAttributes> attributes; - HRESULT hr = decode_engine_->GetAttributes(attributes.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Unlock: Failed to get attributes, hr = " - << std::hex << std::showbase << hr; - return false; - } - - UINT32 dxva; - hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); - if (FAILED(hr) || !dxva) { - LOG(ERROR) << "Failed to get DXVA attr or decoder is not DXVA-aware, hr = " - << std::hex << std::showbase << hr - << " this might not be the right decoder."; - return false; - } - return true; -} - -bool MftH264DecodeEngine::SetDecodeEngineMediaTypes() { - if (!SetDecodeEngineInputMediaType()) - return false; - return SetDecodeEngineOutputMediaType( - ConvertVideoFrameFormatToGuid(info_.stream_info.surface_format)); -} - -bool MftH264DecodeEngine::SetDecodeEngineInputMediaType() { - ScopedComPtr<IMFMediaType> media_type; - HRESULT hr = MFCreateMediaType(media_type.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to create empty media type object"; - return false; - } - - hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - if (FAILED(hr)) { - LOG(ERROR) << "SetGUID for major type failed"; - return false; - } - - hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); - if (FAILED(hr)) { - LOG(ERROR) << "SetGUID for subtype failed"; - return false; - } - - hr = decode_engine_->SetInputType(0, media_type.get(), 0); // No flags - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set decoder's input type"; - return false; - } - - return true; -} - -bool MftH264DecodeEngine::SetDecodeEngineOutputMediaType(const GUID subtype) { - DWORD i = 0; - IMFMediaType* out_media_type; - bool found = false; - while (SUCCEEDED(decode_engine_->GetOutputAvailableType(0, i, - &out_media_type))) { - GUID out_subtype; - HRESULT hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to GetGUID() on GetOutputAvailableType() " << i; - out_media_type->Release(); - continue; - } - if (out_subtype == subtype) { - hr = decode_engine_->SetOutputType(0, out_media_type, 0); // No flags - hr = MFGetAttributeSize(out_media_type, MF_MT_FRAME_SIZE, - reinterpret_cast<UINT32*>(&info_.stream_info.surface_width), - reinterpret_cast<UINT32*>(&info_.stream_info.surface_height)); - config_.width = info_.stream_info.surface_width; - config_.height = info_.stream_info.surface_height; - if (FAILED(hr)) { - LOG(ERROR) << "Failed to SetOutputType to |subtype| or obtain " - << "width/height " << std::hex << hr; - } else { - out_media_type->Release(); - return true; - } - } - i++; - out_media_type->Release(); - } - return false; -} - -bool MftH264DecodeEngine::SendMFTMessage(MFT_MESSAGE_TYPE msg) { - HRESULT hr = decode_engine_->ProcessMessage(msg, NULL); - return SUCCEEDED(hr); -} - -// Prints out info about the input/output streams, gets the minimum buffer sizes -// for input and output samples. -// The MFT will not allocate buffer for neither input nor output, so we have -// to do it ourselves and make sure they're the correct size. -// Exception is when dxva is enabled, the decoder will allocate output. -bool MftH264DecodeEngine::GetStreamsInfoAndBufferReqs() { - HRESULT hr = decode_engine_->GetInputStreamInfo(0, &input_stream_info_); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to get input stream info"; - return false; - } - LOG(INFO) << "Input stream info: "; - LOG(INFO) << "Max latency: " << input_stream_info_.hnsMaxLatency; - - // There should be three flags, one for requiring a whole frame be in a - // single sample, one for requiring there be one buffer only in a single - // sample, and one that specifies a fixed sample size. (as in cbSize) - LOG(INFO) << "Flags: " - << std::hex << std::showbase << input_stream_info_.dwFlags; - CHECK_EQ(input_stream_info_.dwFlags, 0x7u); - LOG(INFO) << "Min buffer size: " << input_stream_info_.cbSize; - LOG(INFO) << "Max lookahead: " << input_stream_info_.cbMaxLookahead; - LOG(INFO) << "Alignment: " << input_stream_info_.cbAlignment; - - hr = decode_engine_->GetOutputStreamInfo(0, &output_stream_info_); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to get output stream info"; - return false; - } - LOG(INFO) << "Output stream info: "; - // The flags here should be the same and mean the same thing, except when - // DXVA is enabled, there is an extra 0x100 flag meaning decoder will - // allocate its own sample. - LOG(INFO) << "Flags: " - << std::hex << std::showbase << output_stream_info_.dwFlags; - CHECK_EQ(output_stream_info_.dwFlags, use_dxva_ ? 0x107u : 0x7u); - LOG(INFO) << "Min buffer size: " << output_stream_info_.cbSize; - LOG(INFO) << "Alignment: " << output_stream_info_.cbAlignment; - - return true; -} - -bool MftH264DecodeEngine::DoDecode() { - if (state_ != kNormal && state_ != kEosDrain) { - LOG(ERROR) << "DoDecode: not in normal or drain state"; - return false; - } - scoped_refptr<VideoFrame> frame; - ScopedComPtr<IMFSample> output_sample; - if (!use_dxva_) { - output_sample.Attach( - CreateEmptySampleWithBuffer(output_stream_info_.cbSize, - output_stream_info_.cbAlignment)); - if (!output_sample.get()) { - LOG(ERROR) << "GetSample: failed to create empty output sample"; - event_handler_->OnError(); - return false; - } - } - MFT_OUTPUT_DATA_BUFFER output_data_buffer; - memset(&output_data_buffer, 0, sizeof(output_data_buffer)); - output_data_buffer.dwStreamID = 0; - output_data_buffer.pSample = output_sample; - - DWORD status; - HRESULT hr = decode_engine_->ProcessOutput(0, // No flags - 1, // # of out streams to pull - &output_data_buffer, - &status); - - IMFCollection* events = output_data_buffer.pEvents; - if (events != NULL) { - LOG(INFO) << "Got events from ProcessOuput, but discarding"; - events->Release(); - } - - if (FAILED(hr)) { - if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { - hr = SetDecodeEngineOutputMediaType( - ConvertVideoFrameFormatToGuid(info_.stream_info.surface_format)); - if (SUCCEEDED(hr)) { - // TODO(hclam): Need to fix this case. This happens when we have a - // format change. We have to resume decoding only after we have - // allocated a new set of video frames. - // AllocFramesFromContext(); - // event_handler_->OnFormatChange(info_.stream_info); - event_handler_->ProduceVideoSample(NULL); - return true; - } else { - event_handler_->OnError(); - return false; - } - } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - if (state_ == kEosDrain) { - // No more output from the decoder. Notify EOS and stop playback. - scoped_refptr<VideoFrame> frame; - VideoFrame::CreateEmptyFrame(&frame); - event_handler_->ConsumeVideoFrame(frame); - state_ = MftH264DecodeEngine::kStopped; - return false; - } - event_handler_->ProduceVideoSample(NULL); - return true; - } else { - LOG(ERROR) << "Unhandled error in DoDecode()"; - state_ = MftH264DecodeEngine::kStopped; - event_handler_->OnError(); - return false; - } - } - - // We succeeded in getting an output sample. - if (use_dxva_) { - // For DXVA we didn't provide the sample, i.e. output_sample was NULL. - output_sample.Attach(output_data_buffer.pSample); - } - if (!output_sample.get()) { - LOG(ERROR) << "ProcessOutput succeeded, but did not get a sample back"; - event_handler_->OnError(); - return true; - } - - int64 timestamp = 0, duration = 0; - if (FAILED(output_sample->GetSampleTime(×tamp)) || - FAILED(output_sample->GetSampleDuration(&duration))) { - LOG(WARNING) << "Failed to get timestamp/duration from output"; - } - - // The duration and timestamps are in 100-ns units, so divide by 10 - // to convert to microseconds. - timestamp /= 10; - duration /= 10; - - // Sanity checks for checking if there is really something in the sample. - DWORD buf_count; - hr = output_sample->GetBufferCount(&buf_count); - if (FAILED(hr) || buf_count != 1) { - LOG(ERROR) << "Failed to get buffer count, or buffer count mismatch"; - return true; - } - - ScopedComPtr<IMFMediaBuffer> output_buffer; - hr = output_sample->GetBufferByIndex(0, output_buffer.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to get buffer from sample"; - return true; - } - if (use_dxva_) { - ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface; - hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, - IID_PPV_ARGS(surface.Receive())); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to get surface from buffer"; - return true; - } - // Since we only allocated 1 frame from context. - // TODO(imcheng): Detect error. - output_frames_[0]->SetTimestamp(TimeDelta::FromMicroseconds(timestamp)); - output_frames_[0]->SetDuration(TimeDelta::FromMicroseconds(duration)); - context_->UploadToVideoFrame( - surface.get(), output_frames_[0], - NewRunnableMethod(this, &MftH264DecodeEngine::OnUploadVideoFrameDone, - surface, output_frames_[0])); - return true; - } else { - // TODO(hclam): Remove this branch. - // Not DXVA. - VideoFrame::CreateFrame(info_.stream_info.surface_format, - info_.stream_info.surface_width, - info_.stream_info.surface_height, - TimeDelta::FromMicroseconds(timestamp), - TimeDelta::FromMicroseconds(duration), - &frame); - if (!frame.get()) { - LOG(ERROR) << "Failed to allocate video frame for yuv plane"; - event_handler_->OnError(); - return true; - } - uint8* src_y; - DWORD max_length, current_length; - HRESULT hr = output_buffer->Lock(&src_y, &max_length, ¤t_length); - if (FAILED(hr)) - return true; - uint8* dst_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); - - memcpy(dst_y, src_y, current_length); - CHECK(SUCCEEDED(output_buffer->Unlock())); - event_handler_->ConsumeVideoFrame(frame); - return true; - } -} - -void MftH264DecodeEngine::OnUploadVideoFrameDone( - ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface, - scoped_refptr<media::VideoFrame> frame) { - // After this method is exited the reference to surface is released. - event_handler_->ConsumeVideoFrame(frame); -} - -} // namespace media - -DISABLE_RUNNABLE_METHOD_REFCOUNT(media::MftH264DecodeEngine); diff --git a/media/video/mft_h264_decode_engine.h b/media/video/mft_h264_decode_engine.h deleted file mode 100644 index e13dce9..0000000 --- a/media/video/mft_h264_decode_engine.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// MFT H.264 decode engine. - -#ifndef MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_H_ -#define MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_H_ - -// TODO(imcheng): Get rid of this header by: -// - forward declaring IMFTransform and its IID as in -// mft_h264_decode_engine_context.h -// - turning the general SendMFTMessage method into specific methods -// (SendFlushMessage, SendDrainMessage, etc.) to avoid having -// MFT_MESSAGE_TYPE in here -#include <mfidl.h> - -#include "base/gtest_prod_util.h" -#include "base/scoped_comptr_win.h" -#include "media/video/video_decode_engine.h" - -struct IDirect3DSurface9; -extern "C" const GUID IID_IDirect3DSurface9; - -class MessageLoop; - -namespace media { - -class VideoDecodeContext; - -class MftH264DecodeEngine : public media::VideoDecodeEngine { - public: - typedef enum { - kUninitialized, // un-initialized. - kNormal, // normal playing state. - kFlushing, // upon received Flush(), before FlushDone() - kEosDrain, // upon input EOS received. - kStopped, // upon output EOS received. - } State; - - explicit MftH264DecodeEngine(bool use_dxva); - virtual ~MftH264DecodeEngine(); - - // VideoDecodeEngine implementation. - virtual void Initialize(MessageLoop* message_loop, - media::VideoDecodeEngine::EventHandler* event_handler, - VideoDecodeContext* context, - const VideoCodecConfig& config); - virtual void Uninitialize(); - virtual void Flush(); - virtual void Seek(); - virtual void ConsumeVideoSample(scoped_refptr<Buffer> buffer); - virtual void ProduceVideoFrame(scoped_refptr<VideoFrame> frame); - - bool use_dxva() const { return use_dxva_; } - State state() const { return state_; } - - private: - friend class MftH264DecodeEngineTest; - FRIEND_TEST_ALL_PREFIXES(MftH264DecodeEngineTest, LibraryInit); - - // TODO(jiesun): Find a way to move all these to GpuVideoService.. - static bool StartupComLibraries(); - static void ShutdownComLibraries(); - bool EnableDxva(); - - bool InitInternal(); - bool InitDecodeEngine(); - void AllocFramesFromContext(); - bool CheckDecodeEngineDxvaSupport(); - bool SetDecodeEngineMediaTypes(); - bool SetDecodeEngineInputMediaType(); - bool SetDecodeEngineOutputMediaType(const GUID subtype); - bool SendMFTMessage(MFT_MESSAGE_TYPE msg); - bool GetStreamsInfoAndBufferReqs(); - bool DoDecode(); - void OnAllocFramesDone(); - void OnUploadVideoFrameDone( - ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface, - scoped_refptr<media::VideoFrame> frame); - - bool use_dxva_; - ScopedComPtr<IMFTransform> decode_engine_; - - MFT_INPUT_STREAM_INFO input_stream_info_; - MFT_OUTPUT_STREAM_INFO output_stream_info_; - - State state_; - - VideoDecodeEngine::EventHandler* event_handler_; - VideoCodecConfig config_; - VideoCodecInfo info_; - - VideoDecodeContext* context_; - std::vector<scoped_refptr<VideoFrame> > output_frames_; - - DISALLOW_COPY_AND_ASSIGN(MftH264DecodeEngine); -}; - -} // namespace media - -#endif // MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_H_ diff --git a/media/video/mft_h264_decode_engine_context.cc b/media/video/mft_h264_decode_engine_context.cc new file mode 100644 index 0000000..1759ced --- /dev/null +++ b/media/video/mft_h264_decode_engine_context.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/video/mft_h264_decode_engine_context.h" + +#include <algorithm> +#include <vector> + +#include <d3d9.h> + +#include "base/task.h" +#include "media/base/callback.h" + +#pragma comment(lib, "dxva2.lib") +#pragma comment(lib, "d3d9.lib") + +using base::TimeDelta; + +namespace media { + +static D3DFORMAT VideoFrameToD3DFormat(VideoFrame::Format format) { + switch (format) { + case VideoFrame::RGB555: + return D3DFMT_X1R5G5B5; + case VideoFrame::RGB565: + return D3DFMT_R5G6B5; + case VideoFrame::RGB32: + return D3DFMT_X8R8G8B8; + case VideoFrame::RGBA: + return D3DFMT_A8R8G8B8; + default: + // Note that although there is a corresponding type for VideoFrame::RGB24 + // (D3DFMT_R8G8B8), it is not supported by render targets. + NOTREACHED() << "Unsupported format"; + return D3DFMT_UNKNOWN; + } +} + +static IDirect3DTexture9* GetTexture(scoped_refptr<VideoFrame> frame) { + return static_cast<IDirect3DTexture9*>(frame->d3d_texture(0)); +} + +static void ReleaseTexture(scoped_refptr<VideoFrame> frame) { + GetTexture(frame)->Release(); +} + +static void ReleaseTextures( + const std::vector<scoped_refptr<VideoFrame> >& frames) { + std::for_each(frames.begin(), frames.end(), ReleaseTexture); +} + +MftH264DecodeEngineContext::MftH264DecodeEngineContext(HWND device_window) + : initialized_(false), + device_window_(device_window), + d3d9_(NULL), + device_(NULL) { + DCHECK(device_window); +} + +MftH264DecodeEngineContext::~MftH264DecodeEngineContext() { +} + +// TODO(imcheng): This should set the success variable once the API is +// finalized. +void MftH264DecodeEngineContext::Initialize(Task* task) { + AutoTaskRunner runner(task); + if (initialized_) + return; + d3d9_ = Direct3DCreate9(D3D_SDK_VERSION); + if (!d3d9_) { + LOG(ERROR) << "Direct3DCreate9 failed"; + return; + } + + D3DPRESENT_PARAMETERS present_params = {0}; + present_params.BackBufferWidth = 0; + present_params.BackBufferHeight = 0; + present_params.BackBufferFormat = D3DFMT_UNKNOWN; + present_params.BackBufferCount = 1; + present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; + present_params.hDeviceWindow = device_window_; + present_params.Windowed = TRUE; + present_params.Flags = D3DPRESENTFLAG_VIDEO; + present_params.FullScreen_RefreshRateInHz = 0; + present_params.PresentationInterval = 0; + + HRESULT hr = d3d9_->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + device_window_, + (D3DCREATE_HARDWARE_VERTEXPROCESSING | + D3DCREATE_MULTITHREADED), + &present_params, + device_.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "CreateDevice failed " << std::hex << hr; + return; + } + initialized_ = true; +} + +void* MftH264DecodeEngineContext::GetDevice() { + return device_.get(); +} + +void MftH264DecodeEngineContext::AllocateVideoFrames( + int n, size_t width, size_t height, VideoFrame::Format format, + std::vector<scoped_refptr<VideoFrame> >* frames, + Task* task) { + DCHECK(initialized_); + DCHECK_GT(n, 0); + DCHECK(frames); + + AutoTaskRunner runner(task); + D3DFORMAT d3d_format = VideoFrameToD3DFormat(format); + std::vector<scoped_refptr<VideoFrame> > temp_frames; + temp_frames.reserve(n); + HRESULT hr; + for (int i = 0; i < n; i++) { + IDirect3DTexture9* texture = NULL; + hr = device_->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, + d3d_format, D3DPOOL_DEFAULT, &texture, NULL); + if (FAILED(hr)) { + LOG(ERROR) << "CreateTexture " << i << " failed " << std::hex << hr; + ReleaseTextures(temp_frames); + return; + } + VideoFrame::D3dTexture texture_array[VideoFrame::kMaxPlanes] = + { texture, texture, texture }; + scoped_refptr<VideoFrame> texture_frame; + VideoFrame::CreateFrameD3dTexture(format, width, height, texture_array, + TimeDelta(), TimeDelta(), &texture_frame); + if (!texture_frame.get()) { + LOG(ERROR) << "CreateFrameD3dTexture " << i << " failed"; + texture->Release(); + ReleaseTextures(temp_frames); + return; + } + temp_frames.push_back(texture_frame); + } + frames->assign(temp_frames.begin(), temp_frames.end()); + managed_frames_.insert(managed_frames_.end(), + temp_frames.begin(), temp_frames.end()); +} + +bool MftH264DecodeEngineContext::UploadToVideoFrame( + void* source, scoped_refptr<VideoFrame> frame) { + DCHECK(initialized_); + DCHECK(source); + DCHECK(frame.get()); + + IDirect3DSurface9* surface = static_cast<IDirect3DSurface9*>(source); + IDirect3DTexture9* texture = GetTexture(frame); + ScopedComPtr<IDirect3DSurface9> top_surface; + HRESULT hr; + hr = texture->GetSurfaceLevel(0, top_surface.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "GetSurfaceLevel failed " << std::hex << hr; + return false; + } + hr = device_->StretchRect(surface, NULL, top_surface.get(), NULL, + D3DTEXF_NONE); + if (FAILED(hr)) { + LOG(ERROR) << "StretchRect failed " << std::hex << hr; + return false; + } + return true; +} + +void MftH264DecodeEngineContext::ReleaseAllVideoFrames() { + ReleaseTextures(managed_frames_); + managed_frames_.clear(); +} + +void MftH264DecodeEngineContext::Destroy(Task* task) { + AutoTaskRunner runner(task); +} + +} // namespace media diff --git a/media/video/mft_h264_decode_engine_context.h b/media/video/mft_h264_decode_engine_context.h new file mode 100644 index 0000000..d33f06c --- /dev/null +++ b/media/video/mft_h264_decode_engine_context.h @@ -0,0 +1,70 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Video decode context for MftH264DecodeEngine. This context manages +// VideoFrame objects for the DXVA-enabled MFT H.264 decode engine, and +// converts its output (which is IDirect3DSurface9) into IDirect3DTexture9 +// (wrapped in a VideoFrame object), which will be compatible with ANGLE. + +#ifndef MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_CONTEXT_H_ +#define MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_CONTEXT_H_ + +#include <vector> + +#include "base/scoped_comptr_win.h" +#include "media/base/video_frame.h" +#include "media/video/video_decode_context.h" + +class Task; + +struct IDirect3D9; +extern "C" const GUID IID_IDirect3D9; +struct IDirect3DDevice9; +extern "C" const GUID IID_IDirect3DDevice9; + +namespace media { + +// TODO(imcheng): Make it implement VideoDecodeContext once the API +// is finalized. +class MftH264DecodeEngineContext { + public: + // Constructs a MftH264DecodeEngineContext with the D3D device attached + // to |device_window|. This device does not own the window, so the caller + // must destroy the window explicitly after the destruction of this object. + explicit MftH264DecodeEngineContext(HWND device_window); + virtual ~MftH264DecodeEngineContext(); + + // TODO(imcheng): Is this a part of the API? + virtual void Initialize(Task* task); + + // Gets the underlying IDirect3DDevice9. + virtual void* GetDevice(); + + // Allocates IDirect3DTexture9 objects wrapped in VideoFrame objects. + virtual void AllocateVideoFrames( + int n, size_t width, size_t height, VideoFrame::Format format, + std::vector<scoped_refptr<VideoFrame> >* frames, + Task* task); + + // TODO(imcheng): Make this follow the API once it is finalized. + // Uploads the decoded frame (IDirect3DSurface9) to a VideoFrame allocated + // by AllocateVideoFrames(). + virtual bool UploadToVideoFrame(void* source, + scoped_refptr<VideoFrame> frame); + virtual void ReleaseAllVideoFrames(); + virtual void Destroy(Task* task); + + bool initialized() const { return initialized_; } + + private: + bool initialized_; + HWND device_window_; + std::vector<scoped_refptr<VideoFrame> > managed_frames_; + ScopedComPtr<IDirect3D9, &IID_IDirect3D9> d3d9_; + ScopedComPtr<IDirect3DDevice9, &IID_IDirect3DDevice9> device_; +}; + +} // namespace media + +#endif // MEDIA_VIDEO_MFT_H264_DECODE_ENGINE_CONTEXT_H_ diff --git a/media/video/mft_h264_decode_engine_unittest.cc b/media/video/mft_h264_decode_engine_unittest.cc deleted file mode 100644 index fcf7d69..0000000 --- a/media/video/mft_h264_decode_engine_unittest.cc +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/message_loop.h" -#include "base/path_service.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/tools/mft_h264_example/file_reader_util.h" -#include "media/video/mft_h264_decode_engine.h" -#include "media/video/mft_h264_decode_engine_context.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::TimeDelta; - -namespace media { - -static const int kDecoderMaxWidth = 1920; -static const int kDecoderMaxHeight = 1088; - -// Helper classes - -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) {} - virtual ~FakeMftReader() {} - - // Provides garbage input to the decoder. - virtual void ReadCallback(scoped_refptr<DataBuffer>* input) { - if (frames_remaining_ > 0) { - int sz = 4096; - uint8* buf = new uint8[sz]; - memset(buf, 42, sz); - *input = new DataBuffer(buf, sz); - (*input)->SetDuration(base::TimeDelta::FromMicroseconds(5000)); - (*input)->SetTimestamp( - base::TimeDelta::FromMicroseconds( - 50000000 - frames_remaining_ * 10000)); - --frames_remaining_; - } else { - // Emulate end of stream on the last "frame". - *input = new DataBuffer(0); - } - } - int frames_remaining() const { return frames_remaining_; } - - private: - int frames_remaining_; -}; - -class SimpleMftH264DecodeEngineHandler - : public VideoDecodeEngine::EventHandler { - public: - SimpleMftH264DecodeEngineHandler() - : 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 ~SimpleMftH264DecodeEngineHandler() {} - 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 ProduceVideoSample(scoped_refptr<Buffer> buffer) { - if (reader_.get() && decoder_) { - empty_buffer_callback_count_++; - scoped_refptr<DataBuffer> input; - reader_->ReadCallback(&input); - decoder_->ConsumeVideoSample(input); - } - } - virtual void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { - fill_buffer_callback_count_++; - current_frame_ = frame; - } - void SetReader(scoped_refptr<BaseMftReader> reader) { - reader_ = reader; - } - void SetDecodeEngine(MftH264DecodeEngine* 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_; - MftH264DecodeEngine* decoder_; - scoped_refptr<VideoFrame> current_frame_; -}; - -class FFmpegFileReaderWrapper : public BaseMftReader { - public: - 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 void ReadCallback(scoped_refptr<DataBuffer>* input) { - if (reader_.get()) { - reader_->Read(input); - } - } - bool GetWidth(int* width) { - if (!reader_.get()) - return false; - return reader_->GetWidth(width); - } - bool GetHeight(int* height) { - if (!reader_.get()) - return false; - return reader_->GetHeight(height); - } - scoped_ptr<FFmpegFileReader> reader_; -}; - -// Helper functions - -static FilePath GetVideoFilePath(const std::string& file_name) { - FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("media") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII(file_name.c_str()); - return path; -} - -class MftH264DecodeEngineTest : public testing::Test { - protected: - MftH264DecodeEngineTest() - : loop_(), - window_(NULL), - handler_(NULL), - engine_(NULL), - context_(NULL) { - } - virtual ~MftH264DecodeEngineTest() {} - virtual void SetUp() { - handler_.reset(new SimpleMftH264DecodeEngineHandler()); - } - virtual void TearDown() { - if (context_.get()) { - context_->ReleaseAllVideoFrames(); - context_->Destroy(NULL); - } - if (window_) - DestroyWindow(window_); - } - void GetDecodeEngine(bool dxva) { - if (dxva) { - if (!window_) - CreateDrawWindow(); - context_.reset(new MftH264DecodeEngineContext(window_)); - ASSERT_TRUE(context_.get()); - context_->Initialize(NULL); - ASSERT_TRUE(context_->initialized()); - } - engine_.reset(new MftH264DecodeEngine(dxva)); - ASSERT_TRUE(engine_.get()); - } - void InitDecodeEngine(int width, int height) { - VideoCodecConfig config; - config.width = width; - config.height = height; - - // Note that although |config| is passed as reference, |config| is copied - // into the decode engine, so it is okay to make |config| a local variable. - engine_->Initialize(&loop_, handler_.get(), context_.get(), config); - EXPECT_EQ(1, handler_->init_count_); - EXPECT_EQ(MftH264DecodeEngine::kNormal, engine_->state()); - } - void InitDecodeEngine() { - InitDecodeEngine(800, 600); - } - void TestInitAndUninit(bool dxva) { - GetDecodeEngine(dxva); - InitDecodeEngine(); - engine_->Uninitialize(); - } - void DecodeAll(scoped_refptr<BaseMftReader> reader) { - handler_->SetReader(reader); - handler_->SetDecodeEngine(engine_.get()); - while (MftH264DecodeEngine::kStopped != engine_->state()) { - scoped_refptr<VideoFrame> frame; - engine_->ProduceVideoFrame(frame); - } - } - 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)); - - VideoCodecConfig config; - CreateDrawWindow(config.width, config.height); - GetDecodeEngine(dxva); - InitDecodeEngine(); - DecodeAll(reader); - - // 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); - engine_->Uninitialize(); - } - void ExpectDefaultDimensionsOnInput(int width, int height) { - GetDecodeEngine(false); - InitDecodeEngine(width, height); - EXPECT_EQ(kDecoderMaxWidth, handler_->info_.stream_info.surface_width); - EXPECT_EQ(kDecoderMaxHeight, handler_->info_.stream_info.surface_height); - engine_->Uninitialize(); - } - - scoped_ptr<SimpleMftH264DecodeEngineHandler> handler_; - scoped_ptr<MftH264DecodeEngine> engine_; - scoped_ptr<MftH264DecodeEngineContext> context_; - - private: - void CreateDrawWindow(int width, int height) { - static const wchar_t kClassName[] = L"Test"; - static const wchar_t kWindowTitle[] = L"MFT Unittest Draw Window"; - WNDCLASS window_class = {0}; - window_class.lpszClassName = kClassName; - window_class.hInstance = NULL; - window_class.hbrBackground = 0; - window_class.lpfnWndProc = DefWindowProc; - window_class.hCursor = 0; - RegisterClass(&window_class); - window_ = CreateWindow(kClassName, - kWindowTitle, - (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & - ~(WS_MAXIMIZEBOX | WS_THICKFRAME), - 100, 100, width, height, - NULL, NULL, NULL, NULL); - ASSERT_TRUE(window_); - } - void CreateDrawWindow() { - CreateDrawWindow(800, 600); - } - - MessageLoop loop_; - HWND window_; -}; - -// A simple test case for init/deinit of MF/COM libraries. -TEST_F(MftH264DecodeEngineTest, LibraryInit) { - EXPECT_TRUE(MftH264DecodeEngine::StartupComLibraries()); - MftH264DecodeEngine::ShutdownComLibraries(); -} - -TEST_F(MftH264DecodeEngineTest, DecoderUninitializedAtFirst) { - GetDecodeEngine(true); - EXPECT_EQ(MftH264DecodeEngine::kUninitialized, engine_->state()); -} - -TEST_F(MftH264DecodeEngineTest, DecoderInitMissingArgs) { - VideoCodecConfig config; - GetDecodeEngine(false); - engine_->Initialize(NULL, NULL, NULL, config); - EXPECT_EQ(MftH264DecodeEngine::kUninitialized, engine_->state()); -} - -TEST_F(MftH264DecodeEngineTest, DecoderInitNoDxva) { - TestInitAndUninit(false); -} - -TEST_F(MftH264DecodeEngineTest, DecoderInitDxva) { - TestInitAndUninit(true); -} - -TEST_F(MftH264DecodeEngineTest, DecoderUninit) { - TestInitAndUninit(false); - EXPECT_EQ(1, handler_->uninit_count_); - EXPECT_EQ(MftH264DecodeEngine::kUninitialized, engine_->state()); -} - -TEST_F(MftH264DecodeEngineTest, UninitBeforeInit) { - GetDecodeEngine(false); - engine_->Uninitialize(); - EXPECT_EQ(0, handler_->uninit_count_); -} - -TEST_F(MftH264DecodeEngineTest, InitWithNegativeDimensions) { - ExpectDefaultDimensionsOnInput(-123, -456); -} - -TEST_F(MftH264DecodeEngineTest, InitWithTooHighDimensions) { - ExpectDefaultDimensionsOnInput(kDecoderMaxWidth + 1, kDecoderMaxHeight + 1); -} - -TEST_F(MftH264DecodeEngineTest, DrainOnEmptyBuffer) { - GetDecodeEngine(false); - InitDecodeEngine(); - - // 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. - scoped_refptr<Buffer> buffer(new DataBuffer(0)); - engine_->ConsumeVideoSample(buffer); - EXPECT_EQ(MftH264DecodeEngine::kStopped, engine_->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()); - engine_->Uninitialize(); -} - -TEST_F(MftH264DecodeEngineTest, NoOutputOnGarbageInput) { - // 100 samples of garbage. - const int kNumFrames = 100; - scoped_refptr<FakeMftReader> reader(new FakeMftReader(kNumFrames)); - ASSERT_TRUE(reader.get()); - - GetDecodeEngine(false); - InitDecodeEngine(); - DecodeAll(reader); - - // 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()); - - // One extra count because of the end of stream NULL sample. - EXPECT_EQ(kNumFrames, handler_->empty_buffer_callback_count_ - 1); - engine_->Uninitialize(); -} - -TEST_F(MftH264DecodeEngineTest, FlushAtStart) { - GetDecodeEngine(false); - InitDecodeEngine(); - engine_->Flush(); - - // Flush should succeed even if input/output are empty. - EXPECT_EQ(1, handler_->flush_count_); - engine_->Uninitialize(); -} - -TEST_F(MftH264DecodeEngineTest, NoFlushAtStopped) { - scoped_refptr<BaseMftReader> reader(new FakeMftReader()); - ASSERT_TRUE(reader.get()); - - GetDecodeEngine(false); - InitDecodeEngine(); - DecodeAll(reader); - - EXPECT_EQ(0, handler_->flush_count_); - int old_flush_count = handler_->flush_count_; - engine_->Flush(); - EXPECT_EQ(old_flush_count, handler_->flush_count_); - engine_->Uninitialize(); -} - -TEST_F(MftH264DecodeEngineTest, DecodeValidVideoDxva) { - DecodeValidVideo("bear.1280x720.mp4", 82, true); -} - -TEST_F(MftH264DecodeEngineTest, DecodeValidVideoNoDxva) { - DecodeValidVideo("bear.1280x720.mp4", 82, false); -} - -} // namespace media |