diff options
author | imcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-10 18:48:53 +0000 |
---|---|---|
committer | imcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-10 18:48:53 +0000 |
commit | 3ac2e1cb25ae429513eb702c3ecd5a76deafa63f (patch) | |
tree | 4df048123a9f2bb81103b83d7e2ce6577c45483c /media/tools | |
parent | 2b610dd17148a71cad5da51b3eabf31d2cdc80c7 (diff) | |
download | chromium_src-3ac2e1cb25ae429513eb702c3ecd5a76deafa63f.zip chromium_src-3ac2e1cb25ae429513eb702c3ecd5a76deafa63f.tar.gz chromium_src-3ac2e1cb25ae429513eb702c3ecd5a76deafa63f.tar.bz2 |
Fixed issue with rendering in mfdecoder where it crashes when rendering D3D surfaces. Also fixed issue with flickering when drawing software-decoded frames.
BUG=49364
TEST=Run the program by passing in -s or -h, and -r and see if it works.
Review URL: http://codereview.chromium.org/3066047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55594 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/tools')
-rw-r--r-- | media/tools/mfdecoder/README.chromium | 21 | ||||
-rw-r--r-- | media/tools/mfdecoder/main.cc | 174 |
2 files changed, 102 insertions, 93 deletions
diff --git a/media/tools/mfdecoder/README.chromium b/media/tools/mfdecoder/README.chromium index ac4994b..c946a54 100644 --- a/media/tools/mfdecoder/README.chromium +++ b/media/tools/mfdecoder/README.chromium @@ -24,26 +24,23 @@ Note: The current version uses a synchronous version of source reader. An asynchronous version will likely to be more useful and have better performance. -Note2: There might be some artifacts produced when displaying using software -decoding. This is related to execution of MMX instructions and alignment -issues with the video buffer during color-space conversion and is not a bug. - -Note3: The program might crash randomly when rendering windows using D3D -surfaces (i.e., both -h and -r flags are specified), especially for videos -for larger dimensions. This seems to be caused by the D3D Device's Present() -method during rendering even though the decoding behaves correctly. - -Note4: The maximum resolution supported by Microsoft's H.264 decoder is +Note2: The maximum resolution supported by Microsoft's H.264 decoder is 1920 by 1088 pixels. If you try to feed it a larger video, it will "fail to determine frame size." This is an inherent limitation that cannot be fixed unless a different decoder is used. + Requirements: Windows 7 -usage: mfdecoder (-s|-h) (-d|-r) input-file +usage: mfdecoder (-s|-h) (-d|-r|-f) input-file flags: -s: Use software decoding -h: Uses hardware decoding -d: Decode to YV12 as fast as possible, no rendering or color-space conversion --r: Render to window
\ No newline at end of file +-r: Render to window +-f: Decode+render as fast as possible + +WARNING: Using both -h and -f, or opening too many windows with -h may lead to +driver crash / system instability. Realistically, you will never want to +do this unless you want to push the limits of the GPU ...
\ No newline at end of file diff --git a/media/tools/mfdecoder/main.cc b/media/tools/mfdecoder/main.cc index c531481..42d8d51 100644 --- a/media/tools/mfdecoder/main.cc +++ b/media/tools/mfdecoder/main.cc @@ -6,6 +6,8 @@ #undef UNICODE #endif +#include <algorithm> + #include <d3d9.h> #include <dxva2api.h> #include <evr.h> @@ -31,15 +33,21 @@ const char* const kWindowTitle = "MF Decoder"; const int kWindowStyleFlags = (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & ~(WS_MAXIMIZEBOX | WS_THICKFRAME); bool g_render_to_window = false; +bool g_render_asap = false; + +base::TimeDelta* g_decode_time; +base::TimeDelta* g_render_time; +int64 g_num_frames = 0; void usage() { - static char* usage_msg = "Usage: mfdecoder (-s|-h) (-d|-r) input-file\n" + static char* usage_msg = "Usage: mfdecoder (-s|-h) (-d|-r|-f) input-file\n" "-s: Use software decoding\n" "-h: Use hardware decoding\n" "\n" "-d: Decode to YV12 as fast as possible, no " \ "rendering or color-space conversion\n" - "-r: Render to window\n" + "-r: Render to window at 30ms per frame\n" + "-f: Decode and render as fast as possible\n" "\n" "To see this message: mfdecoder --help\n"; fprintf(stderr, "%s", usage_msg); @@ -82,6 +90,7 @@ bool ConvertToRGBAndDrawToWindow(HWND video_window, uint8* data, int width, CHECK_GT(width, 0); CHECK_GT(height, 0); CHECK_GE(stride, width); + height = (height + 15) & ~15; bool success = true; uint8* y_start = reinterpret_cast<uint8*>(data); uint8* u_start = y_start + height * stride * 5 / 4; @@ -136,6 +145,7 @@ bool PaintMediaBufferOntoWindow(HWND video_window, IMFMediaBuffer* video_buffer, return false; } if (g_render_to_window) { + base::Time render_start(base::Time::Now()); if (!ConvertToRGBAndDrawToWindow(video_window, reinterpret_cast<uint8*>(data), width, @@ -145,6 +155,7 @@ bool PaintMediaBufferOntoWindow(HWND video_window, IMFMediaBuffer* video_buffer, video_buffer->Unlock(); return false; } + *g_render_time += base::Time::Now() - render_start; } video_buffer->Unlock(); return true; @@ -164,7 +175,8 @@ bool PaintD3D9BufferOntoWindow(IDirect3DDevice9* device, return false; } if (g_render_to_window) { - hr = device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255), + base::Time render_start(base::Time::Now()); + hr = device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); if (FAILED(hr)) { LOG(ERROR) << "Device->Clear() failed"; @@ -184,7 +196,7 @@ bool PaintD3D9BufferOntoWindow(IDirect3DDevice9* device, return false; } hr = device->Present(NULL, NULL, NULL, NULL); - if (FAILED(hr)) { + if (FAILED(hr) && hr != E_FAIL) { static int frames_dropped = 0; LOG(ERROR) << "Device->Present() failed " << std::hex << std::showbase << hr; @@ -194,6 +206,7 @@ bool PaintD3D9BufferOntoWindow(IDirect3DDevice9* device, return false; } } + *g_render_time += base::Time::Now() - render_start; } return true; } @@ -218,12 +231,14 @@ bool DrawVideoSample(HWND video_window, media::MFDecoder* decoder, return false; } ScopedComPtr<IMFSample> video_sample; + base::Time decode_time_start(base::Time::Now()); video_sample.Attach(decoder->ReadVideoSample()); if (video_sample.get() == NULL) { LOG(ERROR) << "Failed to obtain a sample from decoder: end of stream? " << (decoder->end_of_stream() ? "true" : "false"); return false; } + *g_decode_time += base::Time::Now() - decode_time_start; // Get the buffer inside the sample. DWORD buffer_count; @@ -234,7 +249,7 @@ bool DrawVideoSample(HWND video_window, media::MFDecoder* decoder, } // For H.264 videos, the number of buffers in the sample is 1. - CHECK_EQ(static_cast<int>(buffer_count), 1) << "buffer_count should be equal " + CHECK_EQ(buffer_count, 1u) << "buffer_count should be equal " << "to 1 for H.264 format"; ScopedComPtr<IMFMediaBuffer> video_buffer; hr = video_sample->GetBufferByIndex(0, video_buffer.Receive()); @@ -257,7 +272,7 @@ HWND CreateDrawWindow(int width, int height) { WNDCLASS window_class = {0}; window_class.lpszClassName = kWindowClass; window_class.hInstance = NULL; - window_class.hbrBackground = GetSysColorBrush(COLOR_3DFACE); + window_class.hbrBackground = 0; window_class.lpfnWndProc = DefWindowProc; window_class.hCursor = LoadCursor(0, IDC_ARROW); @@ -307,7 +322,7 @@ IDirect3DDeviceManager9* CreateD3DDevManager(HWND video_window, // they even matter. (taken from DXVA_HD sample code) present_params.BackBufferWidth = 0; present_params.BackBufferHeight = 0; - present_params.BackBufferFormat = D3DFMT_X8R8G8B8; + present_params.BackBufferFormat = D3DFMT_UNKNOWN; present_params.BackBufferCount = 1; present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; present_params.hDeviceWindow = video_window; @@ -317,10 +332,11 @@ IDirect3DDeviceManager9* CreateD3DDevManager(HWND video_window, present_params.PresentationInterval = 0; ScopedComPtr<IDirect3DDevice9> temp_device; + // D3DCREATE_HARDWARE_VERTEXPROCESSING specifies hardware vertex processing. HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, - video_window, + NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &present_params, temp_device.Receive()); @@ -357,10 +373,9 @@ bool AdjustD3DDeviceBackBufferDimensions(media::MFDecoder* decoder, CHECK(decoder->use_dxva2()); CHECK(device != NULL); D3DPRESENT_PARAMETERS present_params = {0}; - memset(&present_params, 0, sizeof(present_params)); present_params.BackBufferWidth = decoder->width(); present_params.BackBufferHeight = decoder->height(); - present_params.BackBufferFormat = D3DFMT_X8R8G8B8; + present_params.BackBufferFormat = D3DFMT_UNKNOWN; present_params.BackBufferCount = 1; present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; present_params.hDeviceWindow = video_window; @@ -372,58 +387,36 @@ bool AdjustD3DDeviceBackBufferDimensions(media::MFDecoder* decoder, return SUCCEEDED(device->Reset(&present_params)) ? true : false; } -// Post this task in the MessageLoop if DXVA2 is enabled. -void RepaintD3D9(media::MFDecoder* decoder, HWND video_window, +// Post this task in the MessageLoop. This function keeps posting itself +// until DrawVideoSample fails. +void RepaintTask(media::MFDecoder* decoder, HWND video_window, IDirect3DDevice9* device) { + // This sends a WM_PAINT message so we can paint on the window later. // If we are using D3D9, then we do not send a WM_PAINT message since the two // do not work well together. + if (!decoder->use_dxva2()) + InvalidateRect(video_window, NULL, TRUE); + base::Time start(base::Time::Now()); if (!DrawVideoSample(video_window, decoder, device)) { LOG(ERROR) << "DrawVideoSample failed, quitting MessageLoop"; MessageLoopForUI::current()->Quit(); } else { - if (g_render_to_window) { + ++g_num_frames; + if (!g_render_asap && g_render_to_window) { + base::Time end(base::Time::Now()); + int64 delta = (end-start).InMilliseconds(); MessageLoopForUI::current()->PostDelayedTask( FROM_HERE, - NewRunnableFunction(&RepaintD3D9, decoder, video_window, device), 30); + NewRunnableFunction(&RepaintTask, decoder, video_window, device), + std::max<int64>(0L, 30-delta)); } else { MessageLoopForUI::current()->PostTask( FROM_HERE, - NewRunnableFunction(&RepaintD3D9, decoder, video_window, device)); + NewRunnableFunction(&RepaintTask, decoder, video_window, device)); } } } -// Post this task in the MessageLoop if DXVA2 is NOT enabled. -void RepaintGdi(media::MFDecoder* decoder, HWND video_window) { - // This sends a WM_PAINT message so we can paint on the window later. - InvalidateRect(video_window, NULL, TRUE); - - // We do not have a D3D device if we did not enable DXVA2, so NULL is passed - // in. - if (!DrawVideoSample(video_window, decoder, NULL)) { - LOG(ERROR) << "DrawVideoSample failed, quitting MessageLoop"; - MessageLoopForUI::current()->Quit(); - } else { - if (g_render_to_window) { - MessageLoopForUI::current()->PostDelayedTask( - FROM_HERE, - NewRunnableFunction(&RepaintGdi, decoder, video_window), 30); - } else { - MessageLoopForUI::current()->PostTask( - FROM_HERE, - NewRunnableFunction(&RepaintGdi, decoder, video_window)); - } - } -} - -// Implementation of Observer for MessageLoopForUI. -class WindowObserver : public MessageLoopForUI::Observer { - public: - virtual void WillProcessMessage(const MSG& msg) {} - - virtual void DidProcessMessage(const MSG& msg) {} -}; - } // namespace int main(int argc, char** argv) { @@ -455,16 +448,21 @@ int main(int argc, char** argv) { LOG(INFO) << "use_dxva2: " << use_dxva2; g_render_to_window = false; + g_render_asap = false; if (strcmp(argv[2], "-d") == 0) { g_render_to_window = false; } else if (strcmp(argv[2], "-r") == 0) { g_render_to_window = true; + } else if (strcmp(argv[2], "-f") == 0) { + g_render_to_window = true; + g_render_asap = true; } else { fprintf(stderr, "unknown option %s\n", argv[2]); usage(); return -1; } LOG(INFO) << "g_render_to_window: " << g_render_to_window; + LOG(INFO) << "g_render_asap: " << g_render_asap; scoped_array<wchar_t> file_name(ConvertASCIIStringToUnicode(argv[argc-1])); if (file_name.get() == NULL) { @@ -474,7 +472,8 @@ int main(int argc, char** argv) { // Once we initialized the decoder, we should resize the window to frame size. // For now, just create a window with arbitrary dimensions. - HWND video_window = CreateDrawWindow(640, 480); + HWND video_window = g_render_to_window ? CreateDrawWindow(640, 480) + : GetDesktopWindow(); if (video_window == NULL) { LOG(ERROR) << "main: Failed to create the video window"; return -1; @@ -499,53 +498,66 @@ int main(int argc, char** argv) { } // Resize the window to the dimensions of video frame. - RECT rect; - rect.left = 0; - rect.right = decoder->width(); - rect.top = 0; - rect.bottom = decoder->height(); - AdjustWindowRect(&rect, kWindowStyleFlags, FALSE); - if (!MoveWindow(video_window, 0, 0, rect.right - rect.left, - rect.bottom - rect.top, TRUE)) { - LOG(WARNING) << "Warning: Failed to resize window"; - } - if (decoder->use_dxva2()) { - // Reset the device's back buffer dimensions to match the window's - // dimensions. - if (!AdjustD3DDeviceBackBufferDimensions(decoder.get(), device.get(), - video_window)) { - LOG(WARNING) << "Warning: Failed to reset device to have correct " - << "backbuffer dimension, scaling might occur"; + if (g_render_to_window) { + RECT rect; + rect.left = 0; + rect.right = decoder->width(); + rect.top = 0; + rect.bottom = decoder->height(); + AdjustWindowRect(&rect, kWindowStyleFlags, FALSE); + if (!MoveWindow(video_window, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, TRUE)) { + LOG(WARNING) << "Warning: Failed to resize window"; } + if (decoder->use_dxva2()) { + // Reset the device's back buffer dimensions to match the window's + // dimensions. + if (!AdjustD3DDeviceBackBufferDimensions(decoder.get(), device.get(), + video_window)) { + LOG(WARNING) << "Warning: Failed to reset device to have correct " + << "backbuffer dimension, scaling might occur"; + } + } + } + g_decode_time = new base::TimeDelta(); + g_render_time = new base::TimeDelta(); + if (g_decode_time == NULL || g_render_time == NULL) { + fprintf(stderr, "Failed to create decode/render timers\n"); + return -1; } base::Time start(base::Time::Now()); printf("Decoding started\n"); - LOG(INFO) << "Decoding started at " << start.ToTimeT(); + LOG(INFO) << "Decoding " << file_name.get() + << " started at " << start.ToTimeT(); - // MessageLoop base::AtExitManager exit_manager; MessageLoopForUI message_loop; - WindowObserver window_observer; - MessageLoopForUI::current()->AddObserver(&window_observer); - if (decoder->use_dxva2()) { - MessageLoopForUI::current()->PostTask(FROM_HERE, - NewRunnableFunction(&RepaintD3D9, - decoder.get(), - video_window, - device.get())); - } else { - MessageLoopForUI::current()->PostTask(FROM_HERE, - NewRunnableFunction(&RepaintGdi, - decoder.get(), - video_window)); - } + + // The device is NULL if DXVA2 is not enabled. + MessageLoopForUI::current()->PostTask(FROM_HERE, + NewRunnableFunction(&RepaintTask, + decoder.get(), + video_window, + device.get())); MessageLoopForUI::current()->Run(NULL); printf("Decoding finished\n"); base::Time end(base::Time::Now()); LOG(INFO) << "Decoding finished at " << end.ToTimeT(); LOG(INFO) << "Took " << (end-start).InMilliseconds() << "ms"; - + LOG(INFO) << "Number of frames processed: " << g_num_frames; + LOG(INFO) << "Decode time: " << g_decode_time->InMilliseconds() << "ms"; + LOG(INFO) << "Average decode time: " + << (g_num_frames == 0 ? + 0 : + g_decode_time->InMillisecondsF() / g_num_frames); + LOG(INFO) << "Render time: " << g_render_time->InMilliseconds() << "ms"; + LOG(INFO) << "Average render time: " + << (g_num_frames == 0 ? + 0 : + g_render_time->InMillisecondsF() / g_num_frames); printf("Normal termination\n"); + delete g_decode_time; + delete g_render_time; return 0; } |