Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import os | 6 import os |
| 7 import subprocess | 7 import subprocess |
| 8 import sys | 8 import sys |
| 9 | 9 |
| 10 import pyauto_functional | 10 import pyauto_functional |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 _REFERENCE_YUV_FILE = os.path.join(_WORKING_DIR, 'reference_video.yuv') | 23 _REFERENCE_YUV_FILE = os.path.join(_WORKING_DIR, 'reference_video.yuv') |
| 24 | 24 |
| 25 # The YUV file is the file produced by rgba_to_i420_converter. | 25 # The YUV file is the file produced by rgba_to_i420_converter. |
| 26 _OUTPUT_YUV_FILE = os.path.join(_WORKING_DIR, 'captured_video.yuv') | 26 _OUTPUT_YUV_FILE = os.path.join(_WORKING_DIR, 'captured_video.yuv') |
| 27 | 27 |
| 28 | 28 |
| 29 class MissingRequiredToolException(Exception): | 29 class MissingRequiredToolException(Exception): |
| 30 pass | 30 pass |
| 31 | 31 |
| 32 | 32 |
| 33 class FailedToRunToolException(Exception): | |
| 34 pass | |
| 35 | |
| 36 | |
| 33 class WebrtcVideoQualityTest(webrtc_test_base.WebrtcTestBase): | 37 class WebrtcVideoQualityTest(webrtc_test_base.WebrtcTestBase): |
| 34 """Test the video quality of the WebRTC output. | 38 """Test the video quality of the WebRTC output. |
| 35 | 39 |
| 36 Prerequisites: This test case must run on a machine with a virtual webcam that | 40 Prerequisites: This test case must run on a machine with a virtual webcam that |
| 37 plays video from the reference file located in the location defined by | 41 plays video from the reference file located in the location defined by |
| 38 _REFERENCE_YUV_FILE. You must also compile the peerconnection_server target | 42 _REFERENCE_YUV_FILE. You must also compile the peerconnection_server target |
| 39 before you run this test. | 43 before you run this test. |
| 40 | 44 |
| 41 The test case will launch a custom binary (peerconnection_server) which will | 45 The test case will launch a custom binary (peerconnection_server) which will |
| 42 allow two WebRTC clients to find each other. | 46 allow two WebRTC clients to find each other. |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 no_more_frames = self.WaitUntil( | 187 no_more_frames = self.WaitUntil( |
| 184 function=lambda: self.ExecuteJavascript('haveMoreFramesToSend()', | 188 function=lambda: self.ExecuteJavascript('haveMoreFramesToSend()', |
| 185 tab_index=1), | 189 tab_index=1), |
| 186 expect_retval='no-more-frames', retry_sleep=1, timeout=150) | 190 expect_retval='no-more-frames', retry_sleep=1, timeout=150) |
| 187 self.assertTrue(no_more_frames, | 191 self.assertTrue(no_more_frames, |
| 188 msg='Timed out while waiting for frames to send.') | 192 msg='Timed out while waiting for frames to send.') |
| 189 | 193 |
| 190 self.assertTrue(self._RunRGBAToI420Converter(width, height)) | 194 self.assertTrue(self._RunRGBAToI420Converter(width, height)) |
| 191 | 195 |
| 192 stats_file = os.path.join(_WORKING_DIR, 'pyauto_stats.txt') | 196 stats_file = os.path.join(_WORKING_DIR, 'pyauto_stats.txt') |
| 193 self.assertTrue(self._RunBarcodeDecoder(width, height, _OUTPUT_YUV_FILE, | 197 analysis_result = self._CompareVideos(width, height, _OUTPUT_YUV_FILE, |
| 194 stats_file)) | 198 reference_yuv, stats_file) |
| 195 | |
| 196 analysis_result = self._RunFrameAnalyzer(width, height, reference_yuv, | |
| 197 _OUTPUT_YUV_FILE, stats_file) | |
| 198 self._ProcessPsnrAndSsimOutput(analysis_result) | 199 self._ProcessPsnrAndSsimOutput(analysis_result) |
| 199 self._ProcessFramesCountOutput(analysis_result) | 200 self._ProcessFramesCountOutput(analysis_result) |
| 200 | 201 |
| 201 def _StartPywebsocketServer(self): | 202 def _StartPywebsocketServer(self): |
| 202 """Starts the pywebsocket server.""" | 203 """Starts the pywebsocket server.""" |
| 203 print 'Starting pywebsocket server.' | 204 print 'Starting pywebsocket server.' |
| 204 | 205 |
| 205 # Pywebsocket source directory. | 206 # Pywebsocket source directory. |
| 206 path_pyws_dir = os.path.join(pyauto_paths.GetThirdPartyDir(), 'pywebsocket', | 207 path_pyws_dir = os.path.join(pyauto_paths.GetThirdPartyDir(), 'pywebsocket', |
| 207 'src') | 208 'src') |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 # barcode decoder and frame analyzer tools. | 267 # barcode decoder and frame analyzer tools. |
| 267 start_cmd = [path_to_rgba_converter, '--frames_dir=%s' % _WORKING_DIR, | 268 start_cmd = [path_to_rgba_converter, '--frames_dir=%s' % _WORKING_DIR, |
| 268 '--output_file=%s' % _OUTPUT_YUV_FILE, '--width=%d' % width, | 269 '--output_file=%s' % _OUTPUT_YUV_FILE, '--width=%d' % width, |
| 269 '--height=%d' % height, '--delete_frames'] | 270 '--height=%d' % height, '--delete_frames'] |
| 270 print 'Start command: ', ' '.join(start_cmd) | 271 print 'Start command: ', ' '.join(start_cmd) |
| 271 rgba_converter = subprocess.Popen(start_cmd, stdout=sys.stdout, | 272 rgba_converter = subprocess.Popen(start_cmd, stdout=sys.stdout, |
| 272 stderr=sys.stderr) | 273 stderr=sys.stderr) |
| 273 rgba_converter.wait() | 274 rgba_converter.wait() |
| 274 return rgba_converter.returncode == 0 | 275 return rgba_converter.returncode == 0 |
| 275 | 276 |
| 276 def _RunBarcodeDecoder(self, width, height, captured_video_filename, | 277 def _CompareVideos(self, width, height, captured_video_filename, |
| 277 stats_filename): | 278 reference_video_filename, stats_filename): |
| 278 """Runs the barcode decoder script. | 279 """Compares the captured video with the reference video. |
| 279 | 280 |
| 280 The barcode decoder decodes the captured video containing barcodes overlaid | 281 The barcode decoder decodes the captured video containing barcodes overlaid |
| 281 into every frame of the video (produced by rgba_to_i420_converter). It | 282 into every frame of the video (produced by rgba_to_i420_converter). It |
| 282 produces a set of PNG images and a stats file that describes the relation | 283 produces a set of PNG images and a stats file that describes the relation |
| 283 between the filenames and the (decoded) frame number of each frame. | 284 between the filenames and the (decoded) frame number of each frame. |
| 284 | 285 |
| 285 The script depends on an external executable which is a part of the Zxing | 286 The script depends on two external executables which must be located in the |
|
phoglund_chromium
2013/04/30 07:38:28
I think this information is more useful in the cla
kjellander_chromium
2013/05/09 08:32:33
Done.
| |
| 286 barcode library, which must be located in the PATH when running this test. | 287 PATH: |
| 288 * zxing | |
| 289 * ffmpeg | |
| 287 | 290 |
| 288 Args: | 291 Args: |
| 289 width(int): The frames width of the video to be decoded. | 292 width(int): The frames width of the video to be decoded. |
| 290 height(int): The frames height of the video to be decoded. | 293 height(int): The frames height of the video to be decoded. |
| 291 captured_video_filename(string): The captured video file we want to | 294 captured_video_filename(string): The captured video file we want to |
| 292 extract frame images and decode frame numbers from. | 295 extract frame images and decode frame numbers from. |
| 296 reference_video_filename(string): The reference video file we want to | |
| 297 compare the captured video quality with. | |
| 293 stats_filename(string): Filename for the output file containing | 298 stats_filename(string): Filename for the output file containing |
| 294 data that shows the relation between each frame filename and the | 299 data that shows the relation between each frame filename and the |
| 295 reference file's frame numbers. | 300 reference file's frame numbers. |
| 296 | 301 |
| 297 Returns: | 302 Returns: |
| 298 (bool): True if the decoding was successful, False otherwise. | 303 (string): The output of the script. |
| 299 """ | |
| 300 path_to_decoder = os.path.join(pyauto_paths.GetThirdPartyDir(), 'webrtc', | |
| 301 'tools', 'barcode_tools', | |
| 302 'barcode_decoder.py') | |
| 303 if not os.path.exists(path_to_decoder): | |
| 304 raise MissingRequiredToolException( | |
| 305 'Could not locate the barcode decoder script! The barcode decoder ' | |
| 306 'decodes the barcodes overlaid on top of every frame of the captured ' | |
| 307 'video.') | |
| 308 python_interp = sys.executable | |
| 309 start_cmd = [python_interp, path_to_decoder, | |
| 310 '--yuv_file=%s' % captured_video_filename, | |
| 311 '--yuv_frame_width=%d' % width, | |
| 312 '--yuv_frame_height=%d' % height, | |
| 313 '--stats_file=%s' % stats_filename] | |
| 314 print 'Start command: ', ' '.join(start_cmd) | |
| 315 | 304 |
| 316 barcode_decoder = subprocess.Popen(start_cmd, stdout=sys.stdout, | 305 Raises: |
| 317 stderr=sys.stderr) | 306 FailedToRunToolException: If the script fails to run. |
| 318 barcode_decoder.wait() | |
| 319 return barcode_decoder.returncode == 0 | |
| 320 | |
| 321 def _RunFrameAnalyzer(self, width, height, reference_video_file, | |
| 322 captured_video_file, stats_file): | |
| 323 """Runs the frame analyzer tool for PSNR and SSIM analysis. | |
| 324 | |
| 325 The frame analyzer is also part of the webrtc_test_tools. It should be | |
| 326 built before running this test. We assume that the binary will end up next | |
| 327 to Chrome. | |
| 328 | |
| 329 Frame analyzer prints its output to the standard output from where it has to | |
| 330 be read and processed. | |
| 331 | |
| 332 Args: | |
| 333 width(int): The width of the video frames to be analyzed. | |
| 334 height(int): The height of the video frames to be analyzed. | |
| 335 reference_video_file(string): Filename of the video to be used as a | |
| 336 reference during the analysis. | |
| 337 captured_video_file(string): Filename for the video containing the | |
| 338 captured frames. | |
| 339 stats_file(string): Filename for the file that contains frame | |
| 340 synchronization data for the captured frames. | |
| 341 | |
| 342 Returns: | |
| 343 (string): The output from the frame_analyzer. | |
| 344 """ | 307 """ |
| 345 path_to_analyzer = os.path.join(self.BrowserPath(), 'frame_analyzer') | 308 path_to_analyzer = os.path.join(self.BrowserPath(), 'frame_analyzer') |
| 346 path_to_analyzer = os.path.abspath(path_to_analyzer) | 309 path_to_analyzer = os.path.abspath(path_to_analyzer) |
| 347 | |
| 348 path_to_analyzer = self.BinPathForPlatform(path_to_analyzer) | 310 path_to_analyzer = self.BinPathForPlatform(path_to_analyzer) |
| 349 | 311 |
| 350 if not os.path.exists(path_to_analyzer): | 312 path_to_compare_script = os.path.join(pyauto_paths.GetThirdPartyDir(), |
| 351 raise webrtc_test_base.MissingRequiredBinaryException( | 313 'webrtc', 'tools', |
| 352 'Could not locate frame_analyzer! Did you build the ' | 314 'compare_videos.py') |
| 353 'webrtc_test_tools target?') | 315 if not os.path.exists(path_to_compare_script): |
| 316 raise MissingRequiredToolException('Cannot find the script at %s' % | |
| 317 path_to_compare_script) | |
| 318 python_interp = sys.executable | |
| 319 cmd = [ | |
| 320 python_interp, | |
| 321 path_to_compare_script, | |
| 322 '--ref_video=%s' % reference_video_filename, | |
| 323 '--test_video=%s' % captured_video_filename, | |
| 324 '--frame_analyzer=%s' % path_to_analyzer, | |
| 325 '--yuv_frame_width=%d' % width, | |
| 326 '--yuv_frame_height=%d' % height, | |
| 327 '--stats_file=%s' % stats_filename, | |
| 328 ] | |
| 329 print 'Start command: ', ' '.join(cmd) | |
| 354 | 330 |
| 355 start_cmd = [path_to_analyzer, '--reference_file=%s' % reference_video_file, | 331 compare_videos = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
| 356 '--test_file=%s' % captured_video_file, | 332 stderr=subprocess.PIPE) |
| 357 '--stats_file=%s' % stats_file, | 333 output, error = compare_videos.communicate() |
| 358 '--width=%d' % width, '--height=%d' % height] | 334 if compare_videos.returncode != 0: |
| 359 print 'Start command: ', ' '.join(start_cmd) | 335 raise FailedToRunToolException('Failed to run compare videos script!') |
| 360 | 336 |
| 361 frame_analyzer = subprocess.Popen(start_cmd, stdout=subprocess.PIPE, | |
| 362 stderr=subprocess.PIPE) | |
| 363 output, error = frame_analyzer.communicate() | |
| 364 if error: | |
| 365 print 'Error: ', error | |
| 366 return 'BSTATS undef undef; ESTATS' | |
| 367 return output | 337 return output |
| 368 | 338 |
| 369 def _ProcessFramesCountOutput(self, output): | 339 def _ProcessFramesCountOutput(self, output): |
| 370 """Processes the analyzer output for the different frame counts. | 340 """Processes the analyzer output for the different frame counts. |
| 371 | 341 |
| 372 The frame analyzer outputs additional information about the number of unique | 342 The frame analyzer outputs additional information about the number of unique |
| 373 frames captured, The max number of repeated frames in a sequence and the | 343 frames captured, The max number of repeated frames in a sequence and the |
| 374 max number of skipped frames. These values are then written to the Perf | 344 max number of skipped frames. These values are then written to the Perf |
| 375 Graph. (Note: Some of the repeated or skipped frames will probably be due to | 345 Graph. (Note: Some of the repeated or skipped frames will probably be due to |
| 376 the imperfection of JavaScript timers.) | 346 the imperfection of JavaScript timers.) |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 423 entry = item.split(' ') | 393 entry = item.split(' ') |
| 424 psnr.append(float(entry[0])) | 394 psnr.append(float(entry[0])) |
| 425 ssim.append(float(entry[1])) | 395 ssim.append(float(entry[1])) |
| 426 | 396 |
| 427 pyauto_utils.PrintPerfResult('PSNR', 'VGA', psnr, '') | 397 pyauto_utils.PrintPerfResult('PSNR', 'VGA', psnr, '') |
| 428 pyauto_utils.PrintPerfResult('SSIM', 'VGA', ssim, '') | 398 pyauto_utils.PrintPerfResult('SSIM', 'VGA', ssim, '') |
| 429 | 399 |
| 430 | 400 |
| 431 if __name__ == '__main__': | 401 if __name__ == '__main__': |
| 432 pyauto_functional.Main() | 402 pyauto_functional.Main() |
| OLD | NEW |