diff --git a/webrtc/tools/frame_analyzer/video_quality_analysis.cc b/webrtc/tools/frame_analyzer/video_quality_analysis.cc index b8c90b1071..757959f155 100644 --- a/webrtc/tools/frame_analyzer/video_quality_analysis.cc +++ b/webrtc/tools/frame_analyzer/video_quality_analysis.cc @@ -17,6 +17,9 @@ #include #define STATS_LINE_LENGTH 32 +#define Y4M_FILE_HEADER_MAX_SIZE 200 +#define Y4M_FRAME_DELIMITER "FRAME" +#define Y4M_FRAME_HEADER_SIZE 6 namespace webrtc { namespace test { @@ -84,25 +87,8 @@ bool GetNextStatsLine(FILE* stats_file, char* line) { return true; } -bool GetNextI420Frame(FILE* input_file, int width, int height, - uint8* result_frame) { - int frame_size = GetI420FrameSize(width, height); - bool errors = false; - - size_t bytes_read = fread(result_frame, 1, frame_size, input_file); - if (bytes_read != static_cast(frame_size)) { - // If end-of-file is reached, don't print an error. - if (feof(input_file)) { - return false; - } - fprintf(stdout, "Error while reading frame from file\n"); - errors = true; - } - return !errors; -} - -bool ExtractFrameFromI420(const char* i420_file_name, int width, int height, - int frame_number, uint8* result_frame) { +bool ExtractFrameFromYuvFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame) { int frame_size = GetI420FrameSize(width, height); int offset = frame_number * frame_size; // Calculate offset for the frame. bool errors = false; @@ -128,6 +114,58 @@ bool ExtractFrameFromI420(const char* i420_file_name, int width, int height, return !errors; } +bool ExtractFrameFromY4mFile(const char* y4m_file_name, int width, int height, + int frame_number, uint8* result_frame) { + int frame_size = GetI420FrameSize(width, height); + int frame_offset = frame_number * frame_size + bool errors = false; + + FILE* input_file = fopen(y4m_file_name, "rb"); + if (input_file == NULL) { + fprintf(stderr, "Couldn't open input file for reading: %s\n", + y4m_file_name); + return false; + } + + // YUV4MPEG2, a.k.a. Y4M File format has a file header and a frame header. The + // file header has the aspect: "YUV4MPEG2 C420 W640 H360 Ip F30:1 A1:1". + // Skip the header if this is the first frame of the file. + if (frame_number == 0) { + char frame_header[Y4M_FILE_HEADER_MAX_SIZE]; + size_t bytes_read = + fread(frame_header, 1, Y4M_FILE_HEADER_MAX_SIZE, input_file); + if (bytes_read != static_cast(frame_size) && ferror(input_file)) { + fprintf(stdout, "Error while reading first frame from file %s\n", + y4m_file_name); + fclose(input_file); + return false; + } + std::string header_contents(frame_header); + std::size_t found = header_contents.find(Y4M_FRAME_DELIMITER); + if (found == std::string::npos) { + fprintf(stdout, "Corrupted Y4M header, could not find \"FRAME\" in %s\n", + header_contents.c_str()); + fclose(input_file); + return false; + } + frame_offset = static_cast(found); + } + + // Change stream pointer to new offset, skipping the frame header as well. + fseek(input_file, frame_offset + Y4M_FRAME_HEADER_SIZE, SEEK_SET); + + size_t bytes_read = fread(result_frame, 1, frame_size, input_file); + if (bytes_read != static_cast(frame_size) && + ferror(input_file)) { + fprintf(stdout, "Error while reading frame no %d from file %s\n", + frame_number, y4m_file_name); + errors = true; + } + + fclose(input_file); + return !errors; +} + double CalculateMetrics(VideoAnalysisMetricsType video_metrics_type, const uint8* ref_frame, const uint8* test_frame, int width, int height) { @@ -176,6 +214,12 @@ double CalculateMetrics(VideoAnalysisMetricsType video_metrics_type, void RunAnalysis(const char* reference_file_name, const char* test_file_name, const char* stats_file_name, int width, int height, ResultsContainer* results) { + // Check if the reference_file_name ends with "y4m". + bool y4m_mode = false; + if (std::string(reference_file_name).find("y4m") != std::string::npos){ + y4m_mode = true; + } + int size = GetI420FrameSize(width, height); FILE* stats_file = fopen(stats_file_name, "r"); @@ -202,10 +246,15 @@ void RunAnalysis(const char* reference_file_name, const char* test_file_name, assert(extracted_test_frame != -1); assert(decoded_frame_number != -1); - ExtractFrameFromI420(test_file_name, width, height, extracted_test_frame, - test_frame); - ExtractFrameFromI420(reference_file_name, width, height, - decoded_frame_number, reference_frame); + ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, + test_frame); + if (y4m_mode) { + ExtractFrameFromY4mFile(reference_file_name, width, height, + decoded_frame_number, reference_frame); + } else { + ExtractFrameFromYuvFile(reference_file_name, width, height, + decoded_frame_number, reference_frame); + } // Calculate the PSNR and SSIM. double result_psnr = CalculateMetrics(kPSNR, reference_frame, test_frame, diff --git a/webrtc/tools/frame_analyzer/video_quality_analysis.h b/webrtc/tools/frame_analyzer/video_quality_analysis.h index b2ecc082c7..31064a28f8 100644 --- a/webrtc/tools/frame_analyzer/video_quality_analysis.h +++ b/webrtc/tools/frame_analyzer/video_quality_analysis.h @@ -97,13 +97,14 @@ bool IsThereBarcodeError(std::string line); // frame_0023 0284, we will get 284. int ExtractDecodedFrameNumber(std::string line); -// Gets the next frame from an open I420 file. -bool GetNextI420Frame(FILE* input_file, int width, int height, - uint8* result_frame); +// Extracts an I420 frame at position frame_number from the raw YUV file. +bool ExtractFrameFromYuvFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame); -// Extracts an I420 frame at position frame_number from the file. -bool ExtractFrameFromI420(const char* i420_file_name, int width, int height, - int frame_number, uint8* result_frame); +// Extracts an I420 frame at position frame_number from the Y4M file. The first +// frame has corresponded |frame_number| 0. +bool ExtractFrameFromY4mFile(const char* i420_file_name, int width, int height, + int frame_number, uint8* result_frame); } // namespace test diff --git a/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc b/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc index 1524610684..9c9b131a21 100644 --- a/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc +++ b/webrtc/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include @@ -18,10 +19,16 @@ #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" #include "webrtc/tools/simple_command_line_parser.h" +#define MAX_NUM_FRAMES_PER_FILE INT_MAX + void CompareFiles(const char* reference_file_name, const char* test_file_name, const char* results_file_name, int width, int height) { - FILE* ref_file = fopen(reference_file_name, "rb"); - FILE* test_file = fopen(test_file_name, "rb"); + // Check if the reference_file_name ends with "y4m". + bool y4m_mode = false; + if (std::string(reference_file_name).find("y4m") != std::string::npos){ + y4m_mode = true; + } + FILE* results_file = fopen(results_file_name, "w"); int size = webrtc::test::GetI420FrameSize(width, height); @@ -30,10 +37,19 @@ void CompareFiles(const char* reference_file_name, const char* test_file_name, uint8* test_frame = new uint8[size]; uint8* ref_frame = new uint8[size]; - int frame_counter = 0; + bool read_result = true; + for(int frame_counter = 0; frame_counter < MAX_NUM_FRAMES_PER_FILE; + ++frame_counter){ + read_result &= (y4m_mode) ? webrtc::test::ExtractFrameFromY4mFile( + reference_file_name, width, height, frame_counter, ref_frame): + webrtc::test::ExtractFrameFromYuvFile(reference_file_name, width, + height, frame_counter, ref_frame); + read_result &= webrtc::test::ExtractFrameFromYuvFile(test_file_name, width, + height, frame_counter, test_frame); + + if (!read_result) + break; - while (webrtc::test::GetNextI420Frame(ref_file, width, height, ref_frame) && - webrtc::test::GetNextI420Frame(test_file, width, height, test_frame)) { // Calculate the PSNR and SSIM. double result_psnr = webrtc::test::CalculateMetrics( webrtc::test::kPSNR, ref_frame, test_frame, width, height); @@ -41,13 +57,10 @@ void CompareFiles(const char* reference_file_name, const char* test_file_name, webrtc::test::kSSIM, ref_frame, test_frame, width, height); fprintf(results_file, "Frame: %d, PSNR: %f, SSIM: %f\n", frame_counter, result_psnr, result_ssim); - ++frame_counter; } delete[] test_frame; delete[] ref_frame; - fclose(ref_file); - fclose(test_file); fclose(results_file); }