From a31b254084ddfb36c67b65e94670170b16214b2c Mon Sep 17 00:00:00 2001 From: "kjellander@webrtc.org" Date: Fri, 7 Oct 2011 06:50:22 +0000 Subject: [PATCH] Python output flag and keyframe interval flags. Refactored main method into using 6 helper methods for better overview. Review URL: http://webrtc-codereview.appspot.com/207001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@709 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../codecs/test/packet_manipulator.cc | 11 + .../codecs/test/packet_manipulator.h | 2 + .../codecs/test/videoprocessor.cc | 44 +- .../video_coding/codecs/test/videoprocessor.h | 21 +- .../codecs/tools/video_quality_measurement.cc | 641 +++++++++++------- 5 files changed, 466 insertions(+), 253 deletions(-) diff --git a/src/modules/video_coding/codecs/test/packet_manipulator.cc b/src/modules/video_coding/codecs/test/packet_manipulator.cc index 6eabd6fa21..ab75dc1dbb 100644 --- a/src/modules/video_coding/codecs/test/packet_manipulator.cc +++ b/src/modules/video_coding/codecs/test/packet_manipulator.cc @@ -73,5 +73,16 @@ int PacketManipulatorImpl::ManipulatePackets( return nbr_packets_dropped; } +const char* PacketLossModeToStr(PacketLossMode e) { + switch (e) { + case kUniform: + return "Uniform"; + case kBurst: + return "Burst"; + default: + assert(false); + } +} + } // namespace test } // namespace webrtcc diff --git a/src/modules/video_coding/codecs/test/packet_manipulator.h b/src/modules/video_coding/codecs/test/packet_manipulator.h index 76405ffe54..9ef1f7260c 100644 --- a/src/modules/video_coding/codecs/test/packet_manipulator.h +++ b/src/modules/video_coding/codecs/test/packet_manipulator.h @@ -27,6 +27,8 @@ enum PacketLossMode { // length. kBurst }; +// Returns a string representation of the enum value. +const char* PacketLossModeToStr(PacketLossMode e); // Contains configurations related to networking and simulation of // scenarios caused by network interference. diff --git a/src/modules/video_coding/codecs/test/videoprocessor.cc b/src/modules/video_coding/codecs/test/videoprocessor.cc index a03071e65f..9ab0da0e25 100644 --- a/src/modules/video_coding/codecs/test/videoprocessor.cc +++ b/src/modules/video_coding/codecs/test/videoprocessor.cc @@ -132,7 +132,15 @@ bool VideoProcessorImpl::ProcessFrame(int frame_number) { encode_start_ = TickTime::Now(); // Use the frame number as "timestamp" to identify frames source_frame_._timeStamp = frame_number; - WebRtc_Word32 encode_result = encoder_->Encode(source_frame_); + + // Decide if we're going to force a keyframe: + VideoFrameType frame_type = kDeltaFrame; + if (config_.keyframe_interval > 0 && + frame_number % config_.keyframe_interval == 0) { + frame_type = kKeyFrame; + } + WebRtc_Word32 encode_result = encoder_->Encode(source_frame_, NULL, + frame_type); if (encode_result != WEBRTC_VIDEO_CODEC_OK) { fprintf(stderr, "Failed to encode frame %d, return code: %d\n", frame_number, encode_result); @@ -223,6 +231,40 @@ int VideoProcessorImpl::GetElapsedTimeMicroseconds( return static_cast(encode_time); } +const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) { + switch (e) { + case kExcludeOnlyFirstKeyFrame: + return "ExcludeOnlyFirstKeyFrame"; + case kExcludeAllKeyFrames: + return "ExcludeAllKeyFrames"; + default: + assert(false); + } +} + +const char* VideoCodecTypeToStr(webrtc::VideoCodecType e) { + switch (e) { + case kVideoCodecH263: + return "H263"; + case kVideoCodecH264: + return "H264"; + case kVideoCodecVP8: + return "VP8"; + case kVideoCodecMPEG4: + return "MPEG4"; + case kVideoCodecI420: + return "I420"; + case kVideoCodecRED: + return "RED"; + case kVideoCodecULPFEC: + return "ULPFEC"; + case kVideoCodecUnknown: + return "Unknown"; + default: + assert(false); + } +} + // Callbacks WebRtc_Word32 VideoProcessorImpl::VideoProcessorEncodeCompleteCallback::Encoded( diff --git a/src/modules/video_coding/codecs/test/videoprocessor.h b/src/modules/video_coding/codecs/test/videoprocessor.h index 71bfb754d8..74ebcb9dad 100644 --- a/src/modules/video_coding/codecs/test/videoprocessor.h +++ b/src/modules/video_coding/codecs/test/videoprocessor.h @@ -30,6 +30,8 @@ enum ExcludeFrameTypes { // sequence they occur. kExcludeAllKeyFrames }; +// Returns a string representation of the enum value. +const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e); // Test configuration for a test run struct TestConfig { @@ -37,7 +39,7 @@ struct TestConfig { : name(""), description(""), test_number(0), input_filename(""), output_filename(""), output_dir("out"), networking_config(), exclude_frame_types(kExcludeOnlyFirstKeyFrame), - use_single_core(false) { + frame_length_in_bytes(-1), use_single_core(false), keyframe_interval(0) { }; // Name of the test. This is purely metadata and does not affect @@ -70,6 +72,11 @@ struct TestConfig { // from packet loss. Default: kExcludeOnlyFirstKeyFrame. ExcludeFrameTypes exclude_frame_types; + // The length of a single frame of the input video file. This value is + // calculated out of the width and height according to the video format + // specification. Must be set before processing. + int frame_length_in_bytes; + // Force the encoder and decoder to use a single core for processing. // Using a single core is necessary to get a deterministic behavior for the // encoded frames - using multiple cores will produce different encoded frames @@ -79,11 +86,23 @@ struct TestConfig { // Default: false. bool use_single_core; + // If set to a value >0 this setting forces the encoder to create a keyframe + // every Nth frame. Note that the encoder may create a keyframe in other + // locations in addition to the interval that is set using this parameter. + // Forcing key frames may also affect encoder planning optimizations in + // a negative way, since it will suddenly be forced to produce an expensive + // key frame. + // Default: 0. + int keyframe_interval; + // The codec settings to use for the test (target bitrate, video size, // framerate and so on) webrtc::VideoCodec codec_settings; }; +// Returns a string representation of the enum value. +const char* VideoCodecTypeToStr(webrtc::VideoCodecType e); + // Handles encoding/decoding of video using the VideoEncoder/VideoDecoder // interfaces. This is done in a sequential manner in order to be able to // measure times properly. diff --git a/src/modules/video_coding/codecs/tools/video_quality_measurement.cc b/src/modules/video_coding/codecs/tools/video_quality_measurement.cc index 262400040e..b8a8d5793a 100644 --- a/src/modules/video_coding/codecs/tools/video_quality_measurement.cc +++ b/src/modules/video_coding/codecs/tools/video_quality_measurement.cc @@ -22,8 +22,7 @@ #include "videoprocessor.h" #include "vp8.h" -DEFINE_string(test_name, "Quality test", "The name of the test to run. " - "Default: Quality test."); +DEFINE_string(test_name, "Quality test", "The name of the test to run. "); DEFINE_string(test_description, "", "A more detailed description about what " "the current test is about."); DEFINE_string(input_filename, "", "Input file. " @@ -53,6 +52,10 @@ DEFINE_string(output_filename, "", "Output file. " "of the source file. By default this is the same name as the " "input file with '_out' appended before the extension."); DEFINE_int32(bitrate, 500, "Bit rate in kilobits/second."); +DEFINE_int32(keyframe_interval, 0, "Forces a keyframe every Nth frame. " + "0 means the encoder decides when to insert keyframes. Note that " + "the encoder may create a keyframe in other locations in addition " + "to the interval that is set using this parameter."); DEFINE_int32(packet_size, 1500, "Simulated network packet size in bytes (MTU). " "Used for packet loss simulation."); DEFINE_int32(max_payload_size, 1440, "Max payload size in bytes for the " @@ -69,6 +72,330 @@ DEFINE_int32(packet_loss_burst_length, 1, "Packet loss burst length. Defines " DEFINE_bool(csv, false, "CSV output. Enabling this will output all frame " "statistics at the end of execution. Recommended to run combined " "with --noverbose to avoid mixing output."); +DEFINE_bool(python, false, "Python output. Enabling this will output all frame " + "statistics as a Python script at the end of execution. " + "Recommended to run combine with --noverbose to avoid mixing " + "output."); + +// Validates the arguments given as command line flags. +// Returns 0 if everything is OK, otherwise an exit code. +int HandleCommandLineFlags(webrtc::test::TestConfig* config) { + // Validate the mandatory flags: + if (FLAGS_input_filename == "" || FLAGS_width == -1 || FLAGS_height == -1) { + printf("%s\n", google::ProgramUsage()); + return 1; + } + config->name = FLAGS_test_name; + config->description = FLAGS_test_description; + + // Verify the input file exists and is readable: + FILE* test_file; + test_file = fopen(FLAGS_input_filename.c_str(), "rb"); + if (test_file == NULL) { + fprintf(stderr, "Cannot read the specified input file: %s\n", + FLAGS_input_filename.c_str()); + return 2; + } + fclose(test_file); + config->input_filename = FLAGS_input_filename; + + // Verify the output dir exists: + DIR* output_dir = opendir(FLAGS_output_dir.c_str()); + if (output_dir == NULL) { + fprintf(stderr, "Cannot find output directory: %s\n", + FLAGS_output_dir.c_str()); + return 3; + } + closedir(output_dir); + config->output_dir = FLAGS_output_dir; + + // Manufacture an output filename if none was given: + if (FLAGS_output_filename == "") { + // Cut out the filename without extension from the given input file + // (which may include a path) + int startIndex = FLAGS_input_filename.find_last_of("/") + 1; + if (startIndex == 0) { + startIndex = 0; + } + FLAGS_output_filename = + FLAGS_input_filename.substr(startIndex, + FLAGS_input_filename.find_last_of(".") + - startIndex) + "_out.yuv"; + } + + // Verify output file can be written + if (FLAGS_output_dir == ".") { + config->output_filename = FLAGS_output_filename; + } else { + config->output_filename = FLAGS_output_dir + "/"+ FLAGS_output_filename; + } + test_file = fopen(config->output_filename.c_str(), "wb"); + if (test_file == NULL) { + fprintf(stderr, "Cannot write output file: %s\n", + config->output_filename.c_str()); + return 4; + } + fclose(test_file); + + // Check single core flag + config->use_single_core = FLAGS_use_single_core; + + // Seed our random function if that flag is enabled. This will force + // repeatable behaviour between runs + if (!FLAGS_disable_fixed_random_seed) { + srand(0); + } + + // Check the bit rate + if (FLAGS_bitrate <= 0) { + fprintf(stderr, "Bit rate must be >0 kbps, was: %d\n", FLAGS_bitrate); + return 5; + } + config->codec_settings.startBitrate = FLAGS_bitrate; + + // Check the keyframe interval + if (FLAGS_keyframe_interval < 0) { + fprintf(stderr, "Keyframe interval must be >=0, was: %d\n", + FLAGS_keyframe_interval); + return 6; + } + config->keyframe_interval = FLAGS_keyframe_interval; + + // Check packet size and max payload size + if (FLAGS_packet_size <= 0) { + fprintf(stderr, "Packet size must be >0 bytes, was: %d\n", + FLAGS_packet_size); + return 7; + } + config->networking_config.packet_size_in_bytes = FLAGS_packet_size; + + if (FLAGS_max_payload_size <= 0) { + fprintf(stderr, "Max payload size must be >0 bytes, was: %d\n", + FLAGS_max_payload_size); + return 8; + } + config->networking_config.max_payload_size_in_bytes = + FLAGS_max_payload_size; + + // Check the width and height + if (FLAGS_width <= 0 || FLAGS_height <= 0) { + fprintf(stderr, "Width and height must be >0."); + return 9; + } + config->codec_settings.codecType = webrtc::kVideoCodecVP8; + config->codec_settings.width = FLAGS_width; + config->codec_settings.height = FLAGS_height; + config->codec_settings.maxFramerate = FLAGS_framerate; + + // Calculate the size of each frame to read (according to YUV spec): + config->frame_length_in_bytes = + 3 * config->codec_settings.width * config->codec_settings.height / 2; + + // Check packet loss settings + if (FLAGS_packet_loss_mode != "uniform" && + FLAGS_packet_loss_mode != "burst") { + fprintf(stderr, "Unsupported packet loss mode, must be 'uniform' or " + "'burst'\n."); + return 10; + } + config->networking_config.packet_loss_mode = webrtc::test::kUniform; + if (FLAGS_packet_loss_mode == "burst") { + config->networking_config.packet_loss_mode = webrtc::test::kBurst; + } + + if (FLAGS_packet_loss_probability < 0.0 || + FLAGS_packet_loss_probability > 1.0) { + fprintf(stderr, "Invalid packet loss probability. Must be 0.0 - 1.0, " + "was: %f\n", FLAGS_packet_loss_probability); + return 11; + } + config->networking_config.packet_loss_probability = + FLAGS_packet_loss_probability; + + if (FLAGS_packet_loss_burst_length < 1) { + fprintf(stderr, "Invalid packet loss burst length, must be >=1, " + "was: %d\n", FLAGS_packet_loss_burst_length); + return 12; + } + config->networking_config.packet_loss_burst_length = + FLAGS_packet_loss_burst_length; + return 0; +} + +void CalculateSsimVideoMetrics(webrtc::test::TestConfig* config, + QualityMetricsResult* ssimResult) { + log("Calculating SSIM...\n"); + SsimFromFiles(config->input_filename.c_str(), config->output_filename.c_str(), + config->codec_settings.width, + config->codec_settings.height, ssimResult); + log(" Average: %3.2f\n", ssimResult->average); + log(" Min : %3.2f (frame %d)\n", ssimResult->min, + ssimResult->min_frame_number); + log(" Max : %3.2f (frame %d)\n", ssimResult->max, + ssimResult->max_frame_number); +} + +void CalculatePsnrVideoMetrics(webrtc::test::TestConfig* config, + QualityMetricsResult* psnrResult) { + log("Calculating PSNR...\n"); + PsnrFromFiles(config->input_filename.c_str(), config->output_filename.c_str(), + config->codec_settings.width, + config->codec_settings.height, psnrResult); + log(" Average: %3.2f\n", psnrResult->average); + log(" Min : %3.2f (frame %d)\n", psnrResult->min, + psnrResult->min_frame_number); + log(" Max : %3.2f (frame %d)\n", psnrResult->max, + psnrResult->max_frame_number); +} + +void PrintConfigurationSummary(const webrtc::test::TestConfig& config) { + log("Quality test with parameters:\n"); + log(" Test name : %s\n", config.name.c_str()); + log(" Description : %s\n", config.description.c_str()); + log(" Input filename : %s\n", config.input_filename.c_str()); + log(" Output directory : %s\n", config.output_dir.c_str()); + log(" Output filename : %s\n", config.output_filename.c_str()); + log(" Frame length : %d bytes\n", config.frame_length_in_bytes); + log(" Packet size : %d bytes\n", + config.networking_config.packet_size_in_bytes); + log(" Max payload size : %d bytes\n", + config.networking_config.max_payload_size_in_bytes); + log(" Packet loss:\n"); + log(" Mode : %s\n", + PacketLossModeToStr(config.networking_config.packet_loss_mode)); + log(" Probability : %2.1f\n", + config.networking_config.packet_loss_probability); + log(" Burst length : %d packets\n", + config.networking_config.packet_loss_burst_length); +} + +void PrintCsvOutput(const webrtc::test::Stats& stats, + const QualityMetricsResult& ssimResult, + const QualityMetricsResult& psnrResult) { + log("\nCSV output (recommended to run with --noverbose to skip the " + "above output)\n"); + printf("frame_number encoding_successful decoding_successful " + "encode_return_code decode_return_code " + "encode_time_in_us decode_time_in_us " + "bit_rate_in_kbps encoded_frame_length_in_bytes frame_type " + "packets_dropped total_packets " + "ssim psnr\n"); + + for (unsigned int i = 0; i < stats.stats_.size(); ++i) { + const webrtc::test::FrameStatistic& f = stats.stats_[i]; + const FrameResult& ssim = ssimResult.frames[i]; + const FrameResult& psnr = psnrResult.frames[i]; + printf("%4d, %d, %d, %2d, %2d, %6d, %6d, %5d, %7d, %d, %2d, %2d, " + "%5.3f, %5.2f\n", + f.frame_number, + f.encoding_successful, + f.decoding_successful, + f.encode_return_code, + f.decode_return_code, + f.encode_time_in_us, + f.decode_time_in_us, + f.bit_rate_in_kbps, + f.encoded_frame_length_in_bytes, + f.frame_type, + f.packets_dropped, + f.total_packets, + ssim.value, + psnr.value); + } +} + +void PrintPythonOutput(const webrtc::test::TestConfig& config, + const webrtc::test::Stats& stats, + const QualityMetricsResult& ssimResult, + const QualityMetricsResult& psnrResult) { + log("\nPython output (recommended to run with --noverbose to skip the " + "above output)\n"); + printf("test_configuration = [" + "{'name': 'name', 'value': '%s'},\n" + "{'name': 'description', 'value': '%s'},\n" + "{'name': 'test_number', 'value': '%d'},\n" + "{'name': 'input_filename', 'value': '%s'},\n" + "{'name': 'output_filename', 'value': '%s'},\n" + "{'name': 'output_dir', 'value': '%s'},\n" + "{'name': 'packet_size_in_bytes', 'value': '%d'},\n" + "{'name': 'max_payload_size_in_bytes', 'value': '%d'},\n" + "{'name': 'packet_loss_mode', 'value': '%s'},\n" + "{'name': 'packet_loss_probability', 'value': '%f'},\n" + "{'name': 'packet_loss_burst_length', 'value': '%d'},\n" + "{'name': 'exclude_frame_types', 'value': '%s'},\n" + "{'name': 'frame_length_in_bytes', 'value': '%d'},\n" + "{'name': 'use_single_core', 'value': '%s'},\n" + "{'name': 'keyframe_interval;', 'value': '%d'},\n" + "{'name': 'video_codec_type', 'value': '%s'},\n" + "{'name': 'width', 'value': '%d'},\n" + "{'name': 'height', 'value': '%d'},\n" + "{'name': 'bit_rate_in_kbps', 'value': '%d'},\n" + "]\n", + config.name.c_str(), + config.description.c_str(), + config.test_number, + config.input_filename.c_str(), + config.output_filename.c_str(), + config.output_dir.c_str(), + config.networking_config.packet_size_in_bytes, + config.networking_config.max_payload_size_in_bytes, + PacketLossModeToStr(config.networking_config.packet_loss_mode), + config.networking_config.packet_loss_probability, + config.networking_config.packet_loss_burst_length, + ExcludeFrameTypesToStr(config.exclude_frame_types), + config.frame_length_in_bytes, + config.use_single_core ? "True " : "False", + config.keyframe_interval, + webrtc::test::VideoCodecTypeToStr(config.codec_settings.codecType), + config.codec_settings.width, + config.codec_settings.height, + config.codec_settings.startBitrate); + printf("frame_data_types = {" + "'frame_number': ('number', 'Frame number'),\n" + "'encoding_successful': ('boolean', 'Encoding successful?'),\n" + "'decoding_successful': ('boolean', 'Decoding successful?'),\n" + "'encode_time': ('number', 'Encode time (us)'),\n" + "'decode_time': ('number', 'Decode time (us)'),\n" + "'encode_return_code': ('number', 'Encode return code'),\n" + "'decode_return_code': ('number', 'Decode return code'),\n" + "'bit_rate': ('number', 'Bit rate (kbps)'),\n" + "'encoded_frame_length': " + "('number', 'Encoded frame length (bytes)'),\n" + "'frame_type': ('string', 'Frame type'),\n" + "'packets_dropped': ('number', 'Packets dropped'),\n" + "'total_packets': ('number', 'Total packets'),\n" + "'ssim': ('number', 'SSIM'),\n" + "'psnr': ('number', 'PSNR (dB)'),\n" + "}\n"); + printf("frame_data = ["); + for (unsigned int i = 0; i < stats.stats_.size(); ++i) { + const webrtc::test::FrameStatistic& f = stats.stats_[i]; + const FrameResult& ssim = ssimResult.frames[i]; + const FrameResult& psnr = psnrResult.frames[i]; + printf("{'frame_number': %d, " + "'encoding_successful': %s, 'decoding_successful': %s, " + "'encode_time': %d, 'decode_time': %d, " + "'encode_return_code': %d, 'decode_return_code': %d, " + "'bit_rate': %d, 'encoded_frame_length': %d, 'frame_type': %s, " + "'packets_dropped': %d, 'total_packets': %d, " + "'ssim': %f, 'psnr': %f},\n", + f.frame_number, + f.encoding_successful ? "True " : "False", + f.decoding_successful ? "True " : "False", + f.encode_time_in_us, + f.decode_time_in_us, + f.encode_return_code, + f.decode_return_code, + f.bit_rate_in_kbps, + f.encoded_frame_length_in_bytes, + f.frame_type == webrtc::kDeltaFrame ? "'Delta'" : "'Other'", + f.packets_dropped, + f.total_packets, + ssim.value, + psnr.value); + } + printf("]\n"); +} // Runs a quality measurement on the input file supplied to the program. // The input file must be in YUV format. @@ -82,256 +409,68 @@ int main(int argc, char* argv[]) { google::ParseCommandLineFlags(&argc, &argv, true); - if (FLAGS_input_filename == "" || FLAGS_width == -1 || FLAGS_height == -1) { - printf("%s\n", google::ProgramUsage()); - return 1; - } else { - webrtc::test::TestConfig config; - config.name = FLAGS_test_name; - config.description = FLAGS_test_description; - - // Verify the input file exists and is readable: - FILE* test_file; - test_file = fopen(FLAGS_input_filename.c_str(), "rb"); - if (test_file == NULL) { - fprintf(stderr, "Cannot read the specified input file: %s\n", - FLAGS_input_filename.c_str()); - return 2; - } - fclose(test_file); - config.input_filename = FLAGS_input_filename; - - // Verify the output dir exists: - DIR* output_dir = opendir(FLAGS_output_dir.c_str()); - if (output_dir == NULL) { - fprintf(stderr, "Cannot find output directory: %s\n", - FLAGS_output_dir.c_str()); - return 3; - } - closedir(output_dir); - config.output_dir = FLAGS_output_dir; - - // Manufacture an output filename if none was given: - if (FLAGS_output_filename == "") { - // Cut out the filename without extension from the given input file - // (which may include a path) - int startIndex = FLAGS_input_filename.find_last_of("/") + 1; - if (startIndex == 0) { - startIndex = 0; - } - FLAGS_output_filename = - FLAGS_input_filename.substr(startIndex, - FLAGS_input_filename.find_last_of(".") - - startIndex) + "_out.yuv"; - } - - // Verify output file can be written - std::string output_filename; - if (FLAGS_output_dir == ".") { - output_filename = FLAGS_output_filename; - } else { - output_filename = FLAGS_output_dir + "/"+ FLAGS_output_filename; - } - test_file = fopen(output_filename.c_str(), "wb"); - if (test_file == NULL) { - fprintf(stderr, "Cannot write output file: %s\n", - output_filename.c_str()); - return 4; - } - fclose(test_file); - config.output_filename = output_filename; - - // Check single core flag - config.use_single_core = FLAGS_use_single_core; - - // Seed our random function if that flag is enabled. This will force - // repeatable behaviour between runs - if (!FLAGS_disable_fixed_random_seed) { - srand(0); - } - - // Check the bit rate - if (FLAGS_bitrate <= 0) { - fprintf(stderr, "Bit rate must be >0 kbps, was: %d\n", FLAGS_bitrate); - return 5; - } - config.codec_settings.startBitrate = FLAGS_bitrate; - - // Check packet size and max payload size - if (FLAGS_packet_size <= 0) { - fprintf(stderr, "Packet size must be >0 bytes, was: %d\n", - FLAGS_packet_size); - return 6; - } - config.networking_config.packet_size_in_bytes = FLAGS_packet_size; - - if (FLAGS_max_payload_size <= 0) { - fprintf(stderr, "Max payload size must be >0 bytes, was: %d\n", - FLAGS_max_payload_size); - return 7; - } - config.networking_config.max_payload_size_in_bytes = FLAGS_max_payload_size; - - // Check the width and height - if (FLAGS_width <= 0 || FLAGS_height <= 0) { - fprintf(stderr, "Width and height must be >0."); - return 8; - } - config.codec_settings.width = FLAGS_width; - config.codec_settings.height = FLAGS_height; - - // Check framerate - if (FLAGS_framerate <= 0) { - fprintf(stderr, "Framerate be >0."); - return 9; - } - config.codec_settings.maxFramerate = FLAGS_framerate; - - // Check packet loss settings - if (FLAGS_packet_loss_mode != "uniform" && - FLAGS_packet_loss_mode != "burst") { - fprintf(stderr, "Unsupported packet loss mode, must be 'uniform' or " - "'burst'\n."); - return 10; - } - config.networking_config.packet_loss_mode = webrtc::test::kUniform; - if (FLAGS_packet_loss_mode == "burst") { - config.networking_config.packet_loss_mode = webrtc::test::kBurst; - } - - if (FLAGS_packet_loss_probability < 0.0 || - FLAGS_packet_loss_probability > 1.0) { - fprintf(stderr, "Invalid packet loss probability. Must be 0.0 - 1.0, " - "was: %f\n", FLAGS_packet_loss_probability); - return 11; - } - config.networking_config.packet_loss_probability = - FLAGS_packet_loss_probability; - - if (FLAGS_packet_loss_burst_length < 1) { - fprintf(stderr, "Invalid packet loss burst length, must be >=1, " - "was: %d\n", FLAGS_packet_loss_burst_length); - return 12; - } - config.networking_config.packet_loss_burst_length = - FLAGS_packet_loss_burst_length; - - // Calculate the size of each frame to read (according to YUV spec): - int frame_length_in_bytes = - 3 * config.codec_settings.width * config.codec_settings.height / 2; - - log("Quality test with parameters:\n"); - log(" Test name : %s\n", FLAGS_test_name.c_str()); - log(" Description : %s\n", FLAGS_test_description.c_str()); - log(" Input filename : %s\n", FLAGS_input_filename.c_str()); - log(" Output directory : %s\n", config.output_dir.c_str()); - log(" Output filename : %s\n", output_filename.c_str()); - log(" Frame size : %d bytes\n", frame_length_in_bytes); - log(" Packet size : %d bytes\n", FLAGS_packet_size); - log(" Max payload size : %d bytes\n", FLAGS_max_payload_size); - log(" Packet loss:\n"); - log(" Mode : %s\n", FLAGS_packet_loss_mode.c_str()); - log(" Probability : %2.1f\n", FLAGS_packet_loss_probability); - log(" Burst length : %d packets\n", FLAGS_packet_loss_burst_length); - - webrtc::VP8Encoder encoder; - webrtc::VP8Decoder decoder; - webrtc::test::Stats stats; - webrtc::test::FileHandlerImpl file_handler(config.input_filename, - config.output_filename, - frame_length_in_bytes); - file_handler.Init(); - webrtc::test::PacketReader packet_reader; - - webrtc::test::PacketManipulatorImpl packet_manipulator( - &packet_reader, config.networking_config); - webrtc::test::VideoProcessorImpl processor(&encoder, &decoder, - &file_handler, - &packet_manipulator, - config, &stats); - processor.Init(); - - int frame_number = 0; - while (processor.ProcessFrame(frame_number)) { - if (frame_number % 80 == 0) { - log("\n"); // make the output a bit nicer. - } - log("."); - frame_number++; - } - log("\n"); - log("Processed %d frames\n", frame_number); - - // Release encoder and decoder to make sure they have finished processing: - encoder.Release(); - decoder.Release(); - - // Verify statistics are correct: - assert(frame_number == static_cast(stats.stats_.size())); - - // Close the files before we start using them for SSIM/PSNR calculations. - file_handler.Close(); - - stats.PrintSummary(); - - // Calculate SSIM - QualityMetricsResult ssimResult; - log("Calculating SSIM...\n"); - SsimFromFiles(FLAGS_input_filename.c_str(), output_filename.c_str(), - config.codec_settings.width, - config.codec_settings.height, &ssimResult); - log(" Average: %3.2f\n", ssimResult.average); - log(" Min : %3.2f (frame %d)\n", ssimResult.min, - ssimResult.min_frame_number); - log(" Max : %3.2f (frame %d)\n", ssimResult.max, - ssimResult.max_frame_number); - - QualityMetricsResult psnrResult; - log("Calculating PSNR...\n"); - PsnrFromFiles(FLAGS_input_filename.c_str(), output_filename.c_str(), - config.codec_settings.width, - config.codec_settings.height, &psnrResult); - log(" Average: %3.2f\n", psnrResult.average); - log(" Min : %3.2f (frame %d)\n", psnrResult.min, - psnrResult.min_frame_number); - log(" Max : %3.2f (frame %d)\n", psnrResult.max, - psnrResult.max_frame_number); - - if (FLAGS_csv) { - log("\nCSV output (recommended to run with --noverbose to skip the " - "above output)\n"); - printf("frame_number encoding_successful decoding_successful " - "encode_return_code decode_return_code " - "encode_time_in_us decode_time_in_us " - "bit_rate_in_kbps encoded_frame_length_in_bytes frame_type " - "packets_dropped total_packets " - "ssim psnr\n"); - - for (unsigned int i = 0; i < stats.stats_.size(); ++i) { - webrtc::test::FrameStatistic& f = stats.stats_[i]; - FrameResult& ssim = ssimResult.frames[i]; - FrameResult& psnr = psnrResult.frames[i]; - printf("%4d, %d, %d, %2d, %2d, %6d, %6d, %5d, %7d, %d, %2d, %2d, " - "%5.3f, %5.2f\n", - f.frame_number, - f.encoding_successful, - f.decoding_successful, - f.encode_return_code, - f.decode_return_code, - f.encode_time_in_us, - f.decode_time_in_us, - f.bit_rate_in_kbps, - f.encoded_frame_length_in_bytes, - f.frame_type, - f.packets_dropped, - f.total_packets, - ssim.value, - psnr.value); - } - } - log("Quality test finished!"); - return 0; + webrtc::test::TestConfig config; + int return_code = HandleCommandLineFlags(&config); + // Exit if an invalid argument is supplied: + if (return_code != 0) { + return return_code; } + + PrintConfigurationSummary(config); + + webrtc::VP8Encoder encoder; + webrtc::VP8Decoder decoder; + webrtc::test::Stats stats; + webrtc::test::FileHandlerImpl file_handler(config.input_filename, + config.output_filename, + config.frame_length_in_bytes); + file_handler.Init(); + webrtc::test::PacketReader packet_reader; + + webrtc::test::PacketManipulatorImpl packet_manipulator( + &packet_reader, config.networking_config); + webrtc::test::VideoProcessorImpl processor(&encoder, &decoder, + &file_handler, + &packet_manipulator, + config, &stats); + processor.Init(); + + int frame_number = 0; + while (processor.ProcessFrame(frame_number)) { + if (frame_number % 80 == 0) { + log("\n"); // make the output a bit nicer. + } + log("."); + frame_number++; + } + log("\n"); + log("Processed %d frames\n", frame_number); + + // Release encoder and decoder to make sure they have finished processing: + encoder.Release(); + decoder.Release(); + + // Verify statistics are correct: + assert(frame_number == stats.stats_.size()); + + // Close the files before we start using them for SSIM/PSNR calculations. + file_handler.Close(); + + stats.PrintSummary(); + + QualityMetricsResult ssimResult; + CalculateSsimVideoMetrics(&config, &ssimResult); + QualityMetricsResult psnrResult; + CalculatePsnrVideoMetrics(&config, &psnrResult); + + if (FLAGS_csv) { + PrintCsvOutput(stats, ssimResult, psnrResult); + } + if (FLAGS_python) { + PrintPythonOutput(config, stats, ssimResult, psnrResult); + } + log("Quality test finished!"); + return 0; }