OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifdef UNICODE | 5 #ifdef UNICODE |
6 #undef UNICODE | 6 #undef UNICODE |
7 #endif | 7 #endif |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 | 10 |
11 #include <d3d9.h> | 11 #include <d3d9.h> |
12 #include <dxva2api.h> | 12 #include <dxva2api.h> |
13 #include <evr.h> | 13 #include <evr.h> |
14 #include <mfapi.h> | 14 #include <mfapi.h> |
15 #include <mfreadwrite.h> | 15 #include <mfreadwrite.h> |
16 #include <windows.h> | 16 #include <windows.h> |
17 | 17 |
18 #include "base/at_exit.h" | 18 #include "base/at_exit.h" |
19 #include "base/basictypes.h" | 19 #include "base/basictypes.h" |
20 #include "base/logging.h" | 20 #include "base/logging.h" |
21 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
22 #include "base/scoped_comptr_win.h" | 22 #include "base/scoped_comptr_win.h" |
23 #include "base/scoped_ptr.h" | 23 #include "base/scoped_ptr.h" |
24 #include "base/time.h" | 24 #include "base/time.h" |
25 #include "media/base/yuv_convert.h" | 25 #include "media/base/yuv_convert.h" |
26 #include "media/tools/mfdecoder/mfdecoder.h" | 26 #include "media/tools/mfdecoder/mfdecoder.h" |
27 #include "ui/gfx/gdi_util.h" | 27 #include "ui/gfx/gdi_util.h" |
28 | 28 |
29 namespace { | 29 static const char* const kWindowClass = "Chrome_MF_Decoder"; |
| 30 static const char* const kWindowTitle = "MF Decoder"; |
| 31 static const int kWindowStyleFlags = |
| 32 (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & ~(WS_MAXIMIZEBOX | WS_THICKFRAME); |
| 33 static bool g_render_to_window = false; |
| 34 static bool g_render_asap = false; |
30 | 35 |
31 const char* const kWindowClass = "Chrome_MF_Decoder"; | 36 static base::TimeDelta* g_decode_time; |
32 const char* const kWindowTitle = "MF Decoder"; | 37 static base::TimeDelta* g_render_time; |
33 const int kWindowStyleFlags = (WS_OVERLAPPEDWINDOW | WS_VISIBLE) & | 38 static int64 g_num_frames = 0; |
34 ~(WS_MAXIMIZEBOX | WS_THICKFRAME); | |
35 bool g_render_to_window = false; | |
36 bool g_render_asap = false; | |
37 | 39 |
38 base::TimeDelta* g_decode_time; | 40 static void usage() { |
39 base::TimeDelta* g_render_time; | |
40 int64 g_num_frames = 0; | |
41 | |
42 void usage() { | |
43 static char* usage_msg = "Usage: mfdecoder (-s|-h) (-d|-r|-f) input-file\n" | 41 static char* usage_msg = "Usage: mfdecoder (-s|-h) (-d|-r|-f) input-file\n" |
44 "-s: Use software decoding\n" | 42 "-s: Use software decoding\n" |
45 "-h: Use hardware decoding\n" | 43 "-h: Use hardware decoding\n" |
46 "\n" | 44 "\n" |
47 "-d: Decode to YV12 as fast as possible, no " \ | 45 "-d: Decode to YV12 as fast as possible, no " \ |
48 "rendering or color-space conversion\n" | 46 "rendering or color-space conversion\n" |
49 "-r: Render to window at 30ms per frame\n" | 47 "-r: Render to window at 30ms per frame\n" |
50 "-f: Decode and render as fast as possible\n" | 48 "-f: Decode and render as fast as possible\n" |
51 "\n" | 49 "\n" |
52 "To see this message: mfdecoder --help\n"; | 50 "To see this message: mfdecoder --help\n"; |
53 fprintf(stderr, "%s", usage_msg); | 51 fprintf(stderr, "%s", usage_msg); |
54 } | 52 } |
55 | 53 |
56 // Converts an ASCII string to an Unicode string. This function allocates | 54 // Converts an ASCII string to an Unicode string. This function allocates |
57 // space for the returned Unicode string from the heap and it is caller's | 55 // space for the returned Unicode string from the heap and it is caller's |
58 // responsibility to free it. | 56 // responsibility to free it. |
59 // Returns: An equivalent Unicode string if successful, NULL otherwise. | 57 // Returns: An equivalent Unicode string if successful, NULL otherwise. |
60 wchar_t* ConvertASCIIStringToUnicode(const char* source) { | 58 static wchar_t* ConvertASCIIStringToUnicode(const char* source) { |
61 if (source == NULL) { | 59 if (source == NULL) { |
62 LOG(ERROR) << "ConvertASCIIStringToUnicode: source cannot be NULL"; | 60 LOG(ERROR) << "ConvertASCIIStringToUnicode: source cannot be NULL"; |
63 return NULL; | 61 return NULL; |
64 } | 62 } |
65 DWORD string_length = MultiByteToWideChar(CP_ACP, 0, source, -1, NULL, 0); | 63 DWORD string_length = MultiByteToWideChar(CP_ACP, 0, source, -1, NULL, 0); |
66 if (string_length == 0) { | 64 if (string_length == 0) { |
67 LOG(ERROR) << "Error getting size of ansi string"; | 65 LOG(ERROR) << "Error getting size of ansi string"; |
68 return NULL; | 66 return NULL; |
69 } | 67 } |
70 scoped_array<wchar_t> ret(new wchar_t[string_length]); | 68 scoped_array<wchar_t> ret(new wchar_t[string_length]); |
71 if (ret.get() == NULL) { | 69 if (ret.get() == NULL) { |
72 LOG(ERROR) << "Error allocating unicode string buffer"; | 70 LOG(ERROR) << "Error allocating unicode string buffer"; |
73 return NULL; | 71 return NULL; |
74 } | 72 } |
75 if (MultiByteToWideChar(CP_ACP, 0, source, string_length, ret.get(), | 73 if (MultiByteToWideChar(CP_ACP, 0, source, string_length, ret.get(), |
76 string_length) == 0) { | 74 string_length) == 0) { |
77 LOG(ERROR) << "Error converting ansi string to unicode"; | 75 LOG(ERROR) << "Error converting ansi string to unicode"; |
78 return NULL; | 76 return NULL; |
79 } | 77 } |
80 return ret.release(); | 78 return ret.release(); |
81 } | 79 } |
82 | 80 |
83 // Converts the given raw data buffer into RGB32 format, and drawing the result | 81 // Converts the given raw data buffer into RGB32 format, and drawing the result |
84 // into the given window. This is only used when DXVA2 is not enabled. | 82 // into the given window. This is only used when DXVA2 is not enabled. |
85 // Returns: true on success. | 83 // Returns: true on success. |
86 bool ConvertToRGBAndDrawToWindow(HWND video_window, uint8* data, int width, | 84 static bool ConvertToRGBAndDrawToWindow(HWND video_window, uint8* data, |
87 int height, int stride) { | 85 int width, int height, int stride) { |
88 CHECK(video_window != NULL); | 86 CHECK(video_window != NULL); |
89 CHECK(data != NULL); | 87 CHECK(data != NULL); |
90 CHECK_GT(width, 0); | 88 CHECK_GT(width, 0); |
91 CHECK_GT(height, 0); | 89 CHECK_GT(height, 0); |
92 CHECK_GE(stride, width); | 90 CHECK_GE(stride, width); |
93 height = (height + 15) & ~15; | 91 height = (height + 15) & ~15; |
94 bool success = true; | 92 bool success = true; |
95 uint8* y_start = reinterpret_cast<uint8*>(data); | 93 uint8* y_start = reinterpret_cast<uint8*>(data); |
96 uint8* u_start = y_start + height * stride * 5 / 4; | 94 uint8* u_start = y_start + height * stride * 5 / 4; |
97 uint8* v_start = y_start + height * stride; | 95 uint8* v_start = y_start + height * stride; |
(...skipping 27 matching lines...) Expand all Loading... |
125 success = false; | 123 success = false; |
126 } | 124 } |
127 EndPaint(video_window, &ps); | 125 EndPaint(video_window, &ps); |
128 | 126 |
129 return success; | 127 return success; |
130 } | 128 } |
131 | 129 |
132 // Obtains the underlying raw data buffer for the given IMFMediaBuffer, and | 130 // Obtains the underlying raw data buffer for the given IMFMediaBuffer, and |
133 // calls ConvertToRGBAndDrawToWindow() with it. | 131 // calls ConvertToRGBAndDrawToWindow() with it. |
134 // Returns: true on success. | 132 // Returns: true on success. |
135 bool PaintMediaBufferOntoWindow(HWND video_window, IMFMediaBuffer* video_buffer, | 133 static bool PaintMediaBufferOntoWindow(HWND video_window, |
136 int width, int height, int stride) { | 134 IMFMediaBuffer* video_buffer, |
| 135 int width, int height, int stride) { |
137 CHECK(video_buffer != NULL); | 136 CHECK(video_buffer != NULL); |
138 HRESULT hr; | 137 HRESULT hr; |
139 BYTE* data; | 138 BYTE* data; |
140 DWORD buffer_length; | 139 DWORD buffer_length; |
141 DWORD data_length; | 140 DWORD data_length; |
142 hr = video_buffer->Lock(&data, &buffer_length, &data_length); | 141 hr = video_buffer->Lock(&data, &buffer_length, &data_length); |
143 if (FAILED(hr)) { | 142 if (FAILED(hr)) { |
144 LOG(ERROR) << "Failed to lock IMFMediaBuffer"; | 143 LOG(ERROR) << "Failed to lock IMFMediaBuffer"; |
145 return false; | 144 return false; |
146 } | 145 } |
(...skipping 10 matching lines...) Expand all Loading... |
157 } | 156 } |
158 *g_render_time += base::Time::Now() - render_start; | 157 *g_render_time += base::Time::Now() - render_start; |
159 } | 158 } |
160 video_buffer->Unlock(); | 159 video_buffer->Unlock(); |
161 return true; | 160 return true; |
162 } | 161 } |
163 | 162 |
164 // Obtains the D3D9 surface from the given IMFMediaBuffer, then calls methods | 163 // Obtains the D3D9 surface from the given IMFMediaBuffer, then calls methods |
165 // in the D3D device to draw to the window associated with it. | 164 // in the D3D device to draw to the window associated with it. |
166 // Returns: true on success. | 165 // Returns: true on success. |
167 bool PaintD3D9BufferOntoWindow(IDirect3DDevice9* device, | 166 static bool PaintD3D9BufferOntoWindow(IDirect3DDevice9* device, |
168 IMFMediaBuffer* video_buffer) { | 167 IMFMediaBuffer* video_buffer) { |
169 CHECK(device != NULL); | 168 CHECK(device != NULL); |
170 ScopedComPtr<IDirect3DSurface9> surface; | 169 ScopedComPtr<IDirect3DSurface9> surface; |
171 HRESULT hr = MFGetService(video_buffer, MR_BUFFER_SERVICE, | 170 HRESULT hr = MFGetService(video_buffer, MR_BUFFER_SERVICE, |
172 IID_PPV_ARGS(surface.Receive())); | 171 IID_PPV_ARGS(surface.Receive())); |
173 if (FAILED(hr)) { | 172 if (FAILED(hr)) { |
174 LOG(ERROR) << "Failed to get D3D9 surface from buffer"; | 173 LOG(ERROR) << "Failed to get D3D9 surface from buffer"; |
175 return false; | 174 return false; |
176 } | 175 } |
177 if (g_render_to_window) { | 176 if (g_render_to_window) { |
178 base::Time render_start(base::Time::Now()); | 177 base::Time render_start(base::Time::Now()); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 } | 211 } |
213 | 212 |
214 // Reads a sample from the given decoder, and draws the sample to the given | 213 // Reads a sample from the given decoder, and draws the sample to the given |
215 // window. Obtains the IMFMediaBuffer objects from the given IMFSample, and | 214 // window. Obtains the IMFMediaBuffer objects from the given IMFSample, and |
216 // calls either PaintMediaBufferOntoWindow() or PaintD3D9BufferOntoWindow() with | 215 // calls either PaintMediaBufferOntoWindow() or PaintD3D9BufferOntoWindow() with |
217 // each of them, depending on whether the decoder supports DXVA2. | 216 // each of them, depending on whether the decoder supports DXVA2. |
218 // The decoder should be initialized before calling this method. | 217 // The decoder should be initialized before calling this method. |
219 // For H.264 format, there should only be 1 buffer per sample, so each buffer | 218 // For H.264 format, there should only be 1 buffer per sample, so each buffer |
220 // represents 1 frame. | 219 // represents 1 frame. |
221 // Returns: true if successful. | 220 // Returns: true if successful. |
222 bool DrawVideoSample(HWND video_window, media::MFDecoder* decoder, | 221 static bool DrawVideoSample(HWND video_window, media::MFDecoder* decoder, |
223 IDirect3DDevice9* device) { | 222 IDirect3DDevice9* device) { |
224 CHECK(video_window != NULL); | 223 CHECK(video_window != NULL); |
225 CHECK(decoder != NULL); | 224 CHECK(decoder != NULL); |
226 CHECK(decoder->initialized()); | 225 CHECK(decoder->initialized()); |
227 | 226 |
228 if (decoder->end_of_stream()) { | 227 if (decoder->end_of_stream()) { |
229 LOG(ERROR) << "Failed to obtain more samples from decoder because end of " | 228 LOG(ERROR) << "Failed to obtain more samples from decoder because end of " |
230 << "stream has been reached"; | 229 << "stream has been reached"; |
231 return false; | 230 return false; |
232 } | 231 } |
233 ScopedComPtr<IMFSample> video_sample; | 232 ScopedComPtr<IMFSample> video_sample; |
(...skipping 27 matching lines...) Expand all Loading... |
261 return PaintD3D9BufferOntoWindow(device, video_buffer); | 260 return PaintD3D9BufferOntoWindow(device, video_buffer); |
262 } else { | 261 } else { |
263 return PaintMediaBufferOntoWindow(video_window, video_buffer, | 262 return PaintMediaBufferOntoWindow(video_window, video_buffer, |
264 decoder->width(), decoder->height(), | 263 decoder->width(), decoder->height(), |
265 decoder->mfbuffer_stride()); | 264 decoder->mfbuffer_stride()); |
266 } | 265 } |
267 } | 266 } |
268 | 267 |
269 // Creates a window with the given width and height. | 268 // Creates a window with the given width and height. |
270 // Returns: A handle to the window on success, NULL otherwise. | 269 // Returns: A handle to the window on success, NULL otherwise. |
271 HWND CreateDrawWindow(int width, int height) { | 270 static HWND CreateDrawWindow(int width, int height) { |
272 WNDCLASS window_class = {0}; | 271 WNDCLASS window_class = {0}; |
273 window_class.lpszClassName = kWindowClass; | 272 window_class.lpszClassName = kWindowClass; |
274 window_class.hInstance = NULL; | 273 window_class.hInstance = NULL; |
275 window_class.hbrBackground = 0; | 274 window_class.hbrBackground = 0; |
276 window_class.lpfnWndProc = DefWindowProc; | 275 window_class.lpfnWndProc = DefWindowProc; |
277 window_class.hCursor = LoadCursor(0, IDC_ARROW); | 276 window_class.hCursor = LoadCursor(0, IDC_ARROW); |
278 | 277 |
279 if (RegisterClass(&window_class) == 0) { | 278 if (RegisterClass(&window_class) == 0) { |
280 LOG(ERROR) << "Failed to register window class"; | 279 LOG(ERROR) << "Failed to register window class"; |
281 return false; | 280 return false; |
(...skipping 14 matching lines...) Expand all Loading... |
296 return NULL; | 295 return NULL; |
297 } | 296 } |
298 return window; | 297 return window; |
299 } | 298 } |
300 | 299 |
301 // This function creates a D3D Device and a D3D Device Manager, sets the manager | 300 // This function creates a D3D Device and a D3D Device Manager, sets the manager |
302 // to use the device, and returns the manager. It also initializes the D3D | 301 // to use the device, and returns the manager. It also initializes the D3D |
303 // device. This function is used by mfdecoder.cc during the call to | 302 // device. This function is used by mfdecoder.cc during the call to |
304 // MFDecoder::GetDXVA2AttributesForSourceReader(). | 303 // MFDecoder::GetDXVA2AttributesForSourceReader(). |
305 // Returns: The D3D manager object if successful. Otherwise, NULL is returned. | 304 // Returns: The D3D manager object if successful. Otherwise, NULL is returned. |
306 IDirect3DDeviceManager9* CreateD3DDevManager(HWND video_window, | 305 static IDirect3DDeviceManager9* CreateD3DDevManager(HWND video_window, |
307 IDirect3DDevice9** device) { | 306 IDirect3DDevice9** device) { |
308 CHECK(video_window != NULL); | 307 CHECK(video_window != NULL); |
309 CHECK(device != NULL); | 308 CHECK(device != NULL); |
310 int ret = -1; | 309 int ret = -1; |
311 | 310 |
312 ScopedComPtr<IDirect3DDeviceManager9> dev_manager; | 311 ScopedComPtr<IDirect3DDeviceManager9> dev_manager; |
313 ScopedComPtr<IDirect3D9> d3d; | 312 ScopedComPtr<IDirect3D9> d3d; |
314 d3d.Attach(Direct3DCreate9(D3D_SDK_VERSION)); | 313 d3d.Attach(Direct3DCreate9(D3D_SDK_VERSION)); |
315 if (d3d == NULL) { | 314 if (d3d == NULL) { |
316 LOG(ERROR) << "Failed to create D3D9"; | 315 LOG(ERROR) << "Failed to create D3D9"; |
317 return NULL; | 316 return NULL; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 } | 358 } |
360 *device = temp_device.Detach(); | 359 *device = temp_device.Detach(); |
361 return dev_manager.Detach(); | 360 return dev_manager.Detach(); |
362 } | 361 } |
363 | 362 |
364 // Resets the D3D device to prevent scaling from happening because it was | 363 // Resets the D3D device to prevent scaling from happening because it was |
365 // created with window before resizing occurred. We need to change the back | 364 // created with window before resizing occurred. We need to change the back |
366 // buffer dimensions to the actual video frame dimensions. | 365 // buffer dimensions to the actual video frame dimensions. |
367 // Both the decoder and device should be initialized before calling this method. | 366 // Both the decoder and device should be initialized before calling this method. |
368 // Returns: true if successful. | 367 // Returns: true if successful. |
369 bool AdjustD3DDeviceBackBufferDimensions(media::MFDecoder* decoder, | 368 static bool AdjustD3DDeviceBackBufferDimensions(media::MFDecoder* decoder, |
370 IDirect3DDevice9* device, | 369 IDirect3DDevice9* device, |
371 HWND video_window) { | 370 HWND video_window) { |
372 CHECK(decoder != NULL); | 371 CHECK(decoder != NULL); |
373 CHECK(decoder->initialized()); | 372 CHECK(decoder->initialized()); |
374 CHECK(decoder->use_dxva2()); | 373 CHECK(decoder->use_dxva2()); |
375 CHECK(device != NULL); | 374 CHECK(device != NULL); |
376 D3DPRESENT_PARAMETERS present_params = {0}; | 375 D3DPRESENT_PARAMETERS present_params = {0}; |
377 present_params.BackBufferWidth = decoder->width(); | 376 present_params.BackBufferWidth = decoder->width(); |
378 present_params.BackBufferHeight = decoder->height(); | 377 present_params.BackBufferHeight = decoder->height(); |
379 present_params.BackBufferFormat = D3DFMT_UNKNOWN; | 378 present_params.BackBufferFormat = D3DFMT_UNKNOWN; |
380 present_params.BackBufferCount = 1; | 379 present_params.BackBufferCount = 1; |
381 present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; | 380 present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; |
382 present_params.hDeviceWindow = video_window; | 381 present_params.hDeviceWindow = video_window; |
383 present_params.Windowed = TRUE; | 382 present_params.Windowed = TRUE; |
384 present_params.Flags = D3DPRESENTFLAG_VIDEO; | 383 present_params.Flags = D3DPRESENTFLAG_VIDEO; |
385 present_params.FullScreen_RefreshRateInHz = 0; | 384 present_params.FullScreen_RefreshRateInHz = 0; |
386 present_params.PresentationInterval = 0; | 385 present_params.PresentationInterval = 0; |
387 | 386 |
388 return SUCCEEDED(device->Reset(&present_params)) ? true : false; | 387 return SUCCEEDED(device->Reset(&present_params)) ? true : false; |
389 } | 388 } |
390 | 389 |
391 // Post this task in the MessageLoop. This function keeps posting itself | 390 // Post this task in the MessageLoop. This function keeps posting itself |
392 // until DrawVideoSample fails. | 391 // until DrawVideoSample fails. |
393 void RepaintTask(media::MFDecoder* decoder, HWND video_window, | 392 static void RepaintTask(media::MFDecoder* decoder, HWND video_window, |
394 IDirect3DDevice9* device) { | 393 IDirect3DDevice9* device) { |
395 // This sends a WM_PAINT message so we can paint on the window later. | 394 // This sends a WM_PAINT message so we can paint on the window later. |
396 // If we are using D3D9, then we do not send a WM_PAINT message since the two | 395 // If we are using D3D9, then we do not send a WM_PAINT message since the two |
397 // do not work well together. | 396 // do not work well together. |
398 if (!decoder->use_dxva2()) | 397 if (!decoder->use_dxva2()) |
399 InvalidateRect(video_window, NULL, TRUE); | 398 InvalidateRect(video_window, NULL, TRUE); |
400 base::Time start(base::Time::Now()); | 399 base::Time start(base::Time::Now()); |
401 if (!DrawVideoSample(video_window, decoder, device)) { | 400 if (!DrawVideoSample(video_window, decoder, device)) { |
402 LOG(ERROR) << "DrawVideoSample failed, quitting MessageLoop"; | 401 LOG(ERROR) << "DrawVideoSample failed, quitting MessageLoop"; |
403 MessageLoopForUI::current()->Quit(); | 402 MessageLoopForUI::current()->Quit(); |
404 } else { | 403 } else { |
405 ++g_num_frames; | 404 ++g_num_frames; |
406 if (!g_render_asap && g_render_to_window) { | 405 if (!g_render_asap && g_render_to_window) { |
407 base::Time end(base::Time::Now()); | 406 base::Time end(base::Time::Now()); |
408 int64 delta = (end-start).InMilliseconds(); | 407 int64 delta = (end-start).InMilliseconds(); |
409 MessageLoopForUI::current()->PostDelayedTask( | 408 MessageLoopForUI::current()->PostDelayedTask( |
410 FROM_HERE, | 409 FROM_HERE, |
411 NewRunnableFunction(&RepaintTask, decoder, video_window, device), | 410 NewRunnableFunction(&RepaintTask, decoder, video_window, device), |
412 std::max<int64>(0L, 30-delta)); | 411 std::max<int64>(0L, 30-delta)); |
413 } else { | 412 } else { |
414 MessageLoopForUI::current()->PostTask( | 413 MessageLoopForUI::current()->PostTask( |
415 FROM_HERE, | 414 FROM_HERE, |
416 NewRunnableFunction(&RepaintTask, decoder, video_window, device)); | 415 NewRunnableFunction(&RepaintTask, decoder, video_window, device)); |
417 } | 416 } |
418 } | 417 } |
419 } | 418 } |
420 | 419 |
421 } // namespace | |
422 | |
423 int main(int argc, char** argv) { | 420 int main(int argc, char** argv) { |
424 if (argc < 2) { | 421 if (argc < 2) { |
425 fprintf(stderr, "missing arguments\n"); | 422 fprintf(stderr, "missing arguments\n"); |
426 usage(); | 423 usage(); |
427 return -1; | 424 return -1; |
428 } | 425 } |
429 if (strcmp(argv[1], "--help") == 0) { | 426 if (strcmp(argv[1], "--help") == 0) { |
430 usage(); | 427 usage(); |
431 return 0; | 428 return 0; |
432 } | 429 } |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
551 << "\nAverage decode time: " << ((g_num_frames == 0) ? | 548 << "\nAverage decode time: " << ((g_num_frames == 0) ? |
552 0 : (g_decode_time->InMillisecondsF() / g_num_frames)) | 549 0 : (g_decode_time->InMillisecondsF() / g_num_frames)) |
553 << "\nRender time: " << g_render_time->InMilliseconds() << "ms" | 550 << "\nRender time: " << g_render_time->InMilliseconds() << "ms" |
554 << "\nAverage render time: " << ((g_num_frames == 0) ? | 551 << "\nAverage render time: " << ((g_num_frames == 0) ? |
555 0 : (g_render_time->InMillisecondsF() / g_num_frames)); | 552 0 : (g_render_time->InMillisecondsF() / g_num_frames)); |
556 printf("Normal termination\n"); | 553 printf("Normal termination\n"); |
557 delete g_decode_time; | 554 delete g_decode_time; |
558 delete g_render_time; | 555 delete g_render_time; |
559 return 0; | 556 return 0; |
560 } | 557 } |
OLD | NEW |