/* * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include #include #include #include "rtc_tools/frame_analyzer/reference_less_video_analysis_lib.h" #include "rtc_tools/frame_analyzer/video_quality_analysis.h" #define STATS_LINE_LENGTH 28 #define PSNR_FREEZE_THRESHOLD 47 #define SSIM_FREEZE_THRESHOLD .999 #if defined(_WIN32) || defined(_WIN64) #define strtok_r strtok_s #endif void get_height_width_fps(int* height, int* width, int* fps, const std::string& video_file) { // File header looks like : // YUV4MPEG2 W1280 H720 F25:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2. char frame_header[STATS_LINE_LENGTH]; FILE* input_file = fopen(video_file.c_str(), "rb"); size_t bytes_read = fread(frame_header, 1, STATS_LINE_LENGTH - 1, input_file); frame_header[bytes_read] = '\0'; std::string file_header_stats[5]; int no_of_stats = 0; char* save_ptr; char* token = strtok_r(frame_header, " ", &save_ptr); while (token != NULL) { file_header_stats[no_of_stats++] = token; token = strtok_r(NULL, " ", &save_ptr); } *width = std::stoi(file_header_stats[1].erase(0, 1)); *height = std::stoi(file_header_stats[2].erase(0, 1)); *fps = std::stoi(file_header_stats[3].erase(0, 1)); printf("Height: %d Width: %d fps:%d \n", *height, *width, *fps); fclose(input_file); } bool frozen_frame(std::vector psnr_per_frame, std::vector ssim_per_frame, size_t frame) { if (psnr_per_frame[frame] >= PSNR_FREEZE_THRESHOLD || ssim_per_frame[frame] >= SSIM_FREEZE_THRESHOLD) return true; return false; } std::vector find_frame_clusters( const std::vector& psnr_per_frame, const std::vector& ssim_per_frame) { std::vector identical_frame_clusters; int num_frozen = 0; size_t total_no_of_frames = psnr_per_frame.size(); for (size_t each_frame = 0; each_frame < total_no_of_frames; each_frame++) { if (frozen_frame(psnr_per_frame, ssim_per_frame, each_frame)) { num_frozen++; } else if (num_frozen > 0) { // Not frozen anymore. identical_frame_clusters.push_back(num_frozen); num_frozen = 0; } } return identical_frame_clusters; } void print_freezing_metrics(const std::vector& psnr_per_frame, const std::vector& ssim_per_frame) { /* * Prints the different metrics mainly: * 1) Identical frame number, PSNR and SSIM values. * 2) Length of continuous frozen frames. * 3) Max length of continuous freezed frames. * 4) No of unique frames found. * 5) Total different identical frames found. * * Sample output: * Printing metrics for file: /src/rtc_tools/test_3.y4m ============================= Total number of frames received: 74 Total identical frames: 5 Number of unique frames: 69 Printing Identical Frames: Frame Number: 29 PSNR: 48.000000 SSIM: 0.999618 Frame Number: 30 PSNR: 48.000000 SSIM: 0.999898 Frame Number: 60 PSNR: 48.000000 SSIM: 0.999564 Frame Number: 64 PSNR: 48.000000 SSIM: 0.999651 Frame Number: 69 PSNR: 48.000000 SSIM: 0.999684 Print identical frame which appears in clusters : 2 1 1 1 * */ size_t total_no_of_frames = psnr_per_frame.size(); std::vector identical_frame_clusters = find_frame_clusters(psnr_per_frame, ssim_per_frame); int total_identical_frames = std::accumulate( identical_frame_clusters.begin(), identical_frame_clusters.end(), 0); size_t unique_frames = total_no_of_frames - total_identical_frames; printf("Total number of frames received: %zu\n", total_no_of_frames); printf("Total identical frames: %d\n", total_identical_frames); printf("Number of unique frames: %zu\n", unique_frames); printf("Printing Identical Frames: \n"); for (size_t frame = 0; frame < total_no_of_frames; frame++) { if (frozen_frame(psnr_per_frame, ssim_per_frame, frame)) { printf(" Frame Number: %zu PSNR: %f SSIM: %f \n", frame, psnr_per_frame[frame], ssim_per_frame[frame]); } } printf("Print identical frame which appears in clusters : \n"); for (int cluster = 0; cluster < static_cast(identical_frame_clusters.size()); cluster++) printf("%d ", identical_frame_clusters[cluster]); printf("\n"); } void compute_metrics(const std::string& video_file_name, std::vector* psnr_per_frame, std::vector* ssim_per_frame) { int height = 0, width = 0, fps = 0; get_height_width_fps(&height, &width, &fps, video_file_name); int no_of_frames = 0; int size = webrtc::test::GetI420FrameSize(width, height); // Allocate buffers for test and reference frames. uint8_t* current_frame = new uint8_t[size]; uint8_t* next_frame = new uint8_t[size]; while (true) { if (!(webrtc::test::ExtractFrameFromY4mFile(video_file_name.c_str(), width, height, no_of_frames, current_frame))) { break; } if (!(webrtc::test::ExtractFrameFromY4mFile(video_file_name.c_str(), width, height, no_of_frames + 1, next_frame))) { break; } double result_psnr = webrtc::test::CalculateMetrics( webrtc::test::kPSNR, current_frame, next_frame, width, height); double result_ssim = webrtc::test::CalculateMetrics( webrtc::test::kSSIM, current_frame, next_frame, width, height); psnr_per_frame->push_back(result_psnr); ssim_per_frame->push_back(result_ssim); no_of_frames++; } // Cleanup. delete[] current_frame; delete[] next_frame; } bool check_file_extension(const std::string& video_file_name) { if (video_file_name.substr(video_file_name.length() - 3, 3) != "y4m") { printf("Only y4m video file format is supported. Given: %s\n", video_file_name.c_str()); return false; } return true; } int run_analysis(const std::string& video_file) { std::vector psnr_per_frame; std::vector ssim_per_frame; if (check_file_extension(video_file)) { compute_metrics(video_file, &psnr_per_frame, &ssim_per_frame); } else { return -1; } printf("=============================\n"); printf("Printing metrics for file: %s\n", video_file.c_str()); printf("=============================\n"); print_freezing_metrics(psnr_per_frame, ssim_per_frame); return 0; }