diff options
Diffstat (limited to 'media/mf')
-rw-r--r-- | media/mf/README.chromium | 23 | ||||
-rw-r--r-- | media/mf/file_reader_util.cc | 211 | ||||
-rw-r--r-- | media/mf/file_reader_util.h | 70 | ||||
-rw-r--r-- | media/mf/mft_h264_decoder.cc | 706 | ||||
-rw-r--r-- | media/mf/mft_h264_decoder.h | 97 | ||||
-rw-r--r-- | media/mf/mft_h264_decoder_example.cc | 421 | ||||
-rw-r--r-- | media/mf/test/mft_h264_decoder_unittest.cc | 460 | ||||
-rw-r--r-- | media/mf/test/run_all_unittests.cc | 26 |
8 files changed, 0 insertions, 2014 deletions
diff --git a/media/mf/README.chromium b/media/mf/README.chromium deleted file mode 100644 index 73f1deb..0000000 --- a/media/mf/README.chromium +++ /dev/null @@ -1,23 +0,0 @@ -This tool demonstrates the use of the Media Foundation H.264 decoder as a -standalone Media Foundation Transform (MFT). The H.264 decoder takes sample -objects (IMFSample) containing Annex B streams as input, and outputs decoded -YV12/NV12 video frames as output, contained in a buffer object (if DXVA is not -enabled) or a Direct3D surface (if DXVA is enabled.) - -This tool uses ffmpeg's parser and bitstream converter to read a file -containing H.264 video and outputs packets containing Annex B streams which are -then fed into the H.264 decoder. This tool also demonstrates the use of the -H.264 decoder using callbacks. - -Requirements: Windows 7 - -Note1: On some video files, there is a mysterious 1-off decoded frame count -when DXVA is enabled. - -Note2: This tool requires the ffmpeg library to have the H.264 codec and Annex -B bitstream filter. You might need build your own, or grab one from -http://ffmpeg.arrozcru.org/autobuilds/ - -Note3: A single H264Mft instance is only for 1 H.264 video stream only. -Inputting streams consisting of more than 1 video to a single instance -may result in undefined behavior. diff --git a/media/mf/file_reader_util.cc b/media/mf/file_reader_util.cc deleted file mode 100644 index d18afe6..0000000 --- a/media/mf/file_reader_util.cc +++ /dev/null @@ -1,211 +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. -// -// Borrowed from media/tools/omx_test/file_reader_util.cc. -// Added some functionalities related to timestamps on packets. - -#include "media/mf/file_reader_util.h" - -#include <cstring> - -#include <algorithm> - -#include "base/logging.h" -#include "media/base/data_buffer.h" -#include "media/ffmpeg/ffmpeg_common.h" -#include "media/filters/bitstream_converter.h" - -namespace media { - -////////////////////////////////////////////////////////////////////////////// -// FFmpegFileReader -FFmpegFileReader::FFmpegFileReader(const std::string& filename) - : filename_(filename), - format_context_(NULL), - codec_context_(NULL), - target_stream_(-1), - converter_(NULL), - last_timestamp_(0) { -} - -FFmpegFileReader::~FFmpegFileReader() { - if (format_context_) - av_close_input_file(format_context_); -} - -bool FFmpegFileReader::Initialize() { - int result = av_open_input_file(&format_context_, filename_.c_str(), - NULL, 0, NULL); - if (result < 0) { - switch (result) { - case AVERROR_NOFMT: - LOG(ERROR) << "Error: File format not supported " - << filename_; - break; - default: - LOG(ERROR) << "Error: Could not open input for " - << filename_ << ": " << result; - break; - } - return false; - } - if (av_find_stream_info(format_context_) < 0) { - LOG(ERROR) << "can't use FFmpeg to parse stream info"; - return false; - } - - for (size_t i = 0; i < format_context_->nb_streams; ++i) { - codec_context_ = format_context_->streams[i]->codec; - - // Find the video stream. - if (codec_context_->codec_type == CODEC_TYPE_VIDEO) { - target_stream_ = i; - break; - } - } - if (target_stream_ == -1) { - LOG(ERROR) << "no video in the stream"; - return false; - } - - // Initialize the bitstream filter if needed. - // TODO(hclam): find a better way to identify mp4 container. - if (codec_context_->codec_id == CODEC_ID_H264) { - converter_.reset(new media::FFmpegBitstreamConverter( - "h264_mp4toannexb", codec_context_)); - } else if (codec_context_->codec_id == CODEC_ID_MPEG4) { - converter_.reset(new media::FFmpegBitstreamConverter( - "mpeg4video_es", codec_context_)); - } else if (codec_context_->codec_id == CODEC_ID_WMV3) { - converter_.reset(new media::FFmpegBitstreamConverter( - "vc1_asftorcv", codec_context_)); - } else if (codec_context_->codec_id == CODEC_ID_VC1) { - converter_.reset(new media::FFmpegBitstreamConverter( - "vc1_asftoannexg", codec_context_)); - } - if (converter_.get() && !converter_->Initialize()) { - converter_.reset(); - LOG(ERROR) << "failed to initialize h264_mp4toannexb filter"; - return false; - } - return true; -} - -void FFmpegFileReader::Read(scoped_refptr<DataBuffer>* output) { - if (!format_context_ || !codec_context_ || target_stream_ == -1) { - *output = new DataBuffer(0); - return; - } - AVPacket packet; - bool found = false; - while (!found) { - int result = av_read_frame(format_context_, &packet); - if (result < 0) { - *output = new DataBuffer(0); - return; - } - if (packet.stream_index == target_stream_) { - if (converter_.get() && !converter_->ConvertPacket(&packet)) { - LOG(ERROR) << "failed to convert AVPacket"; - } - last_timestamp_ = std::max(last_timestamp_, packet.pts); - CopyPacketToBuffer(&packet, output); - found = true; - } - av_free_packet(&packet); - } -} - -bool FFmpegFileReader::SeekForward(int64 seek_amount_us) { - if (!format_context_ || !codec_context_ || target_stream_ == -1) { - return false; - } - int64 new_us = TimeBaseToMicroseconds(last_timestamp_) + seek_amount_us; - int64 new_timestamp = MicrosecondsToTimeBase(new_us); - last_timestamp_ = new_timestamp; - return av_seek_frame(format_context_, target_stream_, new_timestamp, 0) >= 0; -} - -bool FFmpegFileReader::GetFrameRate(int* num, int* denom) const { - if (!codec_context_) - return false; - *denom = codec_context_->time_base.num; - *num = codec_context_->time_base.den; - if (*denom == 0) { - *num = 0; - return false; - } - return true; -} - -bool FFmpegFileReader::GetWidth(int* width) const { - if (!codec_context_) - return false; - *width = codec_context_->width; - return true; -} - -bool FFmpegFileReader::GetHeight(int* height) const { - if (!codec_context_) - return false; - *height = codec_context_->height; - return true; -} - -bool FFmpegFileReader::GetAspectRatio(int* num, int* denom) const { - if (!codec_context_) - return false; - AVRational aspect_ratio = codec_context_->sample_aspect_ratio; - if (aspect_ratio.num == 0 || aspect_ratio.den == 0) - return false; - *num = aspect_ratio.num; - *denom = aspect_ratio.den; - return true; -} - -int64 FFmpegFileReader::TimeBaseToMicroseconds( - int64 time_base_unit) const { - // FFmpeg units after time base conversion seems to be actually given in - // milliseconds (instead of seconds...) so we need to multiply it by a factor - // of 1,000. - // Note we need to double this because the frame rate is doubled in - // ffmpeg. - CHECK(codec_context_) << "Codec context needs to be initialized"; - return time_base_unit * 2000 * codec_context_->time_base.num / - codec_context_->time_base.den; -} - -int64 FFmpegFileReader::MicrosecondsToTimeBase( - int64 time_base_unit) const { - CHECK(codec_context_) << "Codec context needs to be initialized"; - return time_base_unit * codec_context_->time_base.den / 2000 / - codec_context_->time_base.num; -} - -void FFmpegFileReader::CopyPacketToBuffer(AVPacket* packet, - scoped_refptr<DataBuffer>* output) { - uint8* buffer = new uint8[packet->size]; - if (buffer == NULL) { - LOG(ERROR) << "Failed to allocate buffer for annex b stream"; - *output = NULL; - return; - } - memcpy(buffer, packet->data, packet->size); - *output = new DataBuffer(buffer, packet->size); - if (packet->pts != AV_NOPTS_VALUE) { - (*output)->SetTimestamp( - base::TimeDelta::FromMicroseconds( - TimeBaseToMicroseconds(packet->pts))); - } else { - (*output)->SetTimestamp(StreamSample::kInvalidTimestamp); - } - if (packet->duration == 0) { - LOG(WARNING) << "Packet duration not known"; - } - (*output)->SetDuration( - base::TimeDelta::FromMicroseconds( - TimeBaseToMicroseconds(packet->duration))); -} - -} // namespace media diff --git a/media/mf/file_reader_util.h b/media/mf/file_reader_util.h deleted file mode 100644 index a5fa9ec..0000000 --- a/media/mf/file_reader_util.h +++ /dev/null @@ -1,70 +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. -// -// Borrowed from media/tools/omx_test/file_reader_util.h. -// Added some functionalities related to timestamps on packets and Media -// Foundation. - -#ifndef MEDIA_MF_FILE_READER_UTIL_H_ -#define MEDIA_MF_FILE_READER_UTIL_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" - -struct AVCodecContext; -struct AVFormatContext; -struct AVPacket; - -namespace media { - -class BitstreamConverter; -class DataBuffer; - -// A class to help reading and parsing input file for use in omx_test. -class FileReader { - public: - virtual ~FileReader() {} - - // Initialize FileReader object, returns true if successful. - virtual bool Initialize() = 0; - - // Read the file into |output|, and output the number of bytes read to - // |size|. - virtual void Read(scoped_refptr<DataBuffer>* output) = 0; -}; - -class FFmpegFileReader : public FileReader { - public: - explicit FFmpegFileReader(const std::string& filename); - virtual ~FFmpegFileReader(); - virtual bool Initialize(); - virtual void Read(scoped_refptr<DataBuffer>* output); - virtual bool SeekForward(int64 seek_amount_us); - - bool GetFrameRate(int* num, int* denom) const; - bool GetWidth(int* width) const; - bool GetHeight(int* height) const; - bool GetAspectRatio(int* num, int* denom) const; - int64 TimeBaseToMicroseconds(int64 time_base_unit) const; - int64 MicrosecondsToTimeBase(int64 time_base_unit) const; - - private: - void CopyPacketToBuffer(AVPacket* packet, scoped_refptr<DataBuffer>* output); - - std::string filename_; - AVFormatContext* format_context_; - AVCodecContext* codec_context_; - int target_stream_; - scoped_ptr<BitstreamConverter> converter_; - int64 last_timestamp_; - - DISALLOW_COPY_AND_ASSIGN(FFmpegFileReader); -}; - -} // namespace media - -#endif // MEDIA_MF_FILE_READER_UTIL_H_ diff --git a/media/mf/mft_h264_decoder.cc b/media/mf/mft_h264_decoder.cc deleted file mode 100644 index 2c1970d..0000000 --- a/media/mf/mft_h264_decoder.cc +++ /dev/null @@ -1,706 +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/mf/mft_h264_decoder.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" - -#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 - -MftH264Decoder::MftH264Decoder(bool use_dxva, HWND draw_window) - : use_dxva_(use_dxva), - d3d9_(NULL), - device_(NULL), - device_manager_(NULL), - draw_window_(draw_window), - decoder_(NULL), - input_stream_info_(), - output_stream_info_(), - state_(kUninitialized), - event_handler_(NULL) { - memset(&config_, 0, sizeof(config_)); - memset(&info_, 0, sizeof(info_)); -} - -MftH264Decoder::~MftH264Decoder() { -} - -void MftH264Decoder::Initialize( - MessageLoop* message_loop, - VideoDecodeEngine::EventHandler* event_handler, - VideoDecodeContext* context, - const VideoCodecConfig& config) { - LOG(INFO) << "MftH264Decoder::Initialize"; - if (state_ != kUninitialized) { - LOG(ERROR) << "Initialize: invalid state"; - return; - } - if (!message_loop || !event_handler) { - LOG(ERROR) << "MftH264Decoder::Initialize: parameters cannot be NULL"; - return; - } - - config_ = config; - event_handler_ = event_handler; - - info_.provides_buffers = true; - - // TODO(jiesun): Actually it is more likely an NV12 D3DSuface9. - // Until we had hardware composition working. - if (use_dxva_) { - info_.stream_info.surface_format = VideoFrame::NV12; - 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; - event_handler_->OnInitializeComplete(info_); - } else { - LOG(ERROR) << "MftH264Decoder::Initialize failed"; - } -} - -void MftH264Decoder::Uninitialize() { - LOG(INFO) << "MftH264Decoder::Uninitialize"; - if (state_ == kUninitialized) { - LOG(ERROR) << "Uninitialize: invalid state"; - return; - } - - // TODO(imcheng): - // Cannot shutdown COM libraries here because the COM objects still needs - // to be Release()'ed. We can explicitly release them here, or move the - // uninitialize to GpuVideoService... - decoder_.Release(); - device_manager_.Release(); - device_.Release(); - d3d9_.Release(); - ShutdownComLibraries(); - state_ = kUninitialized; - event_handler_->OnUninitializeComplete(); -} - -void MftH264Decoder::Flush() { - LOG(INFO) << "MftH264Decoder::Flush"; - if (state_ != kNormal) { - LOG(ERROR) << "Flush: invalid state"; - return; - } - state_ = kFlushing; - if (!SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH)) { - LOG(WARNING) << "MftH264Decoder::Flush failed to send message"; - } - state_ = kNormal; - event_handler_->OnFlushComplete(); -} - -void MftH264Decoder::Seek() { - if (state_ != kNormal) { - LOG(ERROR) << "Seek: invalid state"; - return; - } - LOG(INFO) << "MftH264Decoder::Seek"; - // Seek not implemented. - event_handler_->OnSeekComplete(); -} - -void MftH264Decoder::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { - LOG(INFO) << "MftH264Decoder::ConsumeVideoSample"; - 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(decoder_->ProcessInput(0, sample.get(), 0))) { - event_handler_->OnError(); - } - } - } else { - if (state_ != MftH264Decoder::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_ = MftH264Decoder::kEosDrain; - } - } - } - DoDecode(); -} - -void MftH264Decoder::ProduceVideoFrame(scoped_refptr<VideoFrame> frame) { - LOG(INFO) << "MftH264Decoder::ProduceVideoFrame"; - if (state_ == kUninitialized) { - LOG(ERROR) << "ProduceVideoFrame: invalid state"; - return; - } - scoped_refptr<Buffer> buffer; - event_handler_->ProduceVideoSample(buffer); -} - -// private methods - -// static -bool MftH264Decoder::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 MftH264Decoder::ShutdownComLibraries() { - HRESULT hr; - hr = MFShutdown(); - if (FAILED(hr)) { - LOG(WARNING) << "Warning: MF failed to shutdown"; - } - CoUninitialize(); -} - -bool MftH264Decoder::CreateD3DDevManager() { - CHECK(draw_window_); - d3d9_.Attach(Direct3DCreate9(D3D_SDK_VERSION)); - if (d3d9_.get() == NULL) { - LOG(ERROR) << "Failed to create D3D9"; - return false; - } - - 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 = draw_window_; - present_params.Windowed = TRUE; - present_params.Flags = D3DPRESENTFLAG_VIDEO; - present_params.FullScreen_RefreshRateInHz = 0; - present_params.PresentationInterval = 0; - - // D3DCREATE_HARDWARE_VERTEXPROCESSING specifies hardware vertex processing. - // (Is it even needed for just video decoding?) - HRESULT hr = d3d9_->CreateDevice(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - draw_window_, - (D3DCREATE_HARDWARE_VERTEXPROCESSING | - D3DCREATE_MULTITHREADED), - &present_params, - device_.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to create D3D Device"; - return false; - } - - UINT dev_manager_reset_token = 0; - 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_.get(), - dev_manager_reset_token); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set device to device manager"; - return false; - } - return true; -} - -bool MftH264Decoder::InitInternal() { - if (!StartupComLibraries()) - return false; - if (use_dxva_ && !CreateD3DDevManager()) - return false; - if (!InitDecoder()) - return false; - if (!GetStreamsInfoAndBufferReqs()) - return false; - return SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING); -} - -bool MftH264Decoder::InitDecoder() { - // TODO(jiesun): use MFEnum to get decoder CLSID. - HRESULT hr = CoCreateInstance(__uuidof(CMSH264DecoderMFT), - NULL, - CLSCTX_INPROC_SERVER, - __uuidof(IMFTransform), - reinterpret_cast<void**>(decoder_.Receive())); - if (FAILED(hr) || !decoder_.get()) { - LOG(ERROR) << "CoCreateInstance failed " << std::hex << std::showbase << hr; - return false; - } - - if (!CheckDecoderDxvaSupport()) - return false; - - if (use_dxva_) { - hr = decoder_->ProcessMessage( - MFT_MESSAGE_SET_D3D_MANAGER, - reinterpret_cast<ULONG_PTR>(device_manager_.get())); - if (FAILED(hr)) { - LOG(ERROR) << "Failed to set D3D9 device to decoder " << std::hex << hr; - return false; - } - } - - return SetDecoderMediaTypes(); -} - -bool MftH264Decoder::CheckDecoderDxvaSupport() { - ScopedComPtr<IMFAttributes> attributes; - HRESULT hr = decoder_->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 MftH264Decoder::SetDecoderMediaTypes() { - if (!SetDecoderInputMediaType()) - return false; - return SetDecoderOutputMediaType(ConvertVideoFrameFormatToGuid( - info_.stream_info.surface_format)); -} - -bool MftH264Decoder::SetDecoderInputMediaType() { - 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 = decoder_->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 MftH264Decoder::SetDecoderOutputMediaType(const GUID subtype) { - DWORD i = 0; - IMFMediaType* out_media_type; - bool found = false; - while (SUCCEEDED(decoder_->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 = decoder_->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 MftH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE msg) { - HRESULT hr = decoder_->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 MftH264Decoder::GetStreamsInfoAndBufferReqs() { - HRESULT hr = decoder_->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 = decoder_->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 MftH264Decoder::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 = decoder_->ProcessOutput(0, // No flags - 1, // # of out streams to pull from - &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 = SetDecoderOutputMediaType(ConvertVideoFrameFormatToGuid( - info_.stream_info.surface_format)); - if (SUCCEEDED(hr)) { - event_handler_->OnFormatChange(info_.stream_info); - 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_ = MftH264Decoder::kStopped; - return false; - } - return true; - } else { - LOG(ERROR) << "Unhandled error in DoDecode()"; - state_ = MftH264Decoder::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> 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; - } - - if (!frame.get()) { - LOG(ERROR) << "Failed to allocate video frame for d3d texture"; - event_handler_->OnError(); - return true; - } - - // The reference is now in the VideoFrame. - surface.Detach(); - } else { - // 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())); - } - // TODO(jiesun): non-System memory case - event_handler_->ConsumeVideoFrame(frame); - return true; -} - -} // namespace media diff --git a/media/mf/mft_h264_decoder.h b/media/mf/mft_h264_decoder.h deleted file mode 100644 index 57c9e9f..0000000 --- a/media/mf/mft_h264_decoder.h +++ /dev/null @@ -1,97 +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 decoder. - -#ifndef MEDIA_MF_MFT_H264_DECODER_H_ -#define MEDIA_MF_MFT_H264_DECODER_H_ - -#include "build/build_config.h" // For OS_WIN. - -#if defined(OS_WIN) - -#include <d3d9.h> -#include <dxva2api.h> -#include <mfidl.h> - -#include "base/gtest_prod_util.h" -#include "base/scoped_comptr_win.h" -#include "media/video/video_decode_engine.h" - -class MessageLoop; - -namespace media { - -class MftH264Decoder : 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 MftH264Decoder(bool use_dxva, HWND draw_window); - ~MftH264Decoder(); - virtual void Initialize(MessageLoop* message_loop, - 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_; } - IDirect3DDevice9* device() const { return device_.get(); } - State state() const { return state_; } - - private: - friend class MftH264DecoderTest; - FRIEND_TEST_ALL_PREFIXES(MftH264DecoderTest, LibraryInit); - - // TODO(jiesun): Find a way to move all these to GpuVideoService.. - static bool StartupComLibraries(); - static void ShutdownComLibraries(); - bool CreateD3DDevManager(); - - bool InitInternal(); - bool InitDecoder(); - bool CheckDecoderDxvaSupport(); - bool SetDecoderMediaTypes(); - bool SetDecoderInputMediaType(); - bool SetDecoderOutputMediaType(const GUID subtype); - bool SendMFTMessage(MFT_MESSAGE_TYPE msg); - bool GetStreamsInfoAndBufferReqs(); - - bool DoDecode(); - - - bool use_dxva_; - - ScopedComPtr<IDirect3D9> d3d9_; - ScopedComPtr<IDirect3DDevice9> device_; - ScopedComPtr<IDirect3DDeviceManager9> device_manager_; - HWND draw_window_; - ScopedComPtr<IMFTransform> decoder_; - - MFT_INPUT_STREAM_INFO input_stream_info_; - MFT_OUTPUT_STREAM_INFO output_stream_info_; - - State state_; - - VideoDecodeEngine::EventHandler* event_handler_; - VideoCodecConfig config_; - VideoCodecInfo info_; - - DISALLOW_COPY_AND_ASSIGN(MftH264Decoder); -}; - -} // namespace media - -#endif // defined(OS_WIN) - -#endif // MEDIA_MF_MFT_H264_DECODER_H_ diff --git a/media/mf/mft_h264_decoder_example.cc b/media/mf/mft_h264_decoder_example.cc deleted file mode 100644 index b5b6b10..0000000 --- a/media/mf/mft_h264_decoder_example.cc +++ /dev/null @@ -1,421 +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. -// -// Demonstrates the use of MftH264Decoder. - -#include <cstdio> - -#include <string> - -#include <d3d9.h> -#include <dxva2api.h> - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/file_path.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/scoped_comptr_win.h" -#include "base/scoped_ptr.h" -#include "base/time.h" -#include "media/base/data_buffer.h" -#include "media/base/media.h" -#include "media/base/video_frame.h" -#include "media/base/yuv_convert.h" -#include "media/ffmpeg/ffmpeg_common.h" -#include "media/ffmpeg/file_protocol.h" -#include "media/mf/file_reader_util.h" -#include "media/mf/mft_h264_decoder.h" - -using base::AtExitManager; -using base::Time; -using base::TimeDelta; -using media::Buffer; -using media::DataBuffer; -using media::FFmpegFileReader; -using media::MftH264Decoder; -using media::VideoCodecConfig; -using media::VideoCodecInfo; -using media::VideoDecodeEngine; -using media::VideoFrame; -using media::VideoStreamInfo; - -namespace { - -const wchar_t* const kWindowClass = L"Chrome_H264_MFT"; -const wchar_t* const kWindowTitle = L"H264_MFT"; -const int kWindowStyleFlags = (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & - ~(WS_MAXIMIZEBOX | WS_THICKFRAME); - -void usage() { - static char* usage_msg = - "Usage: mft_h264_decoder [--enable-dxva] [--render] --input-file=FILE\n" - "enable-dxva: Enables hardware accelerated decoding\n" - "render: Render to window\n" - "During rendering, press spacebar to skip forward at least 5 seconds.\n" - "To display this message: mft_h264_decoder --help"; - fprintf(stderr, "%s\n", usage_msg); -} - -static bool InitFFmpeg() { - if (!media::InitializeMediaLibrary(FilePath())) - return false; - avcodec_init(); - av_register_all(); - av_register_protocol2(&kFFmpegFileProtocol, sizeof(kFFmpegFileProtocol)); - return true; -} - -// Creates a window with the given width and height. -// Returns: A handle to the window on success, NULL otherwise. -static HWND CreateDrawWindow(int width, int height) { - WNDCLASS window_class = {0}; - window_class.lpszClassName = kWindowClass; - window_class.hInstance = NULL; - window_class.hbrBackground = 0; - window_class.lpfnWndProc = DefWindowProc; - window_class.hCursor = 0; - - RegisterClass(&window_class); - - HWND window = CreateWindow(kWindowClass, - kWindowTitle, - kWindowStyleFlags, - 100, - 100, - width, - height, - NULL, - NULL, - NULL, - NULL); - if (window == NULL) { - LOG(ERROR) << "Failed to create window"; - return NULL; - } - RECT rect; - rect.left = 0; - rect.right = width; - rect.top = 0; - rect.bottom = height; - AdjustWindowRect(&rect, kWindowStyleFlags, FALSE); - MoveWindow(window, 0, 0, rect.right - rect.left, rect.bottom - rect.top, - TRUE); - return window; -} - -class WindowObserver : public base::MessagePumpWin::Observer { - public: - WindowObserver(FFmpegFileReader* reader, MftH264Decoder* decoder) - : reader_(reader), - decoder_(decoder) { - } - - virtual void WillProcessMessage(const MSG& msg) { - if (msg.message == WM_CHAR && msg.wParam == ' ') { - // Seek forward 5 seconds. - decoder_->Flush(); - reader_->SeekForward(5000000); - } - } - - virtual void DidProcessMessage(const MSG& msg) { - } - - private: - FFmpegFileReader* reader_; - MftH264Decoder* decoder_; -}; - -class MftH264DecoderHandler - : public VideoDecodeEngine::EventHandler, - public base::RefCountedThreadSafe<MftH264DecoderHandler> { - public: - MftH264DecoderHandler() : frames_read_(0), frames_decoded_(0) { - memset(&info_, 0, sizeof(info_)); - } - virtual ~MftH264DecoderHandler() {} - virtual void OnInitializeComplete(const VideoCodecInfo& info) { - info_ = info; - } - virtual void OnUninitializeComplete() { - } - virtual void OnFlushComplete() { - } - virtual void OnSeekComplete() {} - virtual void OnError() {} - virtual void OnFormatChange(VideoStreamInfo stream_info) { - info_.stream_info = stream_info; - } - virtual void ProduceVideoSample(scoped_refptr<Buffer> buffer) { - if (reader_ && decoder_) { - scoped_refptr<DataBuffer> input; - reader_->Read(&input); - if (!input->IsEndOfStream()) - frames_read_++; - decoder_->ConsumeVideoSample(input); - } - } - virtual void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { - if (frame.get()) { - if (frame->format() != VideoFrame::EMPTY) { - frames_decoded_++; - } - } - } - virtual void SetReader(FFmpegFileReader* reader) { - reader_ = reader; - } - virtual void SetDecoder(MftH264Decoder* decoder) { - decoder_= decoder; - } - virtual void DecodeSingleFrame() { - scoped_refptr<VideoFrame> frame; - decoder_->ProduceVideoFrame(frame); - } - virtual void Start() { - while (decoder_->state() != MftH264Decoder::kStopped) - DecodeSingleFrame(); - } - - VideoCodecInfo info_; - int frames_read_; - int frames_decoded_; - FFmpegFileReader* reader_; - MftH264Decoder* decoder_; -}; - -class RenderToWindowHandler : public MftH264DecoderHandler { - public: - RenderToWindowHandler(HWND window, MessageLoop* loop) - : MftH264DecoderHandler(), - window_(window), - loop_(loop), - has_output_(false) { - } - virtual ~RenderToWindowHandler() {} - bool RenderSoftwareFrame(scoped_refptr<VideoFrame> frame) { - int width = frame->width(); - int height = frame->height(); - - // Assume height does not change. - static uint8* rgb_frame = new uint8[height * frame->stride(0) * 4]; - uint8* frame_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); - uint8* frame_u = static_cast<uint8*>(frame->data(VideoFrame::kUPlane)); - uint8* frame_v = static_cast<uint8*>(frame->data(VideoFrame::kVPlane)); - media::ConvertYUVToRGB32(frame_y, frame_v, frame_u, rgb_frame, - width, height, - frame->stride(0), frame->stride(1), - 4 * frame->stride(0), media::YV12); - PAINTSTRUCT ps; - InvalidateRect(window_, NULL, TRUE); - HDC hdc = BeginPaint(window_, &ps); - BITMAPINFOHEADER hdr; - hdr.biSize = sizeof(BITMAPINFOHEADER); - hdr.biWidth = width; - hdr.biHeight = -height; // minus means top-down bitmap - hdr.biPlanes = 1; - hdr.biBitCount = 32; - hdr.biCompression = BI_RGB; // no compression - hdr.biSizeImage = 0; - hdr.biXPelsPerMeter = 1; - hdr.biYPelsPerMeter = 1; - hdr.biClrUsed = 0; - hdr.biClrImportant = 0; - int rv = StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, - rgb_frame, reinterpret_cast<BITMAPINFO*>(&hdr), - DIB_RGB_COLORS, SRCCOPY); - EndPaint(window_, &ps); - return rv != 0; - } - bool RenderD3dSurface(scoped_refptr<VideoFrame> frame) { - ScopedComPtr<IDirect3DSurface9> surface; - IDirect3DDevice9* device = decoder_->device(); - // TODO(hclam): Comment this since this file will be removed later. - // surface.Attach(static_cast<IDirect3DSurface9*>(frame->d3d_texture(0))); - HRESULT hr; - hr = device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), - 1.0f, 0); - if (FAILED(hr)) { - LOG(ERROR) << "Device->Clear() failed"; - return false; - } - ScopedComPtr<IDirect3DSurface9> backbuffer; - hr = device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, - backbuffer.Receive()); - if (FAILED(hr)) { - LOG(ERROR) << "Device->GetBackBuffer() failed"; - return false; - } - hr = device->StretchRect(surface.get(), NULL, backbuffer.get(), NULL, - D3DTEXF_NONE); - if (FAILED(hr)) { - LOG(ERROR) << "Device->StretchRect() failed"; - return false; - } - hr = device->Present(NULL, NULL, NULL, NULL); - if (FAILED(hr)) { - if (hr == E_FAIL) { - LOG(WARNING) << "Present() returned E_FAIL"; - } else { - static int frames_dropped = 0; - LOG(ERROR) << "Device->Present() failed " - << std::hex << std::showbase << hr; - if (++frames_dropped == 10) { - LOG(ERROR) << "Dropped too many frames, quitting"; - MessageLoopForUI::current()->QuitNow(); - return false; - } - } - } - return true; - } - virtual void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { - has_output_ = true; - if (frame.get()) { - if (frame->format() != VideoFrame::EMPTY) { - frames_decoded_++; - loop_->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &RenderToWindowHandler::DecodeSingleFrame), - frame->GetDuration().InMilliseconds()); - bool success; - if (decoder_->use_dxva()) { - success = RenderD3dSurface(frame); - } else { - success = RenderSoftwareFrame(frame); - } - if (!success) { - LOG(ERROR) << "Render failed"; - loop_->QuitNow(); - } - } else { // if frame is type EMPTY, there will be no more frames. - loop_->QuitNow(); - } - } - } - virtual void DecodeSingleFrame() { - if (decoder_->state() != MftH264Decoder::kStopped) { - while (decoder_->state() != MftH264Decoder::kStopped && !has_output_) { - scoped_refptr<VideoFrame> frame; - decoder_->ProduceVideoFrame(frame); - } - if (decoder_->state() == MftH264Decoder::kStopped) - loop_->QuitNow(); - has_output_ = false; - } else { - loop_->QuitNow(); - } - } - virtual void Start() { - loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, &RenderToWindowHandler::DecodeSingleFrame)); - loop_->Run(); - } - - private: - HWND window_; - MessageLoop* loop_; - bool has_output_; -}; - -static int Run(bool use_dxva, bool render, const std::string& input_file) { - scoped_ptr<FFmpegFileReader> reader(new FFmpegFileReader(input_file)); - if (reader.get() == NULL || !reader->Initialize()) { - LOG(ERROR) << "Failed to create/initialize reader"; - return -1; - } - int width = 0, height = 0; - if (!reader->GetWidth(&width) || !reader->GetHeight(&height)) { - LOG(WARNING) << "Failed to get width/height from reader"; - } - VideoCodecConfig config; - config.width = width; - config.height = height; - HWND window = NULL; - if (use_dxva || render) { - window = CreateDrawWindow(width, height); - if (!render) - ShowWindow(window, SW_HIDE); - if (window == NULL) { - LOG(ERROR) << "Failed to create window"; - return -1; - } - } - - scoped_ptr<MftH264Decoder> mft(new MftH264Decoder(use_dxva, window)); - if (!mft.get()) { - LOG(ERROR) << "Failed to create MFT"; - return -1; - } - - scoped_refptr<MftH264DecoderHandler> handler; - if (render) - handler = new RenderToWindowHandler(window, MessageLoop::current()); - else - handler = new MftH264DecoderHandler(); - handler->SetDecoder(mft.get()); - handler->SetReader(reader.get()); - if (!handler.get()) { - LOG(ERROR) << "Failed to create handler"; - return -1; - } - - mft->Initialize(MessageLoop::current(), handler.get(), NULL, config); - scoped_ptr<WindowObserver> observer; - if (render) { - observer.reset(new WindowObserver(reader.get(), mft.get())); - MessageLoopForUI::current()->AddObserver(observer.get()); - } - - Time decode_start(Time::Now()); - handler->Start(); - TimeDelta decode_time = Time::Now() - decode_start; - - printf("All done, frames read: %d, frames decoded: %d\n", - handler->frames_read_, handler->frames_decoded_); - printf("Took %lldms\n", decode_time.InMilliseconds()); - if (window) - DestroyWindow(window); - return 0; -} - -} // namespace - -int main(int argc, char** argv) { - AtExitManager at_exit; - MessageLoopForUI message_loop; - CommandLine::Init(argc, argv); - if (argc == 1) { - fprintf(stderr, "Not enough arguments\n"); - usage(); - return -1; - } - const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); - if (cmd_line.HasSwitch("help")) { - usage(); - return -1; - } - bool use_dxva = cmd_line.HasSwitch("enable-dxva"); - bool render = cmd_line.HasSwitch("render"); - std::string input_file = cmd_line.GetSwitchValueASCII("input-file"); - if (input_file.empty()) { - fprintf(stderr, "No input file provided\n"); - usage(); - return -1; - } - printf("enable-dxva: %d\n", use_dxva); - printf("render: %d\n", render); - printf("input-file: %s\n", input_file.c_str()); - - if (!InitFFmpeg()) { - LOG(ERROR) << "InitFFMpeg() failed"; - return -1; - } - int ret = Run(use_dxva, render, input_file); - - printf("Done\n"); - return ret; -} diff --git a/media/mf/test/mft_h264_decoder_unittest.cc b/media/mf/test/mft_h264_decoder_unittest.cc deleted file mode 100644 index 11959f7..0000000 --- a/media/mf/test/mft_h264_decoder_unittest.cc +++ /dev/null @@ -1,460 +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/mf/file_reader_util.h" -#include "media/mf/mft_h264_decoder.h" -#include "media/video/video_decode_engine.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::TimeDelta; - -namespace media { - -static const int kDecoderMaxWidth = 1920; -static const int kDecoderMaxHeight = 1088; - -static HWND 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); - - HWND window = CreateWindow(kClassName, - kWindowTitle, - (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & - ~(WS_MAXIMIZEBOX | WS_THICKFRAME), - 100, - 100, - width, - height, - NULL, - NULL, - NULL, - NULL); - if (window == NULL) { - LOG(ERROR) << "Failed to create window"; - return NULL; - } - return window; -} - -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 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_; -}; - -class MftH264DecoderTest : public testing::Test { - public: - MftH264DecoderTest() {} - virtual ~MftH264DecoderTest() {} - - protected: - virtual void SetUp() {} - 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 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 SetDecoder(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_; - MftH264Decoder* decoder_; - scoped_refptr<VideoFrame> current_frame_; -}; - -// A simple test case for init/deinit of MF/COM libraries. -TEST_F(MftH264DecoderTest, LibraryInit) { - EXPECT_TRUE(MftH264Decoder::StartupComLibraries()); - MftH264Decoder::ShutdownComLibraries(); -} - -TEST_F(MftH264DecoderTest, DecoderUninitializedAtFirst) { - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(true, NULL)); - ASSERT_TRUE(decoder.get()); - EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); -} - -TEST_F(MftH264DecoderTest, DecoderInitMissingArgs) { - VideoCodecConfig config; - config.width = 800; - config.height = 600; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(NULL, NULL, NULL, config); - EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); -} - -TEST_F(MftH264DecoderTest, DecoderInitNoDxva) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 800; - config.height = 600; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(1, handler.init_count_); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - decoder->Uninitialize(); -} - -TEST_F(MftH264DecoderTest, DecoderInitDxva) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 800; - config.height = 600; - HWND hwnd = CreateDrawWindow(config.width, config.height); - ASSERT_TRUE(hwnd); - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(true, hwnd)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(1, handler.init_count_); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - decoder->Uninitialize(); - DestroyWindow(hwnd); -} - -TEST_F(MftH264DecoderTest, DecoderUninit) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 800; - config.height = 600; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - decoder->Uninitialize(); - EXPECT_EQ(1, handler.uninit_count_); - EXPECT_EQ(MftH264Decoder::kUninitialized, decoder->state()); -} - -TEST_F(MftH264DecoderTest, UninitBeforeInit) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 800; - config.height = 600; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Uninitialize(); - EXPECT_EQ(0, handler.uninit_count_); -} - -TEST_F(MftH264DecoderTest, InitWithNegativeDimensions) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = -123; - config.height = -456; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, 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, InitWithTooHighDimensions) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = kDecoderMaxWidth + 1; - config.height = kDecoderMaxHeight + 1; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, 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, DrainOnEmptyBuffer) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 1024; - config.height = 768; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - scoped_refptr<Buffer> buffer(new DataBuffer(0)); - - // 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->ConsumeVideoSample(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(); -} - -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_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - handler.SetReader(reader); - handler.SetDecoder(decoder.get()); - while (MftH264Decoder::kStopped != decoder->state()) { - scoped_refptr<VideoFrame> frame; - decoder->ProduceVideoFrame(frame); - } - - // 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); - decoder->Uninitialize(); -} - -TEST_F(MftH264DecoderTest, FlushAtStart) { - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 1024; - config.height = 768; - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, 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_ptr<MftH264Decoder> decoder(new MftH264Decoder(false, NULL)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - handler.SetReader(reader); - handler.SetDecoder(decoder.get()); - while (MftH264Decoder::kStopped != decoder->state()) { - scoped_refptr<VideoFrame> frame; - decoder->ProduceVideoFrame(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); - path = path.AppendASCII("media") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII(file_name.c_str()); - return path; -} - -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)); - - MessageLoop loop; - SimpleMftH264DecoderHandler handler; - VideoCodecConfig config; - config.width = 1; - config.height = 1; - HWND hwnd = CreateDrawWindow(config.width, config.height); - ASSERT_TRUE(hwnd); - scoped_ptr<MftH264Decoder> decoder(new MftH264Decoder(dxva, hwnd)); - ASSERT_TRUE(decoder.get()); - decoder->Initialize(&loop, &handler, NULL, config); - EXPECT_EQ(MftH264Decoder::kNormal, decoder->state()); - handler.SetReader(reader); - handler.SetDecoder(decoder.get()); - while (MftH264Decoder::kStopped != decoder->state()) { - scoped_refptr<VideoFrame> frame; - decoder->ProduceVideoFrame(frame); - } - - // 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(); - DestroyWindow(hwnd); -} - -TEST_F(MftH264DecoderTest, DecodeValidVideoDxva) { - DecodeValidVideo("bear.1280x720.mp4", 82, true); -} - -TEST_F(MftH264DecoderTest, DecodeValidVideoNoDxva) { - DecodeValidVideo("bear.1280x720.mp4", 82, false); -} - -} // namespace media diff --git a/media/mf/test/run_all_unittests.cc b/media/mf/test/run_all_unittests.cc deleted file mode 100644 index 4126108..0000000 --- a/media/mf/test/run_all_unittests.cc +++ /dev/null @@ -1,26 +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/test/test_suite.h" -#include "media/base/media.h" -#include "media/ffmpeg/ffmpeg_common.h" -#include "media/ffmpeg/file_protocol.h" - -static bool InitFFmpeg() { - if (!media::InitializeMediaLibrary(FilePath())) - return false; - avcodec_init(); - av_register_all(); - av_register_protocol2(&kFFmpegFileProtocol, sizeof(kFFmpegFileProtocol)); - return true; -} - -int main(int argc, char** argv) { - if (!InitFFmpeg()) { - fprintf(stderr, "Failed to init ffmpeg\n"); - return -1; - } - return TestSuite(argc, argv).Run(); -} |