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