Index: media/mf/main.cc |
=================================================================== |
--- media/mf/main.cc (revision 0) |
+++ media/mf/main.cc (revision 0) |
@@ -0,0 +1,260 @@ |
+// 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 <d3d9.h> |
+#include <dxva2api.h> |
+#include <mfapi.h> |
+ |
+#include "base/command_line.h" |
+#include "base/file_path.h" |
+#include "base/logging.h" |
+#include "base/scoped_comptr_win.h" |
+#include "base/scoped_ptr.h" |
+#include "base/time.h" |
+#include "media/base/media.h" |
+#include "media/base/video_frame.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::Time; |
+using base::TimeDelta; |
+using media::FFmpegFileReader; |
+using media::MftH264Decoder; |
+using media::VideoFrame; |
+ |
+namespace { |
+ |
+void usage() { |
+ static char* usage_msg = |
+ "Usage: MftH264Decoder [--enable-dxva] --input-file=FILE\n" |
+ "enable-dxva: Enables hardware accelerated decoding\n" |
+ "To display this message: MftH264Decoder --help"; |
+ fprintf(stderr, "%s\n", usage_msg); |
+} |
+ |
+static bool InitFFmpeg() { |
+ if (!media::InitializeMediaLibrary(FilePath())) |
+ return false; |
+ avcodec_init(); |
+ av_register_all(); |
+ av_register_protocol(&kFFmpegFileProtocol); |
+ return true; |
+} |
+ |
+bool InitComLibraries() { |
+ 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; |
+} |
+ |
+void ShutdownComLibraries() { |
+ HRESULT hr; |
+ hr = MFShutdown(); |
+ if (FAILED(hr)) { |
+ LOG(WARNING) << "Warning: MF failed to shutdown"; |
+ } |
+ CoUninitialize(); |
+} |
+ |
+IDirect3DDeviceManager9* CreateD3DDevManager(HWND video_window, |
+ int width, |
+ int height, |
+ IDirect3D9** direct3d, |
+ IDirect3DDevice9** device) { |
+ CHECK(video_window != NULL); |
+ CHECK(direct3d != NULL); |
+ CHECK(device != NULL); |
+ |
+ ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
+ ScopedComPtr<IDirect3D9> d3d; |
+ d3d.Attach(Direct3DCreate9(D3D_SDK_VERSION)); |
+ if (d3d == NULL) { |
+ LOG(ERROR) << "Failed to create D3D9"; |
+ return NULL; |
+ } |
+ D3DPRESENT_PARAMETERS present_params = {0}; |
+ |
+ present_params.BackBufferWidth = width; |
+ present_params.BackBufferHeight = height; |
+ present_params.BackBufferFormat = D3DFMT_UNKNOWN; |
+ present_params.BackBufferCount = 1; |
+ present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; |
+ present_params.hDeviceWindow = video_window; |
+ present_params.Windowed = TRUE; |
+ present_params.Flags = D3DPRESENTFLAG_VIDEO; |
+ present_params.FullScreen_RefreshRateInHz = 0; |
+ present_params.PresentationInterval = 0; |
+ |
+ ScopedComPtr<IDirect3DDevice9> temp_device; |
+ |
+ // D3DCREATE_HARDWARE_VERTEXPROCESSING specifies hardware vertex processing. |
+ // (Is it even needed for just video decoding?) |
+ HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, |
+ D3DDEVTYPE_HAL, |
+ video_window, |
+ D3DCREATE_HARDWARE_VERTEXPROCESSING, |
+ &present_params, |
+ temp_device.Receive()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to create D3D Device"; |
+ return NULL; |
+ } |
+ UINT dev_manager_reset_token = 0; |
+ hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, |
+ dev_manager.Receive()); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Couldn't create D3D Device manager"; |
+ return NULL; |
+ } |
+ hr = dev_manager->ResetDevice(temp_device.get(), dev_manager_reset_token); |
+ if (FAILED(hr)) { |
+ LOG(ERROR) << "Failed to set device to device manager"; |
+ return NULL; |
+ } |
+ *direct3d = d3d.Detach(); |
+ *device = temp_device.Detach(); |
+ return dev_manager.Detach(); |
+} |
+ |
+static void ReleaseOutputBuffer(VideoFrame* frame) { |
+ if (frame == NULL) |
+ return; |
+ if (frame->type() == VideoFrame::TYPE_MFBUFFER || |
+ frame->type() == VideoFrame::TYPE_DIRECT3DSURFACE) { |
+ static_cast<IMFMediaBuffer*>(frame->private_buffer())->Release(); |
+ } else { |
+ return; |
+ } |
+} |
+ |
+class FakeRenderer { |
+ public: |
+ FakeRenderer() {} |
+ ~FakeRenderer() {} |
+ void ProcessFrame(scoped_refptr<VideoFrame> frame) { |
+ ReleaseOutputBuffer(frame); |
+ } |
+}; |
+ |
+int Run(bool use_dxva, const std::string& input_file) { |
+ scoped_ptr<FFmpegFileReader> reader(new FFmpegFileReader(input_file)); |
+ if (reader.get() == NULL) { |
+ LOG(ERROR) << "Failed to create reader"; |
+ return -1; |
+ } |
+ if (!reader->Initialize()) { |
+ LOG(ERROR) << "Failed to initialize reader"; |
+ return -1; |
+ } |
+ |
+ // TODO(imcheng): The frame rate obtained from ffmpeg seems to be double |
+ // of what is expected. Why? |
+ int frame_rate_num = 0, frame_rate_denom = 0; |
+ if (!reader->GetFrameRate(&frame_rate_num, &frame_rate_denom)) { |
+ LOG(WARNING) << "Failed to get frame rate from reader"; |
+ } |
+ int width = 0, height = 0; |
+ if (!reader->GetWidth(&width) || !reader->GetHeight(&height)) { |
+ LOG(WARNING) << "Failed to get width/height from reader"; |
+ } |
+ int aspect_ratio_num = 0, aspect_ratio_denom = 0; |
+ if (!reader->GetAspectRatio(&aspect_ratio_num, &aspect_ratio_denom)) { |
+ LOG(WARNING) << "Failed to get aspect ratio from reader"; |
+ } |
+ ScopedComPtr<IDirect3D9> d3d9; |
+ ScopedComPtr<IDirect3DDevice9> device; |
+ ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
+ if (use_dxva) { |
+ dev_manager.Attach(CreateD3DDevManager(GetDesktopWindow(), |
+ width, |
+ height, |
+ d3d9.Receive(), |
+ device.Receive())); |
+ if (dev_manager.get() == NULL) { |
+ LOG(ERROR) << "Cannot create D3D9 manager"; |
+ return -1; |
+ } |
+ } |
+ scoped_ptr<FakeRenderer> renderer(new FakeRenderer()); |
+ scoped_ptr<MftH264Decoder> mft(new MftH264Decoder(use_dxva)); |
+ if (mft.get() == NULL) { |
+ LOG(ERROR) << "Failed to create MFT"; |
+ return -1; |
+ } |
+ if (!mft->Init(dev_manager, |
+ frame_rate_num, frame_rate_denom, |
+ width, height, |
+ aspect_ratio_num, aspect_ratio_denom, |
+ NewCallback<FFmpegFileReader, uint8**, int*, int64*, int64*>( |
+ reader.get(), &FFmpegFileReader::Read), |
+ NewCallback<FakeRenderer, scoped_refptr<VideoFrame> >( |
+ renderer.get(), &FakeRenderer::ProcessFrame))) { |
+ LOG(ERROR) << "Failed to initialize mft"; |
+ return -1; |
+ } |
+ TimeDelta decode_time; |
+ while (true) { |
+ Time decode_start(Time::Now()); |
+ if (MftH264Decoder::kOutputOk != mft->GetOutput()) |
+ break; |
+ decode_time += Time::Now() - decode_start; |
+ } |
+ printf("All done, frames read: %d, frames decoded: %d\n", |
+ mft->frames_read(), mft->frames_decoded()); |
+ printf("Took %lldms\n", decode_time.InMilliseconds()); |
+ return 0; |
+} |
+ |
+} // namespace |
+ |
+int main(int argc, char** argv) { |
+ 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"); |
+ 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("input-file: %s\n", input_file.c_str()); |
+ |
+ if (!InitFFmpeg()) { |
+ LOG(ERROR) << "InitFFMpeg() failed"; |
+ return -1; |
+ } |
+ if (!InitComLibraries()) { |
+ LOG(ERROR) << "InitComLibraries() failed"; |
+ return -1; |
+ } |
+ int ret = Run(use_dxva, input_file); |
+ ShutdownComLibraries(); |
+ printf("Done\n"); |
+ return ret; |
+} |