[autotest] audio_extension_handler: Use new chrome.audio API
Use new API for the changes in crbug.com/673392.
- Replace getInfo with getDevices
- Replace setProperties with setMute to mute system.
BUG=chromium:699170, chromium:699007
TEST=test_that --fast --debug --board=auron_paine
--args="chameleon_host=chromeos2-row10-rack9-host9-chameleon.cros"
chromeos2-row10-rack9-host9.cros audio_AudioBasicHDMI
TEST=check multimedia_xmlrpc_server audio facade.
>>> s.browser.start_default_chrome()
True
>>> s.audio.set_chrome_active_node_type('INTERNAL_SPEAKER', 'INTERNAL_MIC')
>>> s.audio.set_chrome_active_node_type('HEADPHONE', 'MIC')
>>> s.audio.get_audio_devices()
[{'deviceName': 'Post Mix Pre DSP Loopback', 'displayName': 'Post Mix
Pre DSP Loopback', 'level': 75, 'stableDeviceId': '2315562897',
'streamType': 'INPUT', 'deviceType': 'POST_MIX_LOOPBACK', 'id':
'12884901888', 'isActive': False}, {'deviceName': 'Post DSP Loopback',
'displayName': 'Post DSP Loopback', 'level': 75, 'stableDeviceId':
'2356475750', 'streamType': 'INPUT', 'deviceType': 'POST_DSP_LOOPBACK',
'id': '17179869184', 'isActive': False}, {'deviceName': 'sklnau8825adi:
:0,0', 'displayName': 'Headphone', 'level': 75, 'stableDeviceId':
'2660450915', 'streamType': 'OUTPUT', 'deviceType': 'HEADPHONE', 'id':
'21474836480', 'isActive': True}, {'deviceName': 'sklnau8825adi: :0,0',
'displayName': 'Speaker', 'level': 75, 'stableDeviceId': '1923447123',
'streamType': 'OUTPUT', 'deviceType': 'INTERNAL_SPEAKER', 'id':
'21474836481', 'isActive': False}, {'deviceName': 'sklnau8825adi: :0,1',
'displayName': 'Mic', 'level': 75, 'stableDeviceId': '1638577850',
'streamType': 'INPUT', 'deviceType': 'MIC', 'id': '25769803776',
'isActive': True}, {'deviceName': 'sklnau8825adi: :0,1', 'displayName':
'Internal Mic', 'level': 75, 'stableDeviceId': '1289939621',
'streamType': 'INPUT', 'deviceType': 'INTERNAL_MIC', 'id':
'25769803777', 'isActive': False}]
>>> s.audio.get_chrome_active_volume_mute()
[75, False]
>>> s.set_chrome_active_volume(50)
>>> s.audio.set_chrome_active_volume(50)
>>> s.audio.get_chrome_active_volume_mute()
[50, False]
>>> s.audio.set_chrome_mute(True)
>>> s.audio.get_chrome_active_volume_mute()
[50, True]
Change-Id: If6ea4db43df9f3ed32100f10dc7d1b1219831f2e
Reviewed-on: https://chromium-review.googlesource.com/450833
Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Cheng-Yi Chiang <cychiang@chromium.org>
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
Reviewed-by: Hsu Wei-Cheng <mojahsu@chromium.org>
(cherry picked from commit 402ddbdc1ec1151be2f47f8eaa1c6614196a70f9)
Reviewed-on: https://chromium-review.googlesource.com/471186
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
Commit-Queue: Cheng-Yi Chiang <cychiang@chromium.org>
diff --git a/client/cros/multimedia/audio_extension_handler.py b/client/cros/multimedia/audio_extension_handler.py
index 3d8d21e..feef128 100644
--- a/client/cros/multimedia/audio_extension_handler.py
+++ b/client/cros/multimedia/audio_extension_handler.py
@@ -10,10 +10,14 @@
from autotest_lib.client.cros.multimedia import facade_resource
class AudioExtensionHandlerError(Exception):
+ """Class for exceptions thrown from the AudioExtensionHandler"""
pass
class AudioExtensionHandler(object):
+ """Wrapper around test extension that uses chrome.audio API to get audio
+ device information
+ """
def __init__(self, extension):
"""Initializes an AudioExtensionHandler.
@@ -39,34 +43,44 @@
@facade_resource.retry_chrome_call
- def get_audio_info(self):
- """Gets the audio info from Chrome audio API.
+ def get_audio_devices(self, device_filter=None):
+ """Gets the audio device info from Chrome audio API.
- @returns: An array of [outputInfo, inputInfo].
- outputInfo is an array of output node info dicts. Each dict
+ @param device_filter: Filter for returned device nodes.
+ An optional dict that can have the following properties:
+ string array streamTypes
+ Restricts stream types that returned devices can have.
+ It should contain "INPUT" for result to include input
+ devices, and "OUTPUT" for results to include output devices.
+ If not set, returned devices will not be filtered by the
+ stream type.
+
+ boolean isActive
+ If true, only active devices will be included in the result.
+ If false, only inactive devices will be included in the
+ result.
+
+ The filter param defaults to {}, requests all available audio
+ devices.
+
+ @returns: An array of audioDeviceInfo.
+ Each audioDeviceInfo dict
contains these key-value pairs:
string id
- The unique identifier of the audio output device.
+ The unique identifier of the audio device.
- string name
+ string stableDeviceId
+ The stable identifier of the audio device.
+
+ string streamType
+ "INPUT" if the device is an input audio device,
+ "OUTPUT" if the device is an output audio device.
+
+ string displayName
The user-friendly name (e.g. "Bose Amplifier").
- boolean isActive
- True if this is the current active device.
-
- boolean isMuted
- True if this is muted.
-
- double volume
- The output volume ranging from 0.0 to 100.0.
-
- inputInfo is an arrya of input node info dicts. Each dict
- contains these key-value pairs:
- string id
- The unique identifier of the audio input device.
-
- string name
- The user-friendly name (e.g. "USB Microphone").
+ string deviceName
+ The devuce name
boolean isActive
True if this is the current active device.
@@ -74,57 +88,55 @@
boolean isMuted
True if this is muted.
- double gain
- The input gain ranging from 0.0 to 100.0.
+ long level
+ The output volume or input gain.
"""
- self._extension.ExecuteJavaScript('window.__audio_info = null;')
+ def filter_to_str(device_filter):
+ """Converts python dict device filter to JS object string.
+
+ @param device_filter: Device filter dict.
+
+ @returns: Device filter as a srting representation of a
+ JavaScript object.
+
+ """
+ return str(device_filter or {}).replace('True', 'true').replace(
+ 'False', 'false')
+
+ self._extension.ExecuteJavaScript('window.__audio_devices = null;')
self._extension.ExecuteJavaScript(
- "chrome.audio.getInfo(function(outputInfo, inputInfo) {"
- "window.__audio_info = [outputInfo, inputInfo];})")
+ "chrome.audio.getDevices(%s, function(devices) {"
+ "window.__audio_devices = devices;})"
+ % filter_to_str(device_filter))
utils.wait_for_value(
lambda: (self._extension.EvaluateJavaScript(
- "window.__audio_info") != None),
+ "window.__audio_devices") != None),
expected_value=True)
- return self._extension.EvaluateJavaScript("window.__audio_info")
+ return self._extension.EvaluateJavaScript("window.__audio_devices")
- def _get_active_id(self):
- """Gets active output and input node id.
-
- Assume there is only one active output node and one active input node.
-
- @returns: (output_id, input_id) where output_id and input_id are
- strings for active node id.
-
- """
- output_nodes, input_nodes = self.get_audio_info()
-
- return (self._get_active_id_from_nodes(output_nodes),
- self._get_active_id_from_nodes(input_nodes))
-
-
- def _get_active_id_from_nodes(self, nodes):
- """Gets active node id from nodes.
+ def _get_active_id_for_stream_type(self, stream_type):
+ """Gets active node id of the specified stream type.
Assume there is only one active node.
- @param nodes: A list of input/output nodes got from get_audio_info().
+ @param stream_type: 'INPUT' to get the active input device,
+ 'OUTPUT' to get the active output device.
- @returns: node['id'] where node['isActive'] is True.
+ @returns: A string for the active device id.
@raises: AudioExtensionHandlerError if active id is not unique.
"""
- active_ids = [x['id'] for x in nodes if x['isActive']]
- if len(active_ids) != 1:
+ nodes = self.get_audio_devices(
+ {'streamTypes': [stream_type], 'isActive': True})
+ if len(nodes) != 1:
logging.error(
'Node info contains multiple active nodes: %s', nodes)
- raise AudioExtensionHandlerError(
- 'Active id should be unique')
+ raise AudioExtensionHandlerError('Active id should be unique')
- return active_ids[0]
-
+ return nodes[0]['id']
@facade_resource.retry_chrome_call
@@ -136,20 +148,20 @@
@param volume: Volume to set (0~100).
"""
- output_id, _ = self._get_active_id()
+ output_id = self._get_active_id_for_stream_type('OUTPUT')
logging.debug('output_id: %s', output_id)
- self._extension.ExecuteJavaScript('window.__set_volume_done = null;')
+ self.set_mute(False)
+ self._extension.ExecuteJavaScript('window.__set_volume_done = null;')
self._extension.ExecuteJavaScript(
"""
chrome.audio.setProperties(
'%s',
- {isMuted: false, volume: %s},
+ {level: %s},
function() {window.__set_volume_done = true;});
"""
% (output_id, volume))
-
utils.wait_for_value(
lambda: (self._extension.EvaluateJavaScript(
"window.__set_volume_done") != None),
@@ -158,26 +170,22 @@
@facade_resource.retry_chrome_call
def set_mute(self, mute):
- """Mutes the active audio output using chrome.audio API.
+ """Mutes the audio output using chrome.audio API.
@param mute: True to mute. False otherwise.
"""
- output_id, _ = self._get_active_id()
- logging.debug('output_id: %s', output_id)
-
is_muted_string = 'true' if mute else 'false'
self._extension.ExecuteJavaScript('window.__set_mute_done = null;')
self._extension.ExecuteJavaScript(
"""
- chrome.audio.setProperties(
- '%s',
- {isMuted: %s},
+ chrome.audio.setMute(
+ 'OUTPUT', %s,
function() {window.__set_mute_done = true;});
"""
- % (output_id, is_muted_string))
+ % (is_muted_string))
utils.wait_for_value(
lambda: (self._extension.EvaluateJavaScript(
@@ -186,6 +194,24 @@
@facade_resource.retry_chrome_call
+ def get_mute(self):
+ """Determines whether audio output is muted.
+
+ @returns Whether audio output is muted.
+
+ """
+ self._extension.ExecuteJavaScript('window.__output_muted = null;')
+ self._extension.ExecuteJavaScript(
+ "chrome.audio.getMute('OUTPUT', function(isMute) {"
+ "window.__output_muted = isMute;})")
+ utils.wait_for_value(
+ lambda: (self._extension.EvaluateJavaScript(
+ "window.__output_muted") != None),
+ expected_value=True)
+ return self._extension.EvaluateJavaScript("window.__output_muted")
+
+
+ @facade_resource.retry_chrome_call
def get_active_volume_mute(self):
"""Gets the volume state of active audio output using chrome.audio API.
@@ -193,11 +219,13 @@
is True if node is muted, False otherwise.
"""
- output_nodes, _ = self.get_audio_info()
- active_id = self._get_active_id_from_nodes(output_nodes)
- for node in output_nodes:
- if node['id'] == active_id:
- return (node['volume'], node['isMuted'])
+ nodes = self.get_audio_devices(
+ {'streamTypes': ['OUTPUT'], 'isActive': True})
+ if len(nodes) != 1:
+ logging.error('Node info contains multiple active nodes: %s', nodes)
+ raise AudioExtensionHandlerError('Active id should be unique')
+
+ return (nodes[0]['level'], self.get_mute())
@facade_resource.retry_chrome_call
@@ -207,14 +235,27 @@
The current active node will be disabled first if the new active node
is different from the current one.
- @param node_id: The node id obtained from cras_utils.get_cras_nodes.
+ @param node_id: Node id obtained from cras_utils.get_cras_nodes.
Chrome.audio also uses this id to specify input/output
nodes.
+ Note that node id returned by cras_utils.get_cras_nodes
+ is a number, while chrome.audio API expects a string.
@raises AudioExtensionHandlerError if there is no such id.
"""
- if node_id in self._get_active_id():
+ nodes = self.get_audio_devices({})
+ target_node = None
+ for node in nodes:
+ if node['id'] == str(node_id):
+ target_node = node
+ break
+
+ if not target_node:
+ logging.error('Node %s not found.', node_id)
+ raise AudioExtensionHandlerError('Node id not found')
+
+ if target_node['isActive']:
logging.debug('Node %s is already active.', node_id)
return
@@ -222,13 +263,15 @@
self._extension.ExecuteJavaScript('window.__set_active_done = null;')
+ is_input = target_node['streamType'] == 'INPUT'
+ stream_type = 'input' if is_input else 'output'
self._extension.ExecuteJavaScript(
"""
chrome.audio.setActiveDevices(
- ['%s'],
+ {'%s': ['%s']},
function() {window.__set_active_done = true;});
"""
- % (node_id))
+ % (stream_type, node_id))
utils.wait_for_value(
lambda: (self._extension.EvaluateJavaScript(
diff --git a/client/cros/multimedia/audio_facade_native.py b/client/cros/multimedia/audio_facade_native.py
index e496e6f..a894b7e 100644
--- a/client/cros/multimedia/audio_facade_native.py
+++ b/client/cros/multimedia/audio_facade_native.py
@@ -82,13 +82,13 @@
return self._loaded_extension_handler
- def get_audio_info(self):
- """Returns the audio info from chrome.audio API.
+ def get_audio_devices(self):
+ """Returns the audio devices from chrome.audio API.
- @returns: Checks docstring of get_audio_info of AudioExtensionHandler.
+ @returns: Checks docstring of get_audio_devices of AudioExtensionHandler.
"""
- return self._extension_handler.get_audio_info()
+ return self._extension_handler.get_audio_devices()
def set_chrome_active_volume(self, volume):