| 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 #include "build/build_config.h" |
| 11 |
| 12 // For pipe _setmode to binary |
| 13 #if defined(OS_WIN) |
| 14 #include <fcntl.h> |
| 15 #include <io.h> |
| 16 #endif |
| 11 | 17 |
| 12 #include <iomanip> | 18 #include <iomanip> |
| 13 #include <iostream> | 19 #include <iostream> |
| 14 #include <string> | 20 #include <string> |
| 15 | 21 |
| 16 #include "base/at_exit.h" | 22 #include "base/at_exit.h" |
| 17 #include "base/basictypes.h" | 23 #include "base/basictypes.h" |
| 18 #include "base/command_line.h" | 24 #include "base/command_line.h" |
| 19 #include "base/file_path.h" | 25 #include "base/file_path.h" |
| 20 #include "base/file_util.h" | 26 #include "base/file_util.h" |
| 21 #include "base/logging.h" | 27 #include "base/md5.h" |
| 22 #include "base/string_util.h" | 28 #include "base/string_util.h" |
| 23 #include "base/time.h" | 29 #include "base/time.h" |
| 24 #include "media/base/media.h" | 30 #include "media/base/media.h" |
| 25 #include "media/bench/file_protocol.h" | 31 #include "media/bench/file_protocol.h" |
| 26 #include "media/filters/ffmpeg_common.h" | 32 #include "media/filters/ffmpeg_common.h" |
| 27 #include "media/filters/ffmpeg_video_decoder.h" | 33 #include "media/filters/ffmpeg_video_decoder.h" |
| 28 | 34 |
| 29 namespace switches { | 35 namespace switches { |
| 30 const wchar_t kStream[] = L"stream"; | 36 const wchar_t kStream[] = L"stream"; |
| 31 const wchar_t kVideoThreads[] = L"video-threads"; | 37 const wchar_t kVideoThreads[] = L"video-threads"; |
| 32 const wchar_t kFast2[] = L"fast2"; | 38 const wchar_t kFast2[] = L"fast2"; |
| 33 const wchar_t kSkip[] = L"skip"; | 39 const wchar_t kSkip[] = L"skip"; |
| 34 const wchar_t kFlush[] = L"flush"; | 40 const wchar_t kFlush[] = L"flush"; |
| 35 const wchar_t kHash[] = L"hash"; | 41 const wchar_t kDjb2[] = L"djb2"; |
| 42 const wchar_t kMd5[] = L"md5"; |
| 43 const wchar_t kFrames[] = L"frames"; |
| 36 } // namespace switches | 44 } // namespace switches |
| 37 | 45 |
| 38 namespace { | 46 namespace { |
| 39 // DJB2 hash | 47 // DJB2 hash |
| 40 unsigned int hash_djb2(const uint8* s, | 48 unsigned int DJB2Hash(const uint8* s, |
| 41 size_t len, unsigned int hash) { | 49 size_t len, unsigned int hash) { |
| 42 while (len--) | 50 if (len > 0) { |
| 43 hash = hash * 33 + *s++; | 51 do { |
| 52 hash = hash * 33 + *s++; |
| 53 } while (--len); |
| 54 } |
| 44 return hash; | 55 return hash; |
| 45 } | 56 } |
| 46 } | 57 } |
| 47 | 58 |
| 59 #if defined(OS_WIN) |
| 60 // warning: disable warning about exception handler. |
| 61 #pragma warning(disable:4509) |
| 62 |
| 63 // Thread priorities to make benchmark more stable. |
| 64 |
| 65 void EnterTimingSection() { |
| 66 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); |
| 67 } |
| 68 |
| 69 void LeaveTimingSection() { |
| 70 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); |
| 71 } |
| 72 #else |
| 73 void EnterTimingSection() { |
| 74 pthread_attr_t pta; |
| 75 struct sched_param param; |
| 76 |
| 77 pthread_attr_init(&pta); |
| 78 memset(¶m, 0, sizeof(param)); |
| 79 param.sched_priority = 78; |
| 80 pthread_attr_setschedparam(&pta, ¶m); |
| 81 pthread_attr_destroy(&pta); |
| 82 } |
| 83 |
| 84 void LeaveTimingSection() { |
| 85 } |
| 86 #endif |
| 87 |
| 48 int main(int argc, const char** argv) { | 88 int main(int argc, const char** argv) { |
| 49 base::AtExitManager exit_manager; | 89 base::AtExitManager exit_manager; |
| 50 | 90 |
| 51 CommandLine::Init(argc, argv); | 91 CommandLine::Init(argc, argv); |
| 52 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 92 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 53 | 93 |
| 54 std::vector<std::wstring> filenames(cmd_line->GetLooseValues()); | 94 std::vector<std::wstring> filenames(cmd_line->GetLooseValues()); |
| 55 if (filenames.empty()) { | 95 if (filenames.empty()) { |
| 56 std::cerr << "Usage: media_bench [OPTIONS] FILE [DUMPFILE]\n" | 96 std::cerr << "Usage: media_bench [OPTIONS] FILE [DUMPFILE]\n" |
| 57 << " --stream=[audio|video] " | 97 << " --stream=[audio|video] " |
| 58 << "Benchmark either the audio or video stream\n" | 98 << "Benchmark either the audio or video stream\n" |
| 59 << " --video-threads=N " | 99 << " --video-threads=N " |
| 60 << "Decode video using N threads\n" | 100 << "Decode video using N threads\n" |
| 101 << " --frames=N " |
| 102 << "Decode N frames\n" |
| 61 << " --fast2 " | 103 << " --fast2 " |
| 62 << "Enable fast2 flag\n" | 104 << "Enable fast2 flag\n" |
| 63 << " --flush " | 105 << " --flush " |
| 64 << "Flush last frame\n" | 106 << "Flush last frame\n" |
| 65 << " --hash " | 107 << " --djb2 " |
| 66 << "Hash decoded buffers\n" | 108 << "Hash decoded buffers (DJB2)\n" |
| 109 << " --md5 " |
| 110 << "Hash decoded buffers (MD5)\n" |
| 67 << " --skip=[1|2|3] " | 111 << " --skip=[1|2|3] " |
| 68 << "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl; | 112 << "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl; |
| 69 return 1; | 113 return 1; |
| 70 } | 114 } |
| 71 | 115 |
| 72 // Initialize our media library (try loading DLLs, etc.) before continuing. | 116 // Initialize our media library (try loading DLLs, etc.) before continuing. |
| 73 // We use an empty file path as the parameter to force searching of the | 117 // We use an empty file path as the parameter to force searching of the |
| 74 // default locations for necessary DLLs and DSOs. | 118 // default locations for necessary DLLs and DSOs. |
| 75 if (media::InitializeMediaLibrary(FilePath()) == false) { | 119 if (media::InitializeMediaLibrary(FilePath()) == false) { |
| 76 std::cerr << "Unable to initialize the media library."; | 120 std::cerr << "Unable to initialize the media library."; |
| 77 return 1; | 121 return 1; |
| 78 } | 122 } |
| 79 | 123 |
| 80 // Retrieve command line options. | 124 // Retrieve command line options. |
| 81 std::string in_path(WideToUTF8(filenames[0])); | 125 std::string in_path(WideToUTF8(filenames[0])); |
| 82 std::string out_path; | 126 std::string out_path; |
| 83 if (filenames.size() > 1) { | 127 if (filenames.size() > 1) { |
| 84 out_path = WideToUTF8(filenames[1]); | 128 out_path = WideToUTF8(filenames[1]); |
| 85 } | 129 } |
| 86 CodecType target_codec = CODEC_TYPE_UNKNOWN; | 130 CodecType target_codec = CODEC_TYPE_UNKNOWN; |
| 87 int video_threads = 0; | |
| 88 | 131 |
| 89 // Determine whether to benchmark audio or video decoding. | 132 // Determine whether to benchmark audio or video decoding. |
| 90 std::wstring stream(cmd_line->GetSwitchValue(switches::kStream)); | 133 std::wstring stream(cmd_line->GetSwitchValue(switches::kStream)); |
| 91 if (!stream.empty()) { | 134 if (!stream.empty()) { |
| 92 if (stream.compare(L"audio") == 0) { | 135 if (stream.compare(L"audio") == 0) { |
| 93 target_codec = CODEC_TYPE_AUDIO; | 136 target_codec = CODEC_TYPE_AUDIO; |
| 94 } else if (stream.compare(L"video") == 0) { | 137 } else if (stream.compare(L"video") == 0) { |
| 95 target_codec = CODEC_TYPE_VIDEO; | 138 target_codec = CODEC_TYPE_VIDEO; |
| 96 } else { | 139 } else { |
| 97 std::cerr << "Unknown --stream option " << stream << std::endl; | 140 std::cerr << "Unknown --stream option " << stream << std::endl; |
| 98 return 1; | 141 return 1; |
| 99 } | 142 } |
| 100 } | 143 } |
| 101 | 144 |
| 102 // Determine number of threads to use for video decoding (optional). | 145 // Determine number of threads to use for video decoding (optional). |
| 146 int video_threads = 0; |
| 103 std::wstring threads(cmd_line->GetSwitchValue(switches::kVideoThreads)); | 147 std::wstring threads(cmd_line->GetSwitchValue(switches::kVideoThreads)); |
| 104 if (!threads.empty() && | 148 if (!threads.empty() && |
| 105 !StringToInt(WideToUTF16Hack(threads), &video_threads)) { | 149 !StringToInt(WideToUTF16Hack(threads), &video_threads)) { |
| 106 video_threads = 0; | 150 video_threads = 0; |
| 107 } | 151 } |
| 108 | 152 |
| 153 // Determine number of frames to decode (optional). |
| 154 int max_frames = 0; |
| 155 std::wstring frames_opt(cmd_line->GetSwitchValue(switches::kFrames)); |
| 156 if (!frames_opt.empty() && |
| 157 !StringToInt(WideToUTF16Hack(frames_opt), &max_frames)) { |
| 158 max_frames = 0; |
| 159 } |
| 160 |
| 109 bool fast2 = false; | 161 bool fast2 = false; |
| 110 if (cmd_line->HasSwitch(switches::kFast2)) { | 162 if (cmd_line->HasSwitch(switches::kFast2)) { |
| 111 fast2 = true; | 163 fast2 = true; |
| 112 } | 164 } |
| 113 | 165 |
| 114 bool flush = false; | 166 bool flush = false; |
| 115 if (cmd_line->HasSwitch(switches::kFlush)) { | 167 if (cmd_line->HasSwitch(switches::kFlush)) { |
| 116 flush = true; | 168 flush = true; |
| 117 } | 169 } |
| 118 | 170 |
| 119 unsigned int hash_value = 5381u; // Seed for DJB2. | 171 unsigned int hash_value = 5381u; // Seed for DJB2. |
| 120 bool hash = false; | 172 bool hash_djb2 = false; |
| 121 if (cmd_line->HasSwitch(switches::kHash)) { | 173 if (cmd_line->HasSwitch(switches::kDjb2)) { |
| 122 hash = true; | 174 hash_djb2 = true; |
| 175 } |
| 176 |
| 177 MD5Context ctx; // intermediate MD5 data: do not use |
| 178 MD5Init(&ctx); |
| 179 bool hash_md5 = false; |
| 180 if (cmd_line->HasSwitch(switches::kMd5)) { |
| 181 hash_md5 = true; |
| 123 } | 182 } |
| 124 | 183 |
| 125 int skip = 0; | 184 int skip = 0; |
| 126 if (cmd_line->HasSwitch(switches::kSkip)) { | 185 if (cmd_line->HasSwitch(switches::kSkip)) { |
| 127 std::wstring skip_opt(cmd_line->GetSwitchValue(switches::kSkip)); | 186 std::wstring skip_opt(cmd_line->GetSwitchValue(switches::kSkip)); |
| 128 if (!StringToInt(WideToUTF16Hack(skip_opt), &skip)) { | 187 if (!StringToInt(WideToUTF16Hack(skip_opt), &skip)) { |
| 129 skip = 0; | 188 skip = 0; |
| 130 } | 189 } |
| 131 } | 190 } |
| 132 | 191 |
| 192 std::ostream* log_out = &std::cout; |
| 193 #if defined(OS_WIN) |
| 194 // Catch exceptions so this tool can be used in automated testing. |
| 195 __try { |
| 196 #endif |
| 197 |
| 133 // Register FFmpeg and attempt to open file. | 198 // Register FFmpeg and attempt to open file. |
| 134 avcodec_init(); | 199 avcodec_init(); |
| 135 av_register_all(); | 200 av_register_all(); |
| 136 av_register_protocol(&kFFmpegFileProtocol); | 201 av_register_protocol(&kFFmpegFileProtocol); |
| 137 AVFormatContext* format_context = NULL; | 202 AVFormatContext* format_context = NULL; |
| 138 if (av_open_input_file(&format_context, in_path.c_str(), NULL, 0, NULL) < 0) { | 203 if (av_open_input_file(&format_context, in_path.c_str(), NULL, 0, NULL) < 0) { |
| 139 std::cerr << "Could not open " << in_path << std::endl; | 204 std::cerr << "Error: Could not open input for " |
| 205 << in_path << std::endl; |
| 140 return 1; | 206 return 1; |
| 141 } | 207 } |
| 142 | 208 |
| 143 // Open output file. | 209 // Open output file. |
| 144 FILE *output = NULL; | 210 FILE *output = NULL; |
| 145 if (!out_path.empty()) { | 211 if (!out_path.empty()) { |
| 146 output = file_util::OpenFile(out_path.c_str(), "wb"); | 212 // TODO(fbarchard): Add pipe:1 for piping to stderr. |
| 213 if (!strncmp(out_path.c_str(), "pipe:", 5) || |
| 214 !strcmp(out_path.c_str(), "-")) { |
| 215 output = stdout; |
| 216 log_out = &std::cerr; |
| 217 #if defined(OS_WIN) |
| 218 _setmode(_fileno(stdout),_O_BINARY); |
| 219 #endif |
| 220 } else { |
| 221 output = file_util::OpenFile(out_path.c_str(), "wb"); |
| 222 } |
| 147 if (!output) { | 223 if (!output) { |
| 148 LOG(ERROR) << "could not open output"; | 224 std::cerr << "Error: Could not open output " |
| 225 << out_path << std::endl; |
| 149 return 1; | 226 return 1; |
| 150 } | 227 } |
| 151 } | 228 } |
| 152 | 229 |
| 153 // Parse a little bit of the stream to fill out the format context. | 230 // Parse a little bit of the stream to fill out the format context. |
| 154 if (av_find_stream_info(format_context) < 0) { | 231 if (av_find_stream_info(format_context) < 0) { |
| 155 std::cerr << "Could not find stream info for " << in_path << std::endl; | 232 std::cerr << "Error: Could not find stream info for " |
| 233 << in_path << std::endl; |
| 156 return 1; | 234 return 1; |
| 157 } | 235 } |
| 158 | 236 |
| 159 // Find our target stream. | 237 // Find our target stream. |
| 160 int target_stream = -1; | 238 int target_stream = -1; |
| 161 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 239 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 162 AVCodecContext* codec_context = format_context->streams[i]->codec; | 240 AVCodecContext* codec_context = format_context->streams[i]->codec; |
| 163 AVCodec* codec = avcodec_find_decoder(codec_context->codec_id); | 241 AVCodec* codec = avcodec_find_decoder(codec_context->codec_id); |
| 164 | 242 |
| 165 // See if we found our target codec. | 243 // See if we found our target codec. |
| 166 if (codec_context->codec_type == target_codec && target_stream < 0) { | 244 if (codec_context->codec_type == target_codec && target_stream < 0) { |
| 167 std::cout << "* "; | 245 *log_out << "* "; |
| 168 target_stream = i; | 246 target_stream = i; |
| 169 } else { | 247 } else { |
| 170 std::cout << " "; | 248 *log_out << " "; |
| 171 } | 249 } |
| 172 | 250 |
| 173 if (codec_context->codec_type == CODEC_TYPE_UNKNOWN) { | 251 if (!codec || (codec_context->codec_type == CODEC_TYPE_UNKNOWN)) { |
| 174 std::cout << "Stream #" << i << ": Unknown" << std::endl; | 252 *log_out << "Stream #" << i << ": Unknown" << std::endl; |
| 175 } else { | 253 } else { |
| 176 // Print out stream information | 254 // Print out stream information |
| 177 std::cout << "Stream #" << i << ": " << codec->name << " (" | 255 *log_out << "Stream #" << i << ": " << codec->name << " (" |
| 178 << codec->long_name << ")" << std::endl; | 256 << codec->long_name << ")" << std::endl; |
| 179 } | 257 } |
| 180 } | 258 } |
| 181 | 259 |
| 182 // Only continue if we found our target stream. | 260 // Only continue if we found our target stream. |
| 183 if (target_stream < 0) { | 261 if (target_stream < 0) { |
| 262 std::cerr << "Error: Could not find target stream " |
| 263 << target_stream << " for " << in_path << std::endl; |
| 184 return 1; | 264 return 1; |
| 185 } | 265 } |
| 186 | 266 |
| 187 // Prepare FFmpeg structures. | 267 // Prepare FFmpeg structures. |
| 188 AVPacket packet; | 268 AVPacket packet; |
| 189 AVCodecContext* codec_context = format_context->streams[target_stream]->codec; | 269 AVCodecContext* codec_context = format_context->streams[target_stream]->codec; |
| 190 AVCodec* codec = avcodec_find_decoder(codec_context->codec_id); | 270 AVCodec* codec = avcodec_find_decoder(codec_context->codec_id); |
| 191 | 271 |
| 272 // Only continue if we found our codec. |
| 273 if (!codec) { |
| 274 std::cerr << "Error: Could not find codec for " |
| 275 << in_path << std::endl; |
| 276 return 1; |
| 277 } |
| 278 |
| 192 if (skip == 1) { | 279 if (skip == 1) { |
| 193 codec_context->skip_loop_filter = AVDISCARD_NONREF; | 280 codec_context->skip_loop_filter = AVDISCARD_NONREF; |
| 194 } else if (skip == 2) { | 281 } else if (skip == 2) { |
| 195 codec_context->skip_loop_filter = AVDISCARD_ALL; | 282 codec_context->skip_loop_filter = AVDISCARD_ALL; |
| 196 } else if (skip == 3) { | 283 } else if (skip == 3) { |
| 197 codec_context->skip_loop_filter = AVDISCARD_ALL; | 284 codec_context->skip_loop_filter = AVDISCARD_ALL; |
| 198 codec_context->skip_frame = AVDISCARD_NONREF; | 285 codec_context->skip_frame = AVDISCARD_NONREF; |
| 199 } | 286 } |
| 200 if (fast2) { | 287 if (fast2) { |
| 201 codec_context->flags2 |= CODEC_FLAG2_FAST; | 288 codec_context->flags2 |= CODEC_FLAG2_FAST; |
| 202 } | 289 } |
| 203 | 290 |
| 204 // Initialize threaded decode. | 291 // Initialize threaded decode. |
| 205 if (target_codec == CODEC_TYPE_VIDEO && video_threads > 0) { | 292 if (target_codec == CODEC_TYPE_VIDEO && video_threads > 0) { |
| 206 if (avcodec_thread_init(codec_context, video_threads) < 0) { | 293 if (avcodec_thread_init(codec_context, video_threads) < 0) { |
| 207 std::cerr << "WARNING: Could not initialize threading!\n" | 294 std::cerr << "Warning: Could not initialize threading!\n" |
| 208 << "Did you build with pthread/w32thread support?" << std::endl; | 295 << "Did you build with pthread/w32thread support?" << std::endl; |
| 209 } | 296 } |
| 210 } | 297 } |
| 211 | 298 |
| 212 // Initialize our codec. | 299 // Initialize our codec. |
| 213 if (avcodec_open(codec_context, codec) < 0) { | 300 if (avcodec_open(codec_context, codec) < 0) { |
| 214 std::cerr << "Could not open codec " << codec_context->codec->name | 301 std::cerr << "Error: Could not open codec " |
| 215 << std::endl; | 302 << codec_context->codec->name << " for " |
| 303 << in_path << std::endl; |
| 216 return 1; | 304 return 1; |
| 217 } | 305 } |
| 218 | 306 |
| 219 // Buffer used for audio decoding. | 307 // Buffer used for audio decoding. |
| 220 int16* samples = | 308 int16* samples = |
| 221 reinterpret_cast<int16*>(av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)); | 309 reinterpret_cast<int16*>(av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)); |
| 222 | 310 |
| 223 // Buffer used for video decoding. | 311 // Buffer used for video decoding. |
| 224 AVFrame* frame = avcodec_alloc_frame(); | 312 AVFrame* frame = avcodec_alloc_frame(); |
| 225 if (!frame) { | 313 if (!frame) { |
| 226 std::cerr << "Could not allocate an AVFrame" << std::endl; | 314 std::cerr << "Error: avcodec_alloc_frame for " |
| 315 << in_path << std::endl; |
| 227 return 1; | 316 return 1; |
| 228 } | 317 } |
| 229 | 318 |
| 230 // Stats collector. | 319 // Stats collector. |
| 320 EnterTimingSection(); |
| 231 std::vector<double> decode_times; | 321 std::vector<double> decode_times; |
| 232 decode_times.reserve(4096); | 322 decode_times.reserve(4096); |
| 233 // Parse through the entire stream until we hit EOF. | 323 // Parse through the entire stream until we hit EOF. |
| 234 base::TimeTicks start = base::TimeTicks::HighResNow(); | 324 base::TimeTicks start = base::TimeTicks::HighResNow(); |
| 235 size_t frames = 0; | 325 int frames = 0; |
| 236 int read_result = 0; | 326 int read_result = 0; |
| 237 do { | 327 do { |
| 238 read_result = av_read_frame(format_context, &packet); | 328 read_result = av_read_frame(format_context, &packet); |
| 239 | 329 |
| 240 if (read_result < 0) { | 330 if (read_result < 0) { |
| 241 if (flush) { | 331 if (flush) { |
| 242 packet.stream_index = target_stream; | 332 packet.stream_index = target_stream; |
| 243 packet.size = 0; | 333 packet.size = 0; |
| 244 } else { | 334 } else { |
| 245 break; | 335 break; |
| 246 } | 336 } |
| 247 } | 337 } |
| 248 | 338 |
| 249 // Only decode packets from our target stream. | 339 // Only decode packets from our target stream. |
| 250 if (packet.stream_index == target_stream) { | 340 if (packet.stream_index == target_stream) { |
| 251 int result = -1; | 341 int result = -1; |
| 252 base::TimeTicks decode_start = base::TimeTicks::HighResNow(); | |
| 253 if (target_codec == CODEC_TYPE_AUDIO) { | 342 if (target_codec == CODEC_TYPE_AUDIO) { |
| 254 int size_out = AVCODEC_MAX_AUDIO_FRAME_SIZE; | 343 int size_out = AVCODEC_MAX_AUDIO_FRAME_SIZE; |
| 344 |
| 345 base::TimeTicks decode_start = base::TimeTicks::HighResNow(); |
| 255 result = avcodec_decode_audio3(codec_context, samples, &size_out, | 346 result = avcodec_decode_audio3(codec_context, samples, &size_out, |
| 256 &packet); | 347 &packet); |
| 348 base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start; |
| 349 |
| 257 if (size_out) { | 350 if (size_out) { |
| 351 decode_times.push_back(delta.InMillisecondsF()); |
| 258 ++frames; | 352 ++frames; |
| 259 read_result = 0; // Force continuation. | 353 read_result = 0; // Force continuation. |
| 260 | 354 |
| 261 if (output) { | 355 if (output) { |
| 262 if (fwrite(samples, 1, size_out, output) != | 356 if (fwrite(samples, 1, size_out, output) != |
| 263 static_cast<size_t>(size_out)) { | 357 static_cast<size_t>(size_out)) { |
| 264 std::cerr << "could not write data after " << size_out; | 358 std::cerr << "Error: Could not write " |
| 359 << size_out << " bytes for " << in_path << std::endl; |
| 265 return 1; | 360 return 1; |
| 266 } | 361 } |
| 267 } | 362 } |
| 268 if (hash) { | 363 if (hash_djb2) { |
| 269 hash_value = hash_djb2(reinterpret_cast<const uint8*>(samples), | 364 hash_value = DJB2Hash(reinterpret_cast<const uint8*>(samples), |
| 270 size_out, hash_value); | 365 size_out, hash_value); |
| 366 } |
| 367 if (hash_md5) { |
| 368 MD5Update(&ctx, reinterpret_cast<const uint8*>(samples), |
| 369 size_out); |
| 271 } | 370 } |
| 272 } | 371 } |
| 273 } else if (target_codec == CODEC_TYPE_VIDEO) { | 372 } else if (target_codec == CODEC_TYPE_VIDEO) { |
| 274 int got_picture = 0; | 373 int got_picture = 0; |
| 374 |
| 375 base::TimeTicks decode_start = base::TimeTicks::HighResNow(); |
| 275 result = avcodec_decode_video2(codec_context, frame, &got_picture, | 376 result = avcodec_decode_video2(codec_context, frame, &got_picture, |
| 276 &packet); | 377 &packet); |
| 378 base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start; |
| 379 |
| 277 if (got_picture) { | 380 if (got_picture) { |
| 381 decode_times.push_back(delta.InMillisecondsF()); |
| 278 ++frames; | 382 ++frames; |
| 279 read_result = 0; // Force continuation. | 383 read_result = 0; // Force continuation. |
| 280 | 384 |
| 281 if (output || hash) { | 385 for (int plane = 0; plane < 3; ++plane) { |
| 282 for (int plane = 0; plane < 3; ++plane) { | 386 const uint8* source = frame->data[plane]; |
| 283 const uint8* source = frame->data[plane]; | 387 const size_t source_stride = frame->linesize[plane]; |
| 284 const size_t source_stride = frame->linesize[plane]; | 388 size_t bytes_per_line = codec_context->width; |
| 285 size_t bytes_per_line = codec_context->width; | 389 size_t copy_lines = codec_context->height; |
| 286 size_t copy_lines = codec_context->height; | 390 if (plane != 0) { |
| 287 if (plane != 0) { | 391 switch (codec_context->pix_fmt) { |
| 288 switch (codec_context->pix_fmt) { | 392 case PIX_FMT_YUV420P: |
| 289 case PIX_FMT_YUV420P: | 393 case PIX_FMT_YUVJ420P: |
| 290 case PIX_FMT_YUVJ420P: | 394 bytes_per_line /= 2; |
| 291 bytes_per_line /= 2; | 395 copy_lines = (copy_lines + 1) / 2; |
| 292 copy_lines = (copy_lines + 1) / 2; | 396 break; |
| 293 break; | 397 case PIX_FMT_YUV422P: |
| 294 case PIX_FMT_YUV422P: | 398 case PIX_FMT_YUVJ422P: |
| 295 case PIX_FMT_YUVJ422P: | 399 bytes_per_line /= 2; |
| 296 bytes_per_line /= 2; | 400 break; |
| 297 break; | 401 case PIX_FMT_YUV444P: |
| 298 case PIX_FMT_YUV444P: | 402 case PIX_FMT_YUVJ444P: |
| 299 case PIX_FMT_YUVJ444P: | 403 break; |
| 300 break; | 404 default: |
| 301 default: | 405 std::cerr << "Error: Unknown video format " |
| 302 std::cerr << "unknown video format: " | 406 << codec_context->pix_fmt; |
| 303 << codec_context->pix_fmt; | 407 return 1; |
| 304 return 1; | 408 } |
| 409 } |
| 410 if (output) { |
| 411 for (size_t i = 0; i < copy_lines; ++i) { |
| 412 if (fwrite(source, 1, bytes_per_line, output) != |
| 413 bytes_per_line) { |
| 414 std::cerr << "Error: Could not write data after " |
| 415 << copy_lines << " lines for " |
| 416 << in_path << std::endl; |
| 417 return 1; |
| 305 } | 418 } |
| 419 source += source_stride; |
| 306 } | 420 } |
| 307 if (output) { | 421 } |
| 308 for (size_t i = 0; i < copy_lines; ++i) { | 422 if (hash_djb2) { |
| 309 if (fwrite(source, 1, bytes_per_line, output) != | 423 for (size_t i = 0; i < copy_lines; ++i) { |
| 310 bytes_per_line) { | 424 hash_value = DJB2Hash(source, bytes_per_line, hash_value); |
| 311 std::cerr << "could not write data after " | 425 source += source_stride; |
| 312 << bytes_per_line; | |
| 313 return 1; | |
| 314 } | |
| 315 source += source_stride; | |
| 316 } | |
| 317 } | 426 } |
| 318 if (hash) { | 427 } |
| 319 for (size_t i = 0; i < copy_lines; ++i) { | 428 if (hash_md5) { |
| 320 hash_value = hash_djb2(source, bytes_per_line, hash_value); | 429 for (size_t i = 0; i < copy_lines; ++i) { |
| 321 source += source_stride; | 430 MD5Update(&ctx, reinterpret_cast<const uint8*>(source), |
| 322 } | 431 bytes_per_line); |
| 432 source += source_stride; |
| 323 } | 433 } |
| 324 } | 434 } |
| 325 } | 435 } |
| 326 } | 436 } |
| 327 } else { | 437 } else { |
| 328 NOTREACHED(); | 438 NOTREACHED(); |
| 329 } | 439 } |
| 330 base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start; | |
| 331 | |
| 332 decode_times.push_back(delta.InMillisecondsF()); | |
| 333 | 440 |
| 334 // Make sure our decoding went OK. | 441 // Make sure our decoding went OK. |
| 335 if (result < 0) { | 442 if (result < 0) { |
| 336 std::cerr << "Error while decoding" << std::endl; | 443 std::cerr << "Error: avcodec_decode returned " |
| 444 << result << " for " << in_path << std::endl; |
| 337 return 1; | 445 return 1; |
| 338 } | 446 } |
| 339 } | 447 } |
| 340 // Free our packet. | 448 // Free our packet. |
| 341 av_free_packet(&packet); | 449 av_free_packet(&packet); |
| 450 |
| 451 if (max_frames && (frames >= max_frames)) |
| 452 break; |
| 342 } while (read_result >= 0); | 453 } while (read_result >= 0); |
| 343 base::TimeDelta total = base::TimeTicks::HighResNow() - start; | 454 base::TimeDelta total = base::TimeTicks::HighResNow() - start; |
| 344 | 455 LeaveTimingSection(); |
| 345 if (output) | 456 if (output) |
| 346 file_util::CloseFile(output); | 457 file_util::CloseFile(output); |
| 347 | 458 |
| 348 // Calculate the sum of times. Note that some of these may be zero. | 459 // Calculate the sum of times. Note that some of these may be zero. |
| 349 double sum = 0; | 460 double sum = 0; |
| 350 for (size_t i = 0; i < decode_times.size(); ++i) { | 461 for (size_t i = 0; i < decode_times.size(); ++i) { |
| 351 sum += decode_times[i]; | 462 sum += decode_times[i]; |
| 352 } | 463 } |
| 353 | 464 |
| 354 // Print our results. | 465 // Print our results. |
| 355 std::cout.setf(std::ios::fixed); | 466 log_out->setf(std::ios::fixed); |
| 356 std::cout.precision(2); | 467 log_out->precision(2); |
| 357 std::cout << std::endl; | 468 *log_out << std::endl; |
| 358 std::cout << " Frames:" << std::setw(10) << frames | 469 *log_out << " Frames:" << std::setw(11) << frames |
| 359 << std::endl; | 470 << std::endl; |
| 360 std::cout << " Total:" << std::setw(10) << total.InMillisecondsF() | 471 *log_out << " Total:" << std::setw(11) << total.InMillisecondsF() |
| 361 << " ms" << std::endl; | 472 << " ms" << std::endl; |
| 362 std::cout << " Summation:" << std::setw(10) << sum | 473 *log_out << " Summation:" << std::setw(11) << sum |
| 363 << " ms" << std::endl; | 474 << " ms" << std::endl; |
| 364 if (hash) { | |
| 365 std::cout << " Hash:" << std::setw(10) << hash_value | |
| 366 << std::endl; | |
| 367 } | |
| 368 | 475 |
| 369 if (frames > 0u) { | 476 if (frames > 0) { |
| 370 // Calculate the average time per frame. | 477 // Calculate the average time per frame. |
| 371 double average = sum / frames; | 478 double average = sum / frames; |
| 372 | 479 |
| 373 // Calculate the sum of the squared differences. | 480 // Calculate the sum of the squared differences. |
| 374 // Standard deviation will only be accurate if no threads are used. | 481 // Standard deviation will only be accurate if no threads are used. |
| 375 // TODO(fbarchard): Rethink standard deviation calculation. | 482 // TODO(fbarchard): Rethink standard deviation calculation. |
| 376 double squared_sum = 0; | 483 double squared_sum = 0; |
| 377 for (size_t i = 0; i < frames; ++i) { | 484 for (int i = 0; i < frames; ++i) { |
| 378 double difference = decode_times[i] - average; | 485 double difference = decode_times[i] - average; |
| 379 squared_sum += difference * difference; | 486 squared_sum += difference * difference; |
| 380 } | 487 } |
| 381 | 488 |
| 382 // Calculate the standard deviation (jitter). | 489 // Calculate the standard deviation (jitter). |
| 383 double stddev = sqrt(squared_sum / frames); | 490 double stddev = sqrt(squared_sum / frames); |
| 384 | 491 |
| 385 std::cout << " Average:" << std::setw(10) << average | 492 *log_out << " Average:" << std::setw(11) << average |
| 386 << " ms" << std::endl; | 493 << " ms" << std::endl; |
| 387 std::cout << " StdDev:" << std::setw(10) << stddev | 494 *log_out << " StdDev:" << std::setw(11) << stddev |
| 388 << " ms" << std::endl; | 495 << " ms" << std::endl; |
| 389 } | 496 } |
| 497 if (hash_djb2) { |
| 498 *log_out << " DJB2:" << std::setw(11) << hash_value |
| 499 << " " << in_path << std::endl; |
| 500 } |
| 501 if (hash_md5) { |
| 502 MD5Digest digest; // The result of the computation. |
| 503 MD5Final(&digest, &ctx); |
| 504 *log_out << " MD5: " << MD5DigestToBase16(digest) |
| 505 << " " << in_path << std::endl; |
| 506 } |
| 507 #if defined(OS_WIN) |
| 508 } __except(EXCEPTION_EXECUTE_HANDLER) { |
| 509 *log_out << " Exception:" << std::setw(11) << GetExceptionCode() |
| 510 << " " << in_path << std::endl; |
| 511 return 1; |
| 512 } |
| 513 #endif |
| 390 return 0; | 514 return 0; |
| 391 } | 515 } |
| OLD | NEW |