summaryrefslogtreecommitdiffstats
path: root/media/bench
diff options
context:
space:
mode:
authorfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-12 02:19:32 +0000
committerfbarchard@chromium.org <fbarchard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-12 02:19:32 +0000
commiteb268a94c2f15e9fb647f89dad2e312546e57095 (patch)
treee8a05d094ab649c1f0d055b42b44e97425eb85d0 /media/bench
parentd4b8eeef387561e1bc498e0cd632ba145637f7ed (diff)
downloadchromium_src-eb268a94c2f15e9fb647f89dad2e312546e57095.zip
chromium_src-eb268a94c2f15e9fb647f89dad2e312546e57095.tar.gz
chromium_src-eb268a94c2f15e9fb647f89dad2e312546e57095.tar.bz2
Media Bench file IO redux to address mp4 parsing issue.
BUG=21322 TEST=run media_bench on regression corpus. Old version crashes/fails. New version should report errors and/or work. Review URL: http://codereview.chromium.org/199049 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26072 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/bench')
-rw-r--r--media/bench/bench.cc320
-rw-r--r--media/bench/file_protocol.cc59
2 files changed, 262 insertions, 117 deletions
diff --git a/media/bench/bench.cc b/media/bench/bench.cc
index 717af42..8cb02f8 100644
--- a/media/bench/bench.cc
+++ b/media/bench/bench.cc
@@ -6,8 +6,14 @@
// measure decoding performance between different FFmpeg compile and run-time
// options. We also use this tool to measure performance regressions when
// testing newer builds of FFmpeg from trunk.
-//
-// This tool requires FFMPeg DLL's built with --enable-protocol=file.
+
+#include "build/build_config.h"
+
+// For pipe _setmode to binary
+#if defined(OS_WIN)
+#include <fcntl.h>
+#include <io.h>
+#endif
#include <iomanip>
#include <iostream>
@@ -18,7 +24,7 @@
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
-#include "base/logging.h"
+#include "base/md5.h"
#include "base/string_util.h"
#include "base/time.h"
#include "media/base/media.h"
@@ -32,19 +38,53 @@ const wchar_t kVideoThreads[] = L"video-threads";
const wchar_t kFast2[] = L"fast2";
const wchar_t kSkip[] = L"skip";
const wchar_t kFlush[] = L"flush";
-const wchar_t kHash[] = L"hash";
+const wchar_t kDjb2[] = L"djb2";
+const wchar_t kMd5[] = L"md5";
+const wchar_t kFrames[] = L"frames";
} // namespace switches
namespace {
// DJB2 hash
-unsigned int hash_djb2(const uint8* s,
- size_t len, unsigned int hash) {
- while (len--)
- hash = hash * 33 + *s++;
+unsigned int DJB2Hash(const uint8* s,
+ size_t len, unsigned int hash) {
+ if (len > 0) {
+ do {
+ hash = hash * 33 + *s++;
+ } while (--len);
+ }
return hash;
}
}
+#if defined(OS_WIN)
+// warning: disable warning about exception handler.
+#pragma warning(disable:4509)
+
+// Thread priorities to make benchmark more stable.
+
+void EnterTimingSection() {
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+}
+
+void LeaveTimingSection() {
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+}
+#else
+void EnterTimingSection() {
+ pthread_attr_t pta;
+ struct sched_param param;
+
+ pthread_attr_init(&pta);
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = 78;
+ pthread_attr_setschedparam(&pta, &param);
+ pthread_attr_destroy(&pta);
+}
+
+void LeaveTimingSection() {
+}
+#endif
+
int main(int argc, const char** argv) {
base::AtExitManager exit_manager;
@@ -58,12 +98,16 @@ int main(int argc, const char** argv) {
<< "Benchmark either the audio or video stream\n"
<< " --video-threads=N "
<< "Decode video using N threads\n"
+ << " --frames=N "
+ << "Decode N frames\n"
<< " --fast2 "
<< "Enable fast2 flag\n"
<< " --flush "
<< "Flush last frame\n"
- << " --hash "
- << "Hash decoded buffers\n"
+ << " --djb2 "
+ << "Hash decoded buffers (DJB2)\n"
+ << " --md5 "
+ << "Hash decoded buffers (MD5)\n"
<< " --skip=[1|2|3] "
<< "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl;
return 1;
@@ -84,7 +128,6 @@ int main(int argc, const char** argv) {
out_path = WideToUTF8(filenames[1]);
}
CodecType target_codec = CODEC_TYPE_UNKNOWN;
- int video_threads = 0;
// Determine whether to benchmark audio or video decoding.
std::wstring stream(cmd_line->GetSwitchValue(switches::kStream));
@@ -100,12 +143,21 @@ int main(int argc, const char** argv) {
}
// Determine number of threads to use for video decoding (optional).
+ int video_threads = 0;
std::wstring threads(cmd_line->GetSwitchValue(switches::kVideoThreads));
if (!threads.empty() &&
!StringToInt(WideToUTF16Hack(threads), &video_threads)) {
video_threads = 0;
}
+ // Determine number of frames to decode (optional).
+ int max_frames = 0;
+ std::wstring frames_opt(cmd_line->GetSwitchValue(switches::kFrames));
+ if (!frames_opt.empty() &&
+ !StringToInt(WideToUTF16Hack(frames_opt), &max_frames)) {
+ max_frames = 0;
+ }
+
bool fast2 = false;
if (cmd_line->HasSwitch(switches::kFast2)) {
fast2 = true;
@@ -117,9 +169,16 @@ int main(int argc, const char** argv) {
}
unsigned int hash_value = 5381u; // Seed for DJB2.
- bool hash = false;
- if (cmd_line->HasSwitch(switches::kHash)) {
- hash = true;
+ bool hash_djb2 = false;
+ if (cmd_line->HasSwitch(switches::kDjb2)) {
+ hash_djb2 = true;
+ }
+
+ MD5Context ctx; // intermediate MD5 data: do not use
+ MD5Init(&ctx);
+ bool hash_md5 = false;
+ if (cmd_line->HasSwitch(switches::kMd5)) {
+ hash_md5 = true;
}
int skip = 0;
@@ -130,29 +189,48 @@ int main(int argc, const char** argv) {
}
}
+ std::ostream* log_out = &std::cout;
+#if defined(OS_WIN)
+ // Catch exceptions so this tool can be used in automated testing.
+ __try {
+#endif
+
// Register FFmpeg and attempt to open file.
avcodec_init();
av_register_all();
av_register_protocol(&kFFmpegFileProtocol);
AVFormatContext* format_context = NULL;
if (av_open_input_file(&format_context, in_path.c_str(), NULL, 0, NULL) < 0) {
- std::cerr << "Could not open " << in_path << std::endl;
+ std::cerr << "Error: Could not open input for "
+ << in_path << std::endl;
return 1;
}
// Open output file.
FILE *output = NULL;
if (!out_path.empty()) {
- output = file_util::OpenFile(out_path.c_str(), "wb");
+ // TODO(fbarchard): Add pipe:1 for piping to stderr.
+ if (!strncmp(out_path.c_str(), "pipe:", 5) ||
+ !strcmp(out_path.c_str(), "-")) {
+ output = stdout;
+ log_out = &std::cerr;
+#if defined(OS_WIN)
+ _setmode(_fileno(stdout),_O_BINARY);
+#endif
+ } else {
+ output = file_util::OpenFile(out_path.c_str(), "wb");
+ }
if (!output) {
- LOG(ERROR) << "could not open output";
+ std::cerr << "Error: Could not open output "
+ << out_path << std::endl;
return 1;
}
}
// Parse a little bit of the stream to fill out the format context.
if (av_find_stream_info(format_context) < 0) {
- std::cerr << "Could not find stream info for " << in_path << std::endl;
+ std::cerr << "Error: Could not find stream info for "
+ << in_path << std::endl;
return 1;
}
@@ -164,23 +242,25 @@ int main(int argc, const char** argv) {
// See if we found our target codec.
if (codec_context->codec_type == target_codec && target_stream < 0) {
- std::cout << "* ";
+ *log_out << "* ";
target_stream = i;
} else {
- std::cout << " ";
+ *log_out << " ";
}
- if (codec_context->codec_type == CODEC_TYPE_UNKNOWN) {
- std::cout << "Stream #" << i << ": Unknown" << std::endl;
+ if (!codec || (codec_context->codec_type == CODEC_TYPE_UNKNOWN)) {
+ *log_out << "Stream #" << i << ": Unknown" << std::endl;
} else {
// Print out stream information
- std::cout << "Stream #" << i << ": " << codec->name << " ("
- << codec->long_name << ")" << std::endl;
+ *log_out << "Stream #" << i << ": " << codec->name << " ("
+ << codec->long_name << ")" << std::endl;
}
}
// Only continue if we found our target stream.
if (target_stream < 0) {
+ std::cerr << "Error: Could not find target stream "
+ << target_stream << " for " << in_path << std::endl;
return 1;
}
@@ -189,6 +269,13 @@ int main(int argc, const char** argv) {
AVCodecContext* codec_context = format_context->streams[target_stream]->codec;
AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
+ // Only continue if we found our codec.
+ if (!codec) {
+ std::cerr << "Error: Could not find codec for "
+ << in_path << std::endl;
+ return 1;
+ }
+
if (skip == 1) {
codec_context->skip_loop_filter = AVDISCARD_NONREF;
} else if (skip == 2) {
@@ -204,15 +291,16 @@ int main(int argc, const char** argv) {
// Initialize threaded decode.
if (target_codec == CODEC_TYPE_VIDEO && video_threads > 0) {
if (avcodec_thread_init(codec_context, video_threads) < 0) {
- std::cerr << "WARNING: Could not initialize threading!\n"
+ std::cerr << "Warning: Could not initialize threading!\n"
<< "Did you build with pthread/w32thread support?" << std::endl;
}
}
// Initialize our codec.
if (avcodec_open(codec_context, codec) < 0) {
- std::cerr << "Could not open codec " << codec_context->codec->name
- << std::endl;
+ std::cerr << "Error: Could not open codec "
+ << codec_context->codec->name << " for "
+ << in_path << std::endl;
return 1;
}
@@ -223,16 +311,18 @@ int main(int argc, const char** argv) {
// Buffer used for video decoding.
AVFrame* frame = avcodec_alloc_frame();
if (!frame) {
- std::cerr << "Could not allocate an AVFrame" << std::endl;
+ std::cerr << "Error: avcodec_alloc_frame for "
+ << in_path << std::endl;
return 1;
}
// Stats collector.
+ EnterTimingSection();
std::vector<double> decode_times;
decode_times.reserve(4096);
// Parse through the entire stream until we hit EOF.
base::TimeTicks start = base::TimeTicks::HighResNow();
- size_t frames = 0;
+ int frames = 0;
int read_result = 0;
do {
read_result = av_read_frame(format_context, &packet);
@@ -249,77 +339,97 @@ int main(int argc, const char** argv) {
// Only decode packets from our target stream.
if (packet.stream_index == target_stream) {
int result = -1;
- base::TimeTicks decode_start = base::TimeTicks::HighResNow();
if (target_codec == CODEC_TYPE_AUDIO) {
int size_out = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+
+ base::TimeTicks decode_start = base::TimeTicks::HighResNow();
result = avcodec_decode_audio3(codec_context, samples, &size_out,
&packet);
+ base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
+
if (size_out) {
+ decode_times.push_back(delta.InMillisecondsF());
++frames;
read_result = 0; // Force continuation.
if (output) {
if (fwrite(samples, 1, size_out, output) !=
static_cast<size_t>(size_out)) {
- std::cerr << "could not write data after " << size_out;
+ std::cerr << "Error: Could not write "
+ << size_out << " bytes for " << in_path << std::endl;
return 1;
}
}
- if (hash) {
- hash_value = hash_djb2(reinterpret_cast<const uint8*>(samples),
- size_out, hash_value);
+ if (hash_djb2) {
+ hash_value = DJB2Hash(reinterpret_cast<const uint8*>(samples),
+ size_out, hash_value);
+ }
+ if (hash_md5) {
+ MD5Update(&ctx, reinterpret_cast<const uint8*>(samples),
+ size_out);
}
}
} else if (target_codec == CODEC_TYPE_VIDEO) {
int got_picture = 0;
+
+ base::TimeTicks decode_start = base::TimeTicks::HighResNow();
result = avcodec_decode_video2(codec_context, frame, &got_picture,
&packet);
+ base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
+
if (got_picture) {
+ decode_times.push_back(delta.InMillisecondsF());
++frames;
read_result = 0; // Force continuation.
- if (output || hash) {
- for (int plane = 0; plane < 3; ++plane) {
- const uint8* source = frame->data[plane];
- const size_t source_stride = frame->linesize[plane];
- size_t bytes_per_line = codec_context->width;
- size_t copy_lines = codec_context->height;
- if (plane != 0) {
- switch (codec_context->pix_fmt) {
- case PIX_FMT_YUV420P:
- case PIX_FMT_YUVJ420P:
- bytes_per_line /= 2;
- copy_lines = (copy_lines + 1) / 2;
- break;
- case PIX_FMT_YUV422P:
- case PIX_FMT_YUVJ422P:
- bytes_per_line /= 2;
- break;
- case PIX_FMT_YUV444P:
- case PIX_FMT_YUVJ444P:
- break;
- default:
- std::cerr << "unknown video format: "
- << codec_context->pix_fmt;
- return 1;
- }
+ for (int plane = 0; plane < 3; ++plane) {
+ const uint8* source = frame->data[plane];
+ const size_t source_stride = frame->linesize[plane];
+ size_t bytes_per_line = codec_context->width;
+ size_t copy_lines = codec_context->height;
+ if (plane != 0) {
+ switch (codec_context->pix_fmt) {
+ case PIX_FMT_YUV420P:
+ case PIX_FMT_YUVJ420P:
+ bytes_per_line /= 2;
+ copy_lines = (copy_lines + 1) / 2;
+ break;
+ case PIX_FMT_YUV422P:
+ case PIX_FMT_YUVJ422P:
+ bytes_per_line /= 2;
+ break;
+ case PIX_FMT_YUV444P:
+ case PIX_FMT_YUVJ444P:
+ break;
+ default:
+ std::cerr << "Error: Unknown video format "
+ << codec_context->pix_fmt;
+ return 1;
}
- if (output) {
- for (size_t i = 0; i < copy_lines; ++i) {
- if (fwrite(source, 1, bytes_per_line, output) !=
- bytes_per_line) {
- std::cerr << "could not write data after "
- << bytes_per_line;
- return 1;
- }
- source += source_stride;
+ }
+ if (output) {
+ for (size_t i = 0; i < copy_lines; ++i) {
+ if (fwrite(source, 1, bytes_per_line, output) !=
+ bytes_per_line) {
+ std::cerr << "Error: Could not write data after "
+ << copy_lines << " lines for "
+ << in_path << std::endl;
+ return 1;
}
+ source += source_stride;
}
- if (hash) {
- for (size_t i = 0; i < copy_lines; ++i) {
- hash_value = hash_djb2(source, bytes_per_line, hash_value);
- source += source_stride;
- }
+ }
+ if (hash_djb2) {
+ for (size_t i = 0; i < copy_lines; ++i) {
+ hash_value = DJB2Hash(source, bytes_per_line, hash_value);
+ source += source_stride;
+ }
+ }
+ if (hash_md5) {
+ for (size_t i = 0; i < copy_lines; ++i) {
+ MD5Update(&ctx, reinterpret_cast<const uint8*>(source),
+ bytes_per_line);
+ source += source_stride;
}
}
}
@@ -327,21 +437,22 @@ int main(int argc, const char** argv) {
} else {
NOTREACHED();
}
- base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
-
- decode_times.push_back(delta.InMillisecondsF());
// Make sure our decoding went OK.
if (result < 0) {
- std::cerr << "Error while decoding" << std::endl;
+ std::cerr << "Error: avcodec_decode returned "
+ << result << " for " << in_path << std::endl;
return 1;
}
}
// Free our packet.
av_free_packet(&packet);
+
+ if (max_frames && (frames >= max_frames))
+ break;
} while (read_result >= 0);
base::TimeDelta total = base::TimeTicks::HighResNow() - start;
-
+ LeaveTimingSection();
if (output)
file_util::CloseFile(output);
@@ -352,21 +463,17 @@ int main(int argc, const char** argv) {
}
// Print our results.
- std::cout.setf(std::ios::fixed);
- std::cout.precision(2);
- std::cout << std::endl;
- std::cout << " Frames:" << std::setw(10) << frames
- << std::endl;
- std::cout << " Total:" << std::setw(10) << total.InMillisecondsF()
- << " ms" << std::endl;
- std::cout << " Summation:" << std::setw(10) << sum
- << " ms" << std::endl;
- if (hash) {
- std::cout << " Hash:" << std::setw(10) << hash_value
- << std::endl;
- }
-
- if (frames > 0u) {
+ log_out->setf(std::ios::fixed);
+ log_out->precision(2);
+ *log_out << std::endl;
+ *log_out << " Frames:" << std::setw(11) << frames
+ << std::endl;
+ *log_out << " Total:" << std::setw(11) << total.InMillisecondsF()
+ << " ms" << std::endl;
+ *log_out << " Summation:" << std::setw(11) << sum
+ << " ms" << std::endl;
+
+ if (frames > 0) {
// Calculate the average time per frame.
double average = sum / frames;
@@ -374,7 +481,7 @@ int main(int argc, const char** argv) {
// Standard deviation will only be accurate if no threads are used.
// TODO(fbarchard): Rethink standard deviation calculation.
double squared_sum = 0;
- for (size_t i = 0; i < frames; ++i) {
+ for (int i = 0; i < frames; ++i) {
double difference = decode_times[i] - average;
squared_sum += difference * difference;
}
@@ -382,10 +489,27 @@ int main(int argc, const char** argv) {
// Calculate the standard deviation (jitter).
double stddev = sqrt(squared_sum / frames);
- std::cout << " Average:" << std::setw(10) << average
- << " ms" << std::endl;
- std::cout << " StdDev:" << std::setw(10) << stddev
- << " ms" << std::endl;
+ *log_out << " Average:" << std::setw(11) << average
+ << " ms" << std::endl;
+ *log_out << " StdDev:" << std::setw(11) << stddev
+ << " ms" << std::endl;
+ }
+ if (hash_djb2) {
+ *log_out << " DJB2:" << std::setw(11) << hash_value
+ << " " << in_path << std::endl;
+ }
+ if (hash_md5) {
+ MD5Digest digest; // The result of the computation.
+ MD5Final(&digest, &ctx);
+ *log_out << " MD5: " << MD5DigestToBase16(digest)
+ << " " << in_path << std::endl;
+ }
+#if defined(OS_WIN)
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ *log_out << " Exception:" << std::setw(11) << GetExceptionCode()
+ << " " << in_path << std::endl;
+ return 1;
}
+#endif
return 0;
}
diff --git a/media/bench/file_protocol.cc b/media/bench/file_protocol.cc
index 517d101..cf44eee 100644
--- a/media/bench/file_protocol.cc
+++ b/media/bench/file_protocol.cc
@@ -4,56 +4,72 @@
#include "media/bench/file_protocol.h"
-#include <stdio.h>
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "media/filters/ffmpeg_common.h"
+// warning C4996: 'open': The POSIX name for this item is deprecated.
+MSVC_PUSH_DISABLE_WARNING(4996)
+
namespace {
-FILE* ToFile(void* data) {
- return reinterpret_cast<FILE*>(data);
+int GetHandle(URLContext *h) {
+ return static_cast<int>(reinterpret_cast<intptr_t>(h->priv_data));
}
// FFmpeg protocol interface.
int OpenContext(URLContext* h, const char* filename, int flags) {
- FILE* file = file_util::OpenFile(filename, "rb");
- if (!file)
- return AVERROR_IO;
-
- h->priv_data = file;
- h->flags = URL_RDONLY;
+ int access = O_RDONLY;
+ if (flags & URL_RDWR) {
+ access = O_CREAT | O_TRUNC | O_RDWR;
+ } else if (flags & URL_WRONLY) {
+ access = O_CREAT | O_TRUNC | O_WRONLY;
+ }
+#ifdef O_BINARY
+ access |= O_BINARY;
+#endif
+ int f = open(filename, access, 0666);
+ if (f == -1)
+ return AVERROR(ENOENT);
+ h->priv_data = reinterpret_cast<void*>(static_cast<intptr_t>(f));
h->is_streamed = false;
return 0;
}
int ReadContext(URLContext* h, unsigned char* buf, int size) {
- return fread(buf, 1, size, ToFile(h->priv_data));
+ return read(GetHandle(h), buf, size);
}
int WriteContext(URLContext* h, unsigned char* buf, int size) {
- NOTIMPLEMENTED();
- return AVERROR_IO;
+ return write(GetHandle(h), buf, size);
}
offset_t SeekContext(URLContext* h, offset_t offset, int whence) {
#if defined(OS_WIN)
- return static_cast<offset_t> (_fseeki64(ToFile(h->priv_data),
- static_cast<int64>(offset),
- whence));
+ return lseek(GetHandle(h), static_cast<long>(offset), whence);
#else
- return fseek(ToFile(h->priv_data), offset, whence);
+ return lseek(GetHandle(h), offset, whence);
#endif
}
int CloseContext(URLContext* h) {
- if (file_util::CloseFile(ToFile(h->priv_data)))
- return 0;
- return AVERROR_IO;
+ return close(GetHandle(h));
}
} // namespace
+MSVC_POP_WARNING()
+
URLProtocol kFFmpegFileProtocol = {
"file",
&OpenContext,
@@ -61,4 +77,9 @@ URLProtocol kFFmpegFileProtocol = {
&WriteContext,
&SeekContext,
&CloseContext,
+ NULL, // *next
+ NULL, // url_read_pause
+ NULL, // url_read_seek
+ &GetHandle
};
+