OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 // Standalone benchmarking application based on FFmpeg. This tool is used to | 5 // Standalone benchmarking application based on FFmpeg. This tool is used to |
6 // measure decoding performance between different FFmpeg compile and run-time | 6 // measure decoding performance between different FFmpeg compile and run-time |
7 // options. We also use this tool to measure performance regressions when | 7 // options. We also use this tool to measure performance regressions when |
8 // testing newer builds of FFmpeg from trunk. | 8 // testing newer builds of FFmpeg from trunk. |
9 // | 9 // |
10 // This tool requires FFMPeg DLL's built with --enable-protocol=file. | 10 // This tool requires FFMPeg DLL's built with --enable-protocol=file. |
(...skipping 14 matching lines...) Expand all Loading... |
25 #include "media/bench/file_protocol.h" | 25 #include "media/bench/file_protocol.h" |
26 #include "media/filters/ffmpeg_common.h" | 26 #include "media/filters/ffmpeg_common.h" |
27 #include "media/filters/ffmpeg_video_decoder.h" | 27 #include "media/filters/ffmpeg_video_decoder.h" |
28 | 28 |
29 namespace switches { | 29 namespace switches { |
30 const wchar_t kStream[] = L"stream"; | 30 const wchar_t kStream[] = L"stream"; |
31 const wchar_t kVideoThreads[] = L"video-threads"; | 31 const wchar_t kVideoThreads[] = L"video-threads"; |
32 const wchar_t kFast2[] = L"fast2"; | 32 const wchar_t kFast2[] = L"fast2"; |
33 const wchar_t kSkip[] = L"skip"; | 33 const wchar_t kSkip[] = L"skip"; |
34 const wchar_t kFlush[] = L"flush"; | 34 const wchar_t kFlush[] = L"flush"; |
| 35 const wchar_t kHash[] = L"hash"; |
35 } // namespace switches | 36 } // namespace switches |
36 | 37 |
| 38 namespace { |
| 39 // DJB2 hash |
| 40 unsigned int hash_djb2(const uint8* s, |
| 41 size_t len, unsigned int hash) { |
| 42 while (len--) |
| 43 hash = hash * 33 + *s++; |
| 44 return hash; |
| 45 } |
| 46 } |
| 47 |
37 int main(int argc, const char** argv) { | 48 int main(int argc, const char** argv) { |
38 base::AtExitManager exit_manager; | 49 base::AtExitManager exit_manager; |
39 | 50 |
40 CommandLine::Init(argc, argv); | 51 CommandLine::Init(argc, argv); |
41 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 52 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
42 | 53 |
43 std::vector<std::wstring> filenames(cmd_line->GetLooseValues()); | 54 std::vector<std::wstring> filenames(cmd_line->GetLooseValues()); |
44 if (filenames.empty()) { | 55 if (filenames.empty()) { |
45 std::cerr << "Usage: media_bench [OPTIONS] FILE [DUMPFILE]\n" | 56 std::cerr << "Usage: media_bench [OPTIONS] FILE [DUMPFILE]\n" |
46 << " --stream=[audio|video] " | 57 << " --stream=[audio|video] " |
47 << "Benchmark either the audio or video stream\n" | 58 << "Benchmark either the audio or video stream\n" |
48 << " --video-threads=N " | 59 << " --video-threads=N " |
49 << "Decode video using N threads\n" | 60 << "Decode video using N threads\n" |
50 << " --fast2 " | 61 << " --fast2 " |
51 << "Enable fast2 flag\n" | 62 << "Enable fast2 flag\n" |
52 << " --flush " | 63 << " --flush " |
53 << "Flush last frame\n" | 64 << "Flush last frame\n" |
| 65 << " --hash " |
| 66 << "Hash decoded buffers\n" |
54 << " --skip=[1|2|3] " | 67 << " --skip=[1|2|3] " |
55 << "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl; | 68 << "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl; |
56 return 1; | 69 return 1; |
57 } | 70 } |
58 | 71 |
59 // Initialize our media library (try loading DLLs, etc.) before continuing. | 72 // Initialize our media library (try loading DLLs, etc.) before continuing. |
60 // We use an empty file path as the parameter to force searching of the | 73 // We use an empty file path as the parameter to force searching of the |
61 // default locations for necessary DLLs and DSOs. | 74 // default locations for necessary DLLs and DSOs. |
62 if (media::InitializeMediaLibrary(FilePath()) == false) { | 75 if (media::InitializeMediaLibrary(FilePath()) == false) { |
63 std::cerr << "Unable to initialize the media library."; | 76 std::cerr << "Unable to initialize the media library."; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 bool fast2 = false; | 109 bool fast2 = false; |
97 if (cmd_line->HasSwitch(switches::kFast2)) { | 110 if (cmd_line->HasSwitch(switches::kFast2)) { |
98 fast2 = true; | 111 fast2 = true; |
99 } | 112 } |
100 | 113 |
101 bool flush = false; | 114 bool flush = false; |
102 if (cmd_line->HasSwitch(switches::kFlush)) { | 115 if (cmd_line->HasSwitch(switches::kFlush)) { |
103 flush = true; | 116 flush = true; |
104 } | 117 } |
105 | 118 |
| 119 unsigned int hash_value = 5381u; // Seed for DJB2. |
| 120 bool hash = false; |
| 121 if (cmd_line->HasSwitch(switches::kHash)) { |
| 122 hash = true; |
| 123 } |
| 124 |
106 int skip = 0; | 125 int skip = 0; |
107 if (cmd_line->HasSwitch(switches::kSkip)) { | 126 if (cmd_line->HasSwitch(switches::kSkip)) { |
108 std::wstring skip_opt(cmd_line->GetSwitchValue(switches::kSkip)); | 127 std::wstring skip_opt(cmd_line->GetSwitchValue(switches::kSkip)); |
109 if (!StringToInt(WideToUTF16Hack(skip_opt), &skip)) { | 128 if (!StringToInt(WideToUTF16Hack(skip_opt), &skip)) { |
110 skip = 0; | 129 skip = 0; |
111 } | 130 } |
112 } | 131 } |
113 | 132 |
114 // Register FFmpeg and attempt to open file. | 133 // Register FFmpeg and attempt to open file. |
115 avcodec_init(); | 134 avcodec_init(); |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 ++frames; | 258 ++frames; |
240 read_result = 0; // Force continuation. | 259 read_result = 0; // Force continuation. |
241 | 260 |
242 if (output) { | 261 if (output) { |
243 if (fwrite(samples, 1, size_out, output) != | 262 if (fwrite(samples, 1, size_out, output) != |
244 static_cast<size_t>(size_out)) { | 263 static_cast<size_t>(size_out)) { |
245 std::cerr << "could not write data after " << size_out; | 264 std::cerr << "could not write data after " << size_out; |
246 return 1; | 265 return 1; |
247 } | 266 } |
248 } | 267 } |
| 268 if (hash) { |
| 269 hash_value = hash_djb2(reinterpret_cast<const uint8*>(samples), |
| 270 size_out, hash_value); |
| 271 } |
249 } | 272 } |
250 } else if (target_codec == CODEC_TYPE_VIDEO) { | 273 } else if (target_codec == CODEC_TYPE_VIDEO) { |
251 int got_picture = 0; | 274 int got_picture = 0; |
252 result = avcodec_decode_video2(codec_context, frame, &got_picture, | 275 result = avcodec_decode_video2(codec_context, frame, &got_picture, |
253 &packet); | 276 &packet); |
254 if (got_picture) { | 277 if (got_picture) { |
255 ++frames; | 278 ++frames; |
256 read_result = 0; // Force continuation. | 279 read_result = 0; // Force continuation. |
257 | 280 |
258 // TODO(fbarchard): support formats other than YV12. | 281 if (output || hash) { |
259 if (output) { | |
260 for (int plane = 0; plane < 3; ++plane) { | 282 for (int plane = 0; plane < 3; ++plane) { |
261 const uint8* source = frame->data[plane]; | 283 const uint8* source = frame->data[plane]; |
262 const size_t source_stride = frame->linesize[plane]; | 284 const size_t source_stride = frame->linesize[plane]; |
263 size_t bytes_per_line = codec_context->width; | 285 size_t bytes_per_line = codec_context->width; |
264 size_t copy_lines = codec_context->height; | 286 size_t copy_lines = codec_context->height; |
265 if (plane != 0) { | 287 if (plane != 0) { |
266 switch (codec_context->pix_fmt) { | 288 switch (codec_context->pix_fmt) { |
267 case PIX_FMT_YUV420P: | 289 case PIX_FMT_YUV420P: |
268 case PIX_FMT_YUVJ420P: | 290 case PIX_FMT_YUVJ420P: |
269 bytes_per_line /= 2; | 291 bytes_per_line /= 2; |
270 copy_lines = (copy_lines + 1) / 2; | 292 copy_lines = (copy_lines + 1) / 2; |
271 break; | 293 break; |
272 case PIX_FMT_YUV422P: | 294 case PIX_FMT_YUV422P: |
273 case PIX_FMT_YUVJ422P: | 295 case PIX_FMT_YUVJ422P: |
274 bytes_per_line /= 2; | 296 bytes_per_line /= 2; |
275 copy_lines = copy_lines; | |
276 break; | 297 break; |
277 case PIX_FMT_YUV444P: | 298 case PIX_FMT_YUV444P: |
278 case PIX_FMT_YUVJ444P: | 299 case PIX_FMT_YUVJ444P: |
279 copy_lines = copy_lines; | |
280 break; | 300 break; |
281 default: | 301 default: |
282 std::cerr << "unknown video format: " | 302 std::cerr << "unknown video format: " |
283 << codec_context->pix_fmt; | 303 << codec_context->pix_fmt; |
284 return 1; | 304 return 1; |
285 } | 305 } |
286 } | 306 } |
287 for (size_t i = 0; i < copy_lines; ++i) { | 307 if (output) { |
288 if (fwrite(source, 1, bytes_per_line, output) != | 308 for (size_t i = 0; i < copy_lines; ++i) { |
289 bytes_per_line) { | 309 if (fwrite(source, 1, bytes_per_line, output) != |
290 std::cerr << "could not write data after " | 310 bytes_per_line) { |
291 << bytes_per_line; | 311 std::cerr << "could not write data after " |
292 return 1; | 312 << bytes_per_line; |
| 313 return 1; |
| 314 } |
| 315 source += source_stride; |
293 } | 316 } |
294 source += source_stride; | 317 } |
| 318 if (hash) { |
| 319 for (size_t i = 0; i < copy_lines; ++i) { |
| 320 hash_value = hash_djb2(source, bytes_per_line, hash_value); |
| 321 source += source_stride; |
| 322 } |
295 } | 323 } |
296 } | 324 } |
297 } | 325 } |
298 } | 326 } |
299 } else { | 327 } else { |
300 NOTREACHED(); | 328 NOTREACHED(); |
301 } | 329 } |
302 base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start; | 330 base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start; |
303 | 331 |
304 decode_times.push_back(delta.InMillisecondsF()); | 332 decode_times.push_back(delta.InMillisecondsF()); |
(...skipping 21 matching lines...) Expand all Loading... |
326 // Print our results. | 354 // Print our results. |
327 std::cout.setf(std::ios::fixed); | 355 std::cout.setf(std::ios::fixed); |
328 std::cout.precision(2); | 356 std::cout.precision(2); |
329 std::cout << std::endl; | 357 std::cout << std::endl; |
330 std::cout << " Frames:" << std::setw(10) << frames | 358 std::cout << " Frames:" << std::setw(10) << frames |
331 << std::endl; | 359 << std::endl; |
332 std::cout << " Total:" << std::setw(10) << total.InMillisecondsF() | 360 std::cout << " Total:" << std::setw(10) << total.InMillisecondsF() |
333 << " ms" << std::endl; | 361 << " ms" << std::endl; |
334 std::cout << " Summation:" << std::setw(10) << sum | 362 std::cout << " Summation:" << std::setw(10) << sum |
335 << " ms" << std::endl; | 363 << " ms" << std::endl; |
| 364 if (hash) { |
| 365 std::cout << " Hash:" << std::setw(10) << hash_value |
| 366 << std::endl; |
| 367 } |
336 | 368 |
337 if (frames > 0u) { | 369 if (frames > 0u) { |
338 // Calculate the average time per frame. | 370 // Calculate the average time per frame. |
339 double average = sum / frames; | 371 double average = sum / frames; |
340 | 372 |
341 // Calculate the sum of the squared differences. | 373 // Calculate the sum of the squared differences. |
342 // Standard deviation will only be accurate if no threads are used. | 374 // Standard deviation will only be accurate if no threads are used. |
343 // TODO(fbarchard): Rethink standard deviation calculation. | 375 // TODO(fbarchard): Rethink standard deviation calculation. |
344 double squared_sum = 0; | 376 double squared_sum = 0; |
345 for (size_t i = 0; i < frames; ++i) { | 377 for (size_t i = 0; i < frames; ++i) { |
346 double difference = decode_times[i] - average; | 378 double difference = decode_times[i] - average; |
347 squared_sum += difference * difference; | 379 squared_sum += difference * difference; |
348 } | 380 } |
349 | 381 |
350 // Calculate the standard deviation (jitter). | 382 // Calculate the standard deviation (jitter). |
351 double stddev = sqrt(squared_sum / frames); | 383 double stddev = sqrt(squared_sum / frames); |
352 | 384 |
353 std::cout << " Average:" << std::setw(10) << average | 385 std::cout << " Average:" << std::setw(10) << average |
354 << " ms" << std::endl; | 386 << " ms" << std::endl; |
355 std::cout << " StdDev:" << std::setw(10) << stddev | 387 std::cout << " StdDev:" << std::setw(10) << stddev |
356 << " ms" << std::endl; | 388 << " ms" << std::endl; |
357 } | 389 } |
358 return 0; | 390 return 0; |
359 } | 391 } |
OLD | NEW |