Recently I bought an Apple HomePod mini to control my HomeKit devices such as my electrical plugs and my garage door. The experience is good (as for other assistants) and I can now switch on or off the printer just by speaking… No need to go under my desk where my printer is to manually switch it on!
As I already have a pair of AirPods too, I decided to check if these devices can be use when making WebRTC calls. So, this experience is limited to the Apple addicts :-)
For that article, I used the following devices
The experience of connecting the Homepod to my Mac mini was successful. If the Mac is connected to the same network as the HomePod, it detects your device, and you can select it in the Mac sound panel.
The HomePod is connected using the AirPlay technology. This is great for streaming but here there is a delay of some seconds each time you start or stop your music for example.
But it works great and so the HomePod can be used as an external speaker. For my Mac Mini, this is enough because the internal speaker proposed is great for sound system but not for playing music…
Additionally, I added the HomePod to HomeKit to control my connected plugs and accessories. Everything is working well.
So for entertainment use, it is ok. But what about more professional use, such for telecommuting and video conferencing with colleagues or clients?.
Are the HomePod perfect for these usages too ?
The first showstopper was the lack of support of the output devices and associated setSinkId
API in Safari to be able to have a 100% Apple experience. So, I used Chrome.
Note: As you will see in the following paragraph, as it is an AirPlay device, you need to select in the Mac sound panel for using it. So, it will be used in Safari and Firefox too but not through API.
Using HomePod with WebRTC means being able to use it as an external microphone and as an external speaker.
Let check first, if we could use it as a microphone.
To quickly have the result, I called the API getUserMedia
and checked if the track obtained is from the HomePod or not.
const stream = await navigator.mediaDevices.getUserMedia({audio: true});const audioTrack = stream.getAudioTracks()[0];// MediaStreamTrack {kind: 'audio', id: '1e432156-29cd-4648-921b-67332892c429', label: 'Default - RODE NT-USB (19f7:0003)', enabled: true, muted: false,…}
The Homepod has not been selected. Hum… this probably means that the microphone of the HomePod is not usable for WebRTC.
To confirm, I used the API enumerateDevices
and effectively the HomePod is not listed as an audioinput
device and so can’t be used as a microphone.
Now, let check if the HomePod can be used as an external speaker.
As you may know, at that time of writing and even if the W3C specification on MediaCapture Output is not new (first draft has been written in 2015), only Chrome is able to list the output devices that can be used and proposes to select them using the API setSinkId
.
Firefox’s implementation of setSinkId
is still behind a flag and nothing found with Safari Technology Preview…
Additionally, to that method, this specification introduces a new method selectAudioOutput
for prompting the user to select the specific audio output device he would like to use. But as explained in the specification, the lack of homogenization around the autoplay
is a brake to propose that API. So no implementation exists at this time.
So ok… I will just use the setSinkId
in Chrome…
For the first test, I didn’t select the HomePod as my default speaker in the Mac sound panel. In that case, calling enumerateDevices
didn’t enumerate it.
I have to select it first in the Mac sound panel and only after I called the eumerateDevices
API in Chrome to see if the HomePod is listed
const devices = await navigator.mediaDevices.enumerateDevices();// MediaDeviceInfo {deviceId: '67b8f458a6df...b46', kind: 'audiooutput', label: 'AirPlay (AirPlay)', groupId: '604b960cf...4fa'}
This time, I found it but not exactly with the name I expected to find: AirPlay (Airplay) is the pretty name given to the HomePod… Don’t know what happens if you have more than one HomePod :-)
Just for the information, I connected my Freebox Player (Internet provider multimedia box) and I did the same test. As the result, the Freebox Player is identified as Freebox Player (AirPLay) which is really better. It is a shame that the HomePod is not better identified. Is Chrome doing this on purpose?…
Now, that I have my HomePod listed, the next is to try to switch to that speaker using the API setSinkId
.
const outputSelect = document.querySelector('#outputDevicesSelect');const audioElt = document.querySelector("#audioElt");outputSelect.onchange = async (event) => {try {await audioElt.setSinkId(event.target.value);} catch (err) {// In case of error}}
I succeeded to switch the audio to the HomePod and switch from different speakers to the HomePod and vice versa.
I then tried to play a sound. As it is connected using Airplay, there is a some seconds of delay between the click for starting to play and the moment when I hear the sound. But it works great, and I have no issue to use it.
For testing the HomePod in real situation, I took our worldwide Alcatel-Lucent Enterprise Rainbow solution and made a WebRTC call from my Rainbow Android application (Lenovo tablet) to my Rainbow Desktop application (Mac). I configured the Rainbow Desktop for using the HomePod as the speaker and my Rode device as the microphone.
The experience was excellent. Voice was clear and loud. And no delay between the voice and the video was seen. I was worried about Airplay connectivity, but I didn’t detect any problems.
This result needs perhaps more tries to be confirmed but my test was conclusive, so for me, the HomePod mini can be used as a speaker for having WebRTC calls.
Regarding the Airpods, I should not have any surprises when using them. They should propose to act as microphone and speaker (earpiece).
In order for the browser to detect the Airpods, they should be set in your ears.
Once placed in your ears, they appear in the Mac Sound panel and can be listed by using the API enumerateDevices
.
const devices = await navigator.mediaDevices.enumerateDevices();// InputDeviceInfo {deviceId: 'default', kind: 'audioinput', label: 'AirPods', groupId: 'b067ddb7d3e0a051c5bc320a033ed934e2ab8166a8bda18f3a393e6a221ce862'}// InputDeviceInfo {deviceId: '23cb39500e37d5e5a9d0c5121d465c85a5016918f4be8023bc29ac6bd9d87e1d', kind: 'audioinput', label: 'AirPods', groupId: 'b067ddb7d3e0a051c5bc320a033ed934e2ab8166a8bda18f3a393e6a221ce862'}// MediaDeviceInfo {deviceId: 'default', kind: 'audiooutput', label: 'AirPods', groupId: '0eaa06962d3daf3ede1d2e2eed22b91901ed2b170b5b4be12c68d49ca4bb9c6a'}// MediaDeviceInfo {deviceId: '61b7d914c37b31ac931c57c137ffaee7225702cebe1539a1d7e42b4e227cee2a', kind: 'audiooutput', label: 'AirPods', groupId: '0eaa06962d3daf3ede1d2e2eed22b91901ed2b170b5b4be12c68d49ca4bb9c6a'}
This is the habit in Chrome, the system default device is displayed twice in the list: one time with label equals to default
and a second time with its real deviceId
.
Unlike for the HomePod, this time, the Airpods are clearly identified: no doubt is possible.
But something is strange in Chrome, the groupId
which should be the same when devices belong to the same physical equipment is different here: We have a groupId
for the microphone and a second one for the speaker…
To be sure, I tested using Firefox (by activating the flag setSinkId
in about:config) and Firefox groups the two devices by having a unique groupId. Well done Firefox!
That’s why, after checking that there is no existing issue opened in Chrome, I created a new Chrome issue to check if there is a reason or to fix this bug.
This bug was already detected since Chrome M99: https://bugs.chromium.org/p/chromium/issues/detail?id=1292367.
I made a second test by using the event devicechange
to see if and when the Airpods are detected.
To be honest, I don’t like this event because it only informs the application that something changed at the devices level. It is up to your application then to call enumerateDevices
again and to compare: If there are more devices than before, this means that the user plugs or connects physical equipment. Otherwise, the user has disconnected one.
Not really developers friendly.
Here is a little function I made to compare between the list of devices got before the event devicechange
and after.
let currentListOfDevices = []; // Current list of deviceslet devicesChanged = false; // Devices just changedconst compare = (current, update) => {// Return the number of devices added (positive) or removed (negative) and the devices informationlet result = {count: 0,list: [],};// Same number of devices. We assume that there is no difference.if (current.length === update.length) {return result;}if (current.length > update.length) {// This is the case when devices have been plugged inlet newDevices = current.filter((device) => {return !update.some((elt) => elt.deviceId === device.deviceId);});return {count: newDevices.length,list: newDevices,};}// Here, devices have been unpluggedlet removedDevices = update.filter((device) => {return !current.some((elt) => {return elt.deviceId === device.deviceId;});});return {count: 0 - removedDevices.length,list: removedDevices,};};navigator.mediaDevices.ondevicechange = async (event) => {if(!devicesChanged) {devicesChanged = true;setTimeout(async () => {const devices = await navigator.mediaDevices.enumerateDevices();// diff contains the list of devices added or removedconst diff = compare(currentListOfDevices, devices);// ResetdevicesChanged = false;}, 500);}}
Using that code, I got the following result:
devicechange
is triggered only when the AirPods are put in your ears, not when they are moved out ot the box
devicechange
is triggered as soon as you remove the AirPods from your ears.
To go further, if they are several devices added but with the same groupId
, you can deduce that the user has connected a single physical device (except for the AirPods at this time…).
Note: Using the devicechange
event is not trivial. For example, adding or removing the Airpods in Chrome triggers events 4 times while only once in Firefox and Safari. So in Chrome, be sure to get the devices after the last event, else, the list can be wrong…
Then, I observed a strange thing when connected my AirPods:
Airpods are selected in the Mac sound panel
Calling getUserMedia
without specifying the deviceId
of the AirPods leads to a track obtained from another device…
So, I have to add a constraint on the deviceId
to obtain a track from the AirPods.
const constraints = { audio: { deviceId: { exact: '23cb39500e37d5e5a9d0c5121d465c85a5016918f4be8023bc29ac6bd9d87e1d' }} }; // deviceId of the AirPods got from enumerateDevicesconst stream = await navigator.mediaDevices.getUserMedia(constraints);const audioTrack = stream.getAudioTracks()[0];// MediaStreamTrack {kind: 'audio', id: 'd58e1f88-4835-4258-8e91-53c8b32d1fa5', label: 'AirPods', enabled: true, muted: false, …}
The AirPods work really great in a WebRTC call. I didn’t encounter any issues with echo cancellation. My recipient was hearing me well, without any problems.
I checked which constraints could be applied on the AirPods to see if we can send an audio stereo stream. Calling the API getCapabilities
on the track obtained will reveal the answer
// Target the Airpodsconst constraints = { audio: { deviceId: { exact: '23cb39500e37d5e5a9d0c5121d465c85a5016918f4be8023bc29ac6bd9d87e1d'} }};// Get the streamconst stream = await navigator.mediaDevices.getUserMedia(constraints);// Get the audio track coming from the Airpodsconst track = stream.getAudioTracks()[0];// Get the capabilitiestrack.getCapabilities();// autoGainControl: (2) [true, false]// channelCount: {max: 1, min: 1}// deviceId: "23cb39500e37d5e5a9d0c5121d465c85a5016918f4be8023bc29ac6bd9d87e1d"// echoCancellation: (2) [true, false]// groupId: "ba15099a5a280fccb98205d558a61b1baec70be81b5d73b4d15323127c413310"// latency: {max: 0.170666, min: 0.005333}// noiseSuppression: (2) [true, false]// sampleRate: {max: 48000, min: 24000}// sampleSize: {max: 16, min: 16}
And the answer is no. AirPods microphone captures in mono. At home, I tested several microphones: Only my external Rode NT-USB microphone was able to support capturing in stereo.
The HomePod and the AirPods are great products, they can be used in many situations and are for sure well integrated in the Apple ecosystem.
But for my professional usage, I will continue to use my external physical device for conferencing that integrates a microphone and a speaker: Background noise is well removed, I have no problem when the children are making noise, and I can still hear the front doorbell :-)
This let me doing conferencing comfortably!