1# Copyright 2016 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"""This is a server side WebRTC audio test using the Chameleon board."""
6
7import logging
8import os
9import time
10
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.cros.audio import audio_test_data
13from autotest_lib.client.cros.chameleon import audio_test_utils
14from autotest_lib.client.cros.chameleon import chameleon_audio_helper
15from autotest_lib.client.cros.chameleon import chameleon_audio_ids
16from autotest_lib.client.cros.multimedia import webrtc_utils
17from autotest_lib.server.cros.audio import audio_test
18from autotest_lib.server.cros.multimedia import remote_facade_factory
19
20
21class audio_AudioWebRTCLoopback(audio_test.AudioTest):
22    """Server side WebRTC loopback audio test.
23
24    This test talks to a Chameleon board and a Cros device to verify
25    WebRTC audio function of the Cros device.
26    A sine tone is played to Cros device from Chameleon USB.
27    Through AppRTC loopback page, the sine tone is played to Chameleon
28    LineIn from Cros device headphone.
29    Using USB as audio source because it can provide completely correct
30    data to loopback. This enables the test to do quality verification on
31    headphone.
32
33                  ----------->->->------------
34         USB out |                            | USB in
35           -----------                    --------              AppRTC loopback
36          | Chameleon |                  |  Cros  |  <--------> webpage
37           -----------                    --------
38         Line-In |                            |  Headphone
39                  -----------<-<-<------------
40
41
42    The recorded audio is copied to server side and examined for quality.
43
44    """
45    version = 1
46    RECORD_SECONDS = 10
47    DELAY_AFTER_BINDING_SECONDS = 0.5
48
49    def run_once(self, host, check_quality=False, chrome_block_size=None):
50        """Running basic headphone audio tests.
51
52        @param host: device under test host
53        @param check_quality: flag to check audio quality.
54        @param chrome_block_size: A number to be passed to Chrome
55                                  --audio-buffer-size argument to specify
56                                  block size.
57
58        """
59        if not audio_test_utils.has_headphone(host):
60            logging.info('Skip the test because there is no headphone')
61            return
62
63        golden_file = audio_test_data.SIMPLE_FREQUENCY_LOUD_WAVE_FILE
64        golden_file.generate_file()
65
66        chameleon_board = host.chameleon
67
68        # Checks if a block size is specified for Chrome.
69        extra_browser_args = None
70        if chrome_block_size:
71            extra_browser_args = ['--audio-buffer-size=%d' % chrome_block_size]
72
73        factory = remote_facade_factory.RemoteFacadeFactory(
74                host, results_dir=self.resultsdir,
75                extra_browser_args=extra_browser_args)
76
77        chameleon_board.setup_and_reset(self.outputdir)
78
79        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
80                factory, host)
81
82        headphone = widget_factory.create_widget(
83            chameleon_audio_ids.CrosIds.HEADPHONE)
84        linein = widget_factory.create_widget(
85            chameleon_audio_ids.ChameleonIds.LINEIN)
86        headphone_linein_binder = widget_factory.create_binder(headphone, linein)
87
88        usb_out = widget_factory.create_widget(chameleon_audio_ids.ChameleonIds.USBOUT)
89        usb_in = widget_factory.create_widget(chameleon_audio_ids.CrosIds.USBIN)
90        usb_binder = widget_factory.create_binder(usb_out, usb_in)
91
92        with chameleon_audio_helper.bind_widgets(headphone_linein_binder):
93            with chameleon_audio_helper.bind_widgets(usb_binder):
94                time.sleep(self.DELAY_AFTER_BINDING_SECONDS)
95                audio_facade = factory.create_audio_facade()
96
97                audio_test_utils.dump_cros_audio_logs(
98                        host, audio_facade, self.resultsdir, 'after_binding')
99
100                # Checks headphone and USB nodes are plugged.
101                # Let Chrome select the proper I/O nodes.
102                # Input is USB, output is headphone.
103                audio_test_utils.check_and_set_chrome_active_node_types(
104                        audio_facade=audio_facade,
105                        output_type='HEADPHONE',
106                        input_type='USB')
107
108                logging.info('Setting playback data on Chameleon')
109                usb_out.set_playback_data(golden_file)
110
111                browser_facade = factory.create_browser_facade()
112                apprtc = webrtc_utils.AppRTCController(browser_facade)
113                logging.info('Load AppRTC loopback webpage')
114                apprtc.new_apprtc_loopback_page()
115
116                logging.info('Start recording from Chameleon.')
117                linein.start_recording()
118
119                logging.info('Start playing %s on Cros device',
120                        golden_file.path)
121                usb_out.start_playback()
122
123                time.sleep(self.RECORD_SECONDS)
124
125                linein.stop_recording()
126                logging.info('Stopped recording from Chameleon.')
127
128                audio_test_utils.dump_cros_audio_logs(
129                        host, audio_facade, self.resultsdir, 'after_recording')
130
131                usb_out.stop_playback()
132
133                linein.read_recorded_binary()
134                logging.info('Read recorded binary from Chameleon.')
135
136        golden_file.delete()
137
138        recorded_file = os.path.join(self.resultsdir, "recorded.raw")
139        logging.info('Saving recorded data to %s', recorded_file)
140        linein.save_file(recorded_file)
141
142        diagnostic_path = os.path.join(
143                self.resultsdir,
144                'audio_diagnostics.txt.after_recording')
145        logging.info('Examine diagnostic file at %s', diagnostic_path)
146        diag_warning_msg = audio_test_utils.examine_audio_diagnostics(
147                diagnostic_path)
148        if diag_warning_msg:
149            logging.warning(diag_warning_msg)
150
151        # Raise error.TestFail if there is issue.
152        audio_test_utils.check_recorded_frequency(
153                golden_file, linein, check_artifacts=check_quality)
154