1 # Copyright 2017 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import os 7 8 from autotest_lib.client.bin import test 9 from autotest_lib.client.bin import utils 10 from autotest_lib.client.common_lib import error 11 from autotest_lib.client.common_lib.cros import chrome 12 from autotest_lib.client.cros.video import helper_logger 13 from autotest_lib.client.cros.audio import audio_helper 14 from autotest_lib.client.cros.audio import cras_utils 15 16 # Suppress the media Permission Dialog. 17 EXTRA_BROWSER_ARGS = [ 18 '--use-fake-ui-for-media-stream', # Suppress the Permission Dialog 19 '--use-fake-device-for-media-stream' # Use fake audio & video 20 ] 21 22 AUDIO_LOOPBACK_PAGE = 'audio_loopback.html' 23 24 # The test's runtime. 25 TEST_RUNTIME_SECONDS = 10 26 27 # Number of peer connections to use. 28 NUM_PEER_CONNECTIONS = 1 29 30 # Polling timeout. 31 TIMEOUT = TEST_RUNTIME_SECONDS + 10 32 33 34 class audio_WebRtcAudioLoopback(test.test): 35 """Tests a WebRTC call with a fake audio.""" 36 version = 1 37 38 def start_test(self, cr, recorded_file): 39 """Opens the WebRTC audio loopback page and records audio output. 40 41 @param cr: Autotest Chrome instance. 42 @param recorded_file: File to recorder the audio output to. 43 """ 44 cr.browser.platform.SetHTTPServerDirectories(self.bindir) 45 46 self.tab = cr.browser.tabs[0] 47 self.tab.Navigate(cr.browser.platform.http_server.UrlOf( 48 os.path.join(self.bindir, AUDIO_LOOPBACK_PAGE))) 49 self.tab.WaitForDocumentReadyStateToBeComplete() 50 self.tab.EvaluateJavaScript( 51 "run(%d, %d)" % (TEST_RUNTIME_SECONDS, NUM_PEER_CONNECTIONS)) 52 self.wait_for_active_stream_count(1) 53 cras_utils.capture(recorded_file, duration=TEST_RUNTIME_SECONDS) 54 55 def wait_test_completed(self, timeout_secs): 56 """Waits until the test is done. 57 58 @param timeout_secs Max time to wait in seconds. 59 60 @raises TestError on timeout, or javascript eval fails. 61 """ 62 def _test_done(): 63 status = self.tab.EvaluateJavaScript('testRunner.getStatus()') 64 logging.info(status) 65 return status == 'ok-done' 66 67 utils.poll_for_condition( 68 _test_done, timeout=timeout_secs, sleep_interval=1, 69 desc='audio.html reports itself as finished') 70 71 @staticmethod 72 def wait_for_active_stream_count(expected_count): 73 """Waits for the expected number of active streams. 74 75 @param expected_count: expected count of active streams. 76 """ 77 utils.poll_for_condition( 78 lambda: cras_utils.get_active_stream_count() == expected_count, 79 exception=error.TestError( 80 'Timeout waiting active stream count to become %d' % 81 expected_count)) 82 83 @helper_logger.video_log_wrapper 84 def run_once(self): 85 """Runs the audio_WebRtcAudioLoopback test.""" 86 # Record a sample of "silence" to use as a noise profile. 87 noise_file = os.path.join(self.resultsdir, 'cras_noise.wav') 88 cras_utils.capture(noise_file, duration=1) 89 90 # Create a file for the audio recording. 91 recorded_file = os.path.join(self.resultsdir, 'cras_recorded.wav') 92 93 self.wait_for_active_stream_count(0) 94 with chrome.Chrome(extra_browser_args=EXTRA_BROWSER_ARGS +\ 95 [helper_logger.chrome_vmodule_flag()], 96 init_network_controller=True) as cr: 97 self.start_test(cr, recorded_file) 98 self.wait_test_completed(TIMEOUT) 99 self.print_result(recorded_file, noise_file) 100 101 def print_result(self, recorded_file, noise_file): 102 """Prints results unless status is different from ok-done. 103 104 @raises TestError if the test failed outright. 105 @param recorded_file: File to recorder the audio output to. 106 @param noise_file: Noise recording, used for comparison. 107 """ 108 status = self.tab.EvaluateJavaScript('testRunner.getStatus()') 109 if status != 'ok-done': 110 raise error.TestFail('Failed: %s' % status) 111 112 results = self.tab.EvaluateJavaScript('testRunner.getResults()') 113 logging.info('runTimeSeconds: %.2f', results['runTimeSeconds']) 114 115 rms_value = audio_helper.reduce_noise_and_get_rms( 116 recorded_file, noise_file)[0] 117 logging.info('rms_value: %f', rms_value) 118 self.output_perf_value( 119 description='rms_value', 120 value=rms_value, 121 units='', higher_is_better=True) 122 123