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 |