| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
| 3  * | 3  * | 
| 4  *  Use of this source code is governed by a BSD-style license | 4  *  Use of this source code is governed by a BSD-style license | 
| 5  *  that can be found in the LICENSE file in the root of the source | 5  *  that can be found in the LICENSE file in the root of the source | 
| 6  *  tree. An additional intellectual property rights grant can be found | 6  *  tree. An additional intellectual property rights grant can be found | 
| 7  *  in the file PATENTS.  All contributing project authors may | 7  *  in the file PATENTS.  All contributing project authors may | 
| 8  *  be found in the AUTHORS file in the root of the source tree. | 8  *  be found in the AUTHORS file in the root of the source tree. | 
| 9  */ | 9  */ | 
| 10 | 10 | 
| 11 #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" | 11 #include "webrtc/tools/frame_analyzer/video_quality_analysis.h" | 
| 12 | 12 | 
| 13 #include <assert.h> | 13 #include <assert.h> | 
| 14 #include <stdio.h> | 14 #include <stdio.h> | 
| 15 #include <stdlib.h> | 15 #include <stdlib.h> | 
|  | 16 #include <algorithm> | 
| 16 #include <string> | 17 #include <string> | 
|  | 18 #include <map> | 
|  | 19 #include <utility> | 
| 17 | 20 | 
| 18 #define STATS_LINE_LENGTH 32 | 21 #define STATS_LINE_LENGTH 32 | 
| 19 #define Y4M_FILE_HEADER_MAX_SIZE 200 | 22 #define Y4M_FILE_HEADER_MAX_SIZE 200 | 
| 20 #define Y4M_FRAME_DELIMITER "FRAME" | 23 #define Y4M_FRAME_DELIMITER "FRAME" | 
| 21 #define Y4M_FRAME_HEADER_SIZE 6 | 24 #define Y4M_FRAME_HEADER_SIZE 6 | 
| 22 | 25 | 
| 23 namespace webrtc { | 26 namespace webrtc { | 
| 24 namespace test { | 27 namespace test { | 
| 25 | 28 | 
| 26 using std::string; | 29 using std::string; | 
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 217                                 src_u_b, stride_uv, src_v_b, stride_uv, | 220                                 src_u_b, stride_uv, src_v_b, stride_uv, | 
| 218                                 width, height); | 221                                 width, height); | 
| 219       break; | 222       break; | 
| 220     default: | 223     default: | 
| 221       assert(false); | 224       assert(false); | 
| 222   } | 225   } | 
| 223 | 226 | 
| 224   return result; | 227   return result; | 
| 225 } | 228 } | 
| 226 | 229 | 
| 227 void RunAnalysis(const char* reference_file_name, const char* test_file_name, | 230 void RunAnalysis(const char* reference_file_name, | 
| 228                  const char* stats_file_name, int width, int height, | 231                  const char* test_file_name, | 
|  | 232                  const char* stats_file_reference_name, | 
|  | 233                  const char* stats_file_test_name, | 
|  | 234                  int width, | 
|  | 235                  int height, | 
| 229                  ResultsContainer* results) { | 236                  ResultsContainer* results) { | 
| 230   // Check if the reference_file_name ends with "y4m". | 237   // Check if the reference_file_name ends with "y4m". | 
| 231   bool y4m_mode = false; | 238   bool y4m_mode = false; | 
| 232   if (std::string(reference_file_name).find("y4m") != std::string::npos) { | 239   if (std::string(reference_file_name).find("y4m") != std::string::npos) { | 
| 233     y4m_mode = true; | 240     y4m_mode = true; | 
| 234   } | 241   } | 
| 235 | 242 | 
| 236   int size = GetI420FrameSize(width, height); | 243   int size = GetI420FrameSize(width, height); | 
| 237   FILE* stats_file = fopen(stats_file_name, "r"); | 244   FILE* stats_file_ref = fopen(stats_file_reference_name, "r"); | 
|  | 245   FILE* stats_file_test = fopen(stats_file_test_name, "r"); | 
| 238 | 246 | 
| 239   // String buffer for the lines in the stats file. | 247   // String buffer for the lines in the stats file. | 
| 240   char line[STATS_LINE_LENGTH]; | 248   char line[STATS_LINE_LENGTH]; | 
| 241 | 249 | 
| 242   // Allocate buffers for test and reference frames. | 250   // Allocate buffers for test and reference frames. | 
| 243   uint8_t* test_frame = new uint8_t[size]; | 251   uint8_t* test_frame = new uint8_t[size]; | 
| 244   uint8_t* reference_frame = new uint8_t[size]; | 252   uint8_t* reference_frame = new uint8_t[size]; | 
| 245   int previous_frame_number = -1; | 253   int previous_frame_number = -1; | 
| 246 | 254 | 
|  | 255   // Maps barcode id to the frame id for the reference video. | 
|  | 256   // In case two frames have same id, then we only save the first one. | 
|  | 257   std::map<int, int> ref_barcode_to_frame; | 
| 247   // While there are entries in the stats file. | 258   // While there are entries in the stats file. | 
| 248   while (GetNextStatsLine(stats_file, line)) { | 259   while (GetNextStatsLine(stats_file_ref, line)) { | 
|  | 260     int extracted_ref_frame = ExtractFrameSequenceNumber(line); | 
|  | 261     int decoded_frame_number = ExtractDecodedFrameNumber(line); | 
|  | 262 | 
|  | 263     // Insert will only add if it is not in map already. | 
|  | 264     ref_barcode_to_frame.insert( | 
|  | 265         std::make_pair(decoded_frame_number, extracted_ref_frame)); | 
|  | 266   } | 
|  | 267 | 
|  | 268   while (GetNextStatsLine(stats_file_test, line)) { | 
| 249     int extracted_test_frame = ExtractFrameSequenceNumber(line); | 269     int extracted_test_frame = ExtractFrameSequenceNumber(line); | 
| 250     int decoded_frame_number = ExtractDecodedFrameNumber(line); | 270     int decoded_frame_number = ExtractDecodedFrameNumber(line); | 
|  | 271     auto it = ref_barcode_to_frame.find(decoded_frame_number); | 
|  | 272     if (it == ref_barcode_to_frame.end()) { | 
|  | 273       // Not found in the reference video. | 
|  | 274       // TODO(mandermo) print | 
|  | 275       continue; | 
|  | 276     } | 
|  | 277     int extracted_ref_frame = it->second; | 
| 251 | 278 | 
| 252     // If there was problem decoding the barcode in this frame or the frame has | 279     // If there was problem decoding the barcode in this frame or the frame has | 
| 253     // been duplicated, continue. | 280     // been duplicated, continue. | 
| 254     if (IsThereBarcodeError(line) || | 281     if (IsThereBarcodeError(line) || | 
| 255         decoded_frame_number == previous_frame_number) { | 282         decoded_frame_number == previous_frame_number) { | 
| 256       continue; | 283       continue; | 
| 257     } | 284     } | 
| 258 | 285 | 
| 259     assert(extracted_test_frame != -1); | 286     assert(extracted_test_frame != -1); | 
| 260     assert(decoded_frame_number != -1); | 287     assert(decoded_frame_number != -1); | 
| 261 | 288 | 
| 262     ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, | 289     ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame, | 
| 263                             test_frame); | 290                             test_frame); | 
| 264     if (y4m_mode) { | 291     if (y4m_mode) { | 
| 265       ExtractFrameFromY4mFile(reference_file_name, width, height, | 292       ExtractFrameFromY4mFile(reference_file_name, width, height, | 
| 266                               decoded_frame_number, reference_frame); | 293                               extracted_ref_frame, reference_frame); | 
| 267     } else { | 294     } else { | 
| 268       ExtractFrameFromYuvFile(reference_file_name, width, height, | 295       ExtractFrameFromYuvFile(reference_file_name, width, height, | 
| 269                               decoded_frame_number, reference_frame); | 296                               extracted_ref_frame, reference_frame); | 
| 270     } | 297     } | 
| 271 | 298 | 
| 272     // Calculate the PSNR and SSIM. | 299     // Calculate the PSNR and SSIM. | 
| 273     double result_psnr = CalculateMetrics(kPSNR, reference_frame, test_frame, | 300     double result_psnr = | 
| 274                                           width, height); | 301         CalculateMetrics(kPSNR, reference_frame, test_frame, width, height); | 
| 275     double result_ssim = CalculateMetrics(kSSIM, reference_frame, test_frame, | 302     double result_ssim = | 
| 276                                           width, height); | 303         CalculateMetrics(kSSIM, reference_frame, test_frame, width, height); | 
| 277 | 304 | 
| 278     previous_frame_number = decoded_frame_number; | 305     previous_frame_number = decoded_frame_number; | 
| 279 | 306 | 
| 280     // Fill in the result struct. | 307     // Fill in the result struct. | 
| 281     AnalysisResult result; | 308     AnalysisResult result; | 
| 282     result.frame_number = decoded_frame_number; | 309     result.frame_number = decoded_frame_number; | 
| 283     result.psnr_value = result_psnr; | 310     result.psnr_value = result_psnr; | 
| 284     result.ssim_value = result_ssim; | 311     result.ssim_value = result_ssim; | 
| 285 | 312 | 
| 286     results->frames.push_back(result); | 313     results->frames.push_back(result); | 
| 287   } | 314   } | 
| 288 | 315 | 
| 289   // Cleanup. | 316   // Cleanup. | 
| 290   fclose(stats_file); | 317   fclose(stats_file_ref); | 
|  | 318   fclose(stats_file_test); | 
| 291   delete[] test_frame; | 319   delete[] test_frame; | 
| 292   delete[] reference_frame; | 320   delete[] reference_frame; | 
| 293 } | 321 } | 
| 294 | 322 | 
| 295 void PrintMaxRepeatedAndSkippedFrames(const std::string& label, | 323 void PrintMaxRepeatedAndSkippedFrames(const std::string& label, | 
| 296                                       const std::string& stats_file_name) { | 324                                       const std::string& stats_file_ref_name, | 
| 297   PrintMaxRepeatedAndSkippedFrames(stdout, label, stats_file_name); | 325                                       const std::string& stats_file_test_name) { | 
|  | 326   PrintMaxRepeatedAndSkippedFrames(stdout, label, stats_file_ref_name, | 
|  | 327                                    stats_file_test_name); | 
| 298 } | 328 } | 
| 299 | 329 | 
| 300 void PrintMaxRepeatedAndSkippedFrames(FILE* output, const std::string& label, | 330 namespace { | 
| 301                                       const std::string& stats_file_name) { | 331 std::vector<std::pair<int, int> > GetBarcodeNrToFrequency(FILE* file) { | 
| 302   FILE* stats_file = fopen(stats_file_name.c_str(), "r"); | 332   std::vector<std::pair<int, int> > frame_cnt; | 
| 303   if (stats_file == NULL) { | 333   char line[STATS_LINE_LENGTH]; | 
| 304     fprintf(stderr, "Couldn't open stats file for reading: %s\n", | 334   while (GetNextStatsLine(file, line)) { | 
| 305             stats_file_name.c_str()); | 335     int decoded_frame_number = ExtractDecodedFrameNumber(line); | 
|  | 336     if (frame_cnt.empty() || frame_cnt.back().first != decoded_frame_number) { | 
|  | 337       frame_cnt.push_back(std::make_pair(decoded_frame_number, 1)); | 
|  | 338     } else { | 
|  | 339       ++frame_cnt.back().second; | 
|  | 340     } | 
|  | 341   } | 
|  | 342   return frame_cnt; | 
|  | 343 } | 
|  | 344 }  // namespace | 
|  | 345 | 
|  | 346 void PrintMaxRepeatedAndSkippedFrames(FILE* output, | 
|  | 347                                       const std::string& label, | 
|  | 348                                       const std::string& stats_file_ref_name, | 
|  | 349                                       const std::string& stats_file_test_name) { | 
|  | 350   FILE* stats_file_ref = fopen(stats_file_ref_name.c_str(), "r"); | 
|  | 351   FILE* stats_file_test = fopen(stats_file_test_name.c_str(), "r"); | 
|  | 352   if (stats_file_ref == NULL) { | 
|  | 353     fprintf(stderr, "Couldn't open reference stats file for reading: %s\n", | 
|  | 354             stats_file_ref_name.c_str()); | 
| 306     return; | 355     return; | 
| 307   } | 356   } | 
| 308   char line[STATS_LINE_LENGTH]; | 357   if (stats_file_test == NULL) { | 
|  | 358     fprintf(stderr, "Couldn't open test stats file for reading: %s\n", | 
|  | 359             stats_file_test_name.c_str()); | 
|  | 360     fclose(stats_file_ref); | 
|  | 361     return; | 
|  | 362   } | 
| 309 | 363 | 
| 310   int repeated_frames = 1; |  | 
| 311   int max_repeated_frames = 1; | 364   int max_repeated_frames = 1; | 
| 312   int max_skipped_frames = 1; | 365   int max_skipped_frames = 1; | 
| 313   int previous_frame_number = -1; |  | 
| 314 | 366 | 
| 315   while (GetNextStatsLine(stats_file, line)) { | 367   std::vector<std::pair<int, int> > frame_cnt_ref = | 
| 316     int decoded_frame_number = ExtractDecodedFrameNumber(line); | 368       GetBarcodeNrToFrequency(stats_file_ref); | 
| 317 | 369 | 
| 318     if (decoded_frame_number == -1) { | 370   std::vector<std::pair<int, int> > frame_cnt_test = | 
| 319       continue; | 371       GetBarcodeNrToFrequency(stats_file_test); | 
|  | 372 | 
|  | 373   fclose(stats_file_ref); | 
|  | 374   fclose(stats_file_test); | 
|  | 375 | 
|  | 376   auto it_ref = frame_cnt_ref.begin(); | 
|  | 377   auto it_test = frame_cnt_test.begin(); | 
|  | 378   auto end_ref = frame_cnt_ref.end(); | 
|  | 379   auto end_test = frame_cnt_test.end(); | 
|  | 380 | 
|  | 381   if (it_test == end_test || it_ref == end_ref) { | 
|  | 382     fprintf(stderr, "Either test or ref file is empty, nothing to print"); | 
|  | 383     return; | 
|  | 384   } | 
|  | 385 | 
|  | 386   // Find the first frame in the reference video that match the first frame in | 
|  | 387   // the test video. | 
|  | 388   while (it_ref != end_ref && it_ref->first != it_test->first) { | 
|  | 389     ++it_ref; | 
|  | 390   } | 
|  | 391   if (it_ref == end_ref) { | 
|  | 392     fprintf(stderr, | 
|  | 393             "The barcode in the test videos first frame is not in the " | 
|  | 394             "reference video."); | 
|  | 395     return; | 
|  | 396   } | 
|  | 397 | 
|  | 398   for (;;) { | 
|  | 399     max_repeated_frames = | 
|  | 400         std::max(max_repeated_frames, it_test->second - it_ref->second + 1); | 
|  | 401     ++it_test; | 
|  | 402     if (it_test == end_test) { | 
|  | 403       break; | 
| 320     } | 404     } | 
|  | 405     int skipped_frames = 0; | 
|  | 406     ++it_ref; | 
|  | 407     while (it_ref != end_ref && it_ref->first != it_test->first) { | 
|  | 408       skipped_frames += it_ref->second; | 
|  | 409       ++it_ref; | 
|  | 410     } | 
|  | 411     if (it_ref == end_ref) { | 
|  | 412       fprintf(stderr, | 
|  | 413               "The barcode in the test video is not in the reference video."); | 
|  | 414       return; | 
|  | 415     } | 
|  | 416     if (skipped_frames > max_skipped_frames) { | 
|  | 417       max_skipped_frames = skipped_frames; | 
|  | 418     } | 
|  | 419   } | 
| 321 | 420 | 
| 322     // Calculate how many frames a cluster of repeated frames contains. |  | 
| 323     if (decoded_frame_number == previous_frame_number) { |  | 
| 324       ++repeated_frames; |  | 
| 325       if (repeated_frames > max_repeated_frames) { |  | 
| 326         max_repeated_frames = repeated_frames; |  | 
| 327       } |  | 
| 328     } else { |  | 
| 329       repeated_frames = 1; |  | 
| 330     } |  | 
| 331 |  | 
| 332     // Calculate how much frames have been skipped. |  | 
| 333     if (decoded_frame_number != 0 && previous_frame_number != -1) { |  | 
| 334       int skipped_frames = decoded_frame_number - previous_frame_number - 1; |  | 
| 335       if (skipped_frames > max_skipped_frames) { |  | 
| 336         max_skipped_frames = skipped_frames; |  | 
| 337       } |  | 
| 338     } |  | 
| 339     previous_frame_number = decoded_frame_number; |  | 
| 340   } |  | 
| 341   fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), | 421   fprintf(output, "RESULT Max_repeated: %s= %d\n", label.c_str(), | 
| 342           max_repeated_frames); | 422           max_repeated_frames); | 
| 343   fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), | 423   fprintf(output, "RESULT Max_skipped: %s= %d\n", label.c_str(), | 
| 344           max_skipped_frames); | 424           max_skipped_frames); | 
| 345   fclose(stats_file); |  | 
| 346 } | 425 } | 
| 347 | 426 | 
| 348 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { | 427 void PrintAnalysisResults(const std::string& label, ResultsContainer* results) { | 
| 349   PrintAnalysisResults(stdout, label, results); | 428   PrintAnalysisResults(stdout, label, results); | 
| 350 } | 429 } | 
| 351 | 430 | 
| 352 void PrintAnalysisResults(FILE* output, const std::string& label, | 431 void PrintAnalysisResults(FILE* output, const std::string& label, | 
| 353                           ResultsContainer* results) { | 432                           ResultsContainer* results) { | 
| 354   std::vector<AnalysisResult>::iterator iter; | 433   std::vector<AnalysisResult>::iterator iter; | 
| 355 | 434 | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
| 368     for (iter = results->frames.begin(); iter != results->frames.end() - 1; | 447     for (iter = results->frames.begin(); iter != results->frames.end() - 1; | 
| 369          ++iter) { | 448          ++iter) { | 
| 370       fprintf(output, "%f,", iter->ssim_value); | 449       fprintf(output, "%f,", iter->ssim_value); | 
| 371     } | 450     } | 
| 372     fprintf(output, "%f] score\n", iter->ssim_value); | 451     fprintf(output, "%f] score\n", iter->ssim_value); | 
| 373   } | 452   } | 
| 374 } | 453 } | 
| 375 | 454 | 
| 376 }  // namespace test | 455 }  // namespace test | 
| 377 }  // namespace webrtc | 456 }  // namespace webrtc | 
| OLD | NEW | 
|---|