admin管理员组

文章数量:1313347

We have a flow in our app where we allow someone to be speaking (microphone) to a bot and the bot is talking back via audio.

It works fine when there is only one device, but if the user wants to use airpods we are having problems. On iOS (Safari, and Chrome) when using wired/Bluetooth headphones that include a microphone here is what we experience:

  1. Before recording: Audio playback works correctly through headphones.
  2. After recording + permissions being granted the audio output switches to the device speakers, even though the headphones are still connected.

I created a very simple page to reproduce the issue. I did find a bug that says it's been fixed but its clearly no .cgi?id=196539

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Microphone Recorder</title>
</head>
<body>
    <h1>Microphone Recorder</h1>
    <button id="startBtn">Start Recording</button>
    <button id="stopBtn" disabled>Stop Recording</button>
       <audio id="audioPlayback" controls src="/[email protected]/media/Justice_Genesis_16bit_trim_mono_y6iHYTjEyKU.wav" playsinline></audio>


    <script>
        let mediaRecorder;
        let audioChunks = [];

        document.getElementById('startBtn').addEventListener('click', async () => {
            try {
                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                mediaRecorder = new MediaRecorder(stream);
                audioChunks = [];

                mediaRecorder.ondataavailable = event => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                    }
                };

                mediaRecorder.onstop = () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                    const audioUrl = URL.createObjectURL(audioBlob);
                    document.getElementById('audioPlayback').src = audioUrl;
                };

                mediaRecorder.start();
                document.getElementById('startBtn').disabled = true;
                document.getElementById('stopBtn').disabled = false;
            } catch (error) {
                console.error('Error accessing microphone:', error);
            }
        });

        document.getElementById('stopBtn').addEventListener('click', () => {
            mediaRecorder.stop();
            document.getElementById('startBtn').disabled = false;
            document.getElementById('stopBtn').disabled = true;
        });
    </script>
</body>
</html>


Again the steps to reproduce:

  1. Connect headhpones
  2. Start playing audio
  3. Click start recording

Notice: sound changes from headphones to speakers. This works fine when using Chrome on my laptop.

We have a flow in our app where we allow someone to be speaking (microphone) to a bot and the bot is talking back via audio.

It works fine when there is only one device, but if the user wants to use airpods we are having problems. On iOS (Safari, and Chrome) when using wired/Bluetooth headphones that include a microphone here is what we experience:

  1. Before recording: Audio playback works correctly through headphones.
  2. After recording + permissions being granted the audio output switches to the device speakers, even though the headphones are still connected.

I created a very simple page to reproduce the issue. I did find a bug that says it's been fixed but its clearly no https://bugs.webkit./show_bug.cgi?id=196539

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Microphone Recorder</title>
</head>
<body>
    <h1>Microphone Recorder</h1>
    <button id="startBtn">Start Recording</button>
    <button id="stopBtn" disabled>Stop Recording</button>
       <audio id="audioPlayback" controls src="https://cdn.jsdelivr/npm/[email protected]/media/Justice_Genesis_16bit_trim_mono_y6iHYTjEyKU.wav" playsinline></audio>


    <script>
        let mediaRecorder;
        let audioChunks = [];

        document.getElementById('startBtn').addEventListener('click', async () => {
            try {
                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                mediaRecorder = new MediaRecorder(stream);
                audioChunks = [];

                mediaRecorder.ondataavailable = event => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                    }
                };

                mediaRecorder.onstop = () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                    const audioUrl = URL.createObjectURL(audioBlob);
                    document.getElementById('audioPlayback').src = audioUrl;
                };

                mediaRecorder.start();
                document.getElementById('startBtn').disabled = true;
                document.getElementById('stopBtn').disabled = false;
            } catch (error) {
                console.error('Error accessing microphone:', error);
            }
        });

        document.getElementById('stopBtn').addEventListener('click', () => {
            mediaRecorder.stop();
            document.getElementById('startBtn').disabled = false;
            document.getElementById('stopBtn').disabled = true;
        });
    </script>
</body>
</html>


Again the steps to reproduce:

  1. Connect headhpones
  2. Start playing audio
  3. Click start recording

Notice: sound changes from headphones to speakers. This works fine when using Chrome on my laptop.

Share Improve this question asked Jan 30 at 20:19 NixNix 58.6k31 gold badges153 silver badges204 bronze badges 1
  • 1 Hey, I have the exact same issue, no workaround yet. I found these two webkit bugs that you may want to keep an eye on. --- Just reported Nov 2024, seems to match 100%: bugs.webkit./show_bug.cgi?id=282939 --- More on the WebRTC side, worked on for years, with recent user reports from just a few days ago: bugs.webkit./show_bug.cgi?id=211192#c24 – Andrew Commented Feb 4 at 0:03
Add a comment  | 

1 Answer 1

Reset to default 2

I have found a workaround! It uses the newish AudioSession API to kick iOS into rerouting properly.

In addition to the behavior in the original question, I'd like to also add that if you use a wired headset+mic, sometimes the iphone mic will still be used even though output is routed through the headphones! Very confusing.

I found a workaround for all scenarios though:

  • set navigator.audioSession.type = 'auto' (just to reset to defaults)
  • call getUserMedia({audio: true}), this will return the iphone mic and likely reroute audio output to the handset speakers
  • set navigator.audioSession.type = 'play-and-record'

This seems to "kick" iOS into rerouting audio. Now the external device's mic and speakers (headphones) will be used!

NOTE: if you attempt to set play-and-record before calling getUserMedia, it seems like you have a 50/50 chance of still getting the iPhone mic and not the headphones mic.

Sometimes, an additional problem will manifest after you have closed the mic stream/tracks: the audioSession will remain in play-and-record, resulting in degraded audio output quality. The solution is to always (after releasing the mic):

  • set navigator.audioSession.type = 'playback'
  • set navigator.audioSession.type = 'auto' (immediately, no need for a delay)

This will again "kick" iOS into rerouting / resetting audio. You'll be back in hi fidelity!

I hope this helps!

I have posted similar instructions on the newish open bug report that seems to accurately describe the issue: https://bugs.webkit./show_bug.cgi?id=282939

本文标签: