Agora-Flutter-SDK icon indicating copy to clipboard operation
Agora-Flutter-SDK copied to clipboard

Document how to switch audio input source

Open tp opened this issue 3 years ago • 4 comments

Is your feature request related to a problem? Please describe.

When using e.g. a bluetooth headset in an ongoing call, and then turning it off, it does not automatically switch to e.g. the MacBooks internal microphone.

I tried to manually switch to another audio input device, but no sound is ever transmitted to other participants.

Describe the solution you'd like

Ideally Agora would automatically switch to the next best input. Alternative please describe the steps the code would need to make in order to select another audio source.

Describe alternatives you've considered

I tried the following, which prints that it switched to my display's internal microphone, but I would never hear anything in the receiving tab.


if (audioState == rtc.AudioLocalState.Failed) {
            // This case is entered when e.g. an external headset is shut off, in which case we need to use another audio device

            void switchToFirstAudioRecordingDevice() async {
              final audioDevices =
                  await engine.deviceManager.enumerateAudioRecordingDevices();

              print(audioDevices);

              if (audioDevices.isNotEmpty) {
                await engine.muteLocalAudioStream(true);
                await engine.deviceManager.setAudioRecordingDevice(
                  audioDevices.first.deviceId,
                );
                await engine.deviceManager.setAudioRecordingDeviceMute(false);

                print('value.enableMicrophone');
                print(value.enableMicrophone);
                if (value.enableMicrophone) {
                  print('enabling mic after switch');
                  await engine.muteLocalAudioStream(false);
                }

                print(
                  '[Agora Engine] switched to device ${audioDevices.first.deviceName}',
                );
              }
            }

            switchToFirstAudioRecordingDevice();
          }

tp avatar Mar 16 '22 19:03 tp

Which device is best should be decided by the business, I think you can try to remove the await engine.muteLocalAudioStream(true), and call await engine.deviceManager.setAudioRecordingDevice directly.

littleGnAl avatar Mar 18 '22 09:03 littleGnAl

Which device is best should be decided by the business, I think you can try to remove the await engine.muteLocalAudioStream(true), and call await engine.deviceManager.setAudioRecordingDevice directly.

The engine.muteLocalAudioStream(true) (and the optional unmuted a few lines further down), was only done in order to restore the previous user settings (whether they enabled the microphone or not). But I suppose we can simplify it and just set it once after the switch is done.

Generally though the code above does not work. That's my problem with it 😅Even though it prints that it switched to the other microphone, I can't hear myself from another participant / tab 🤔

tp avatar Mar 20 '22 08:03 tp

I think you can try to remove the call of engine.muteLocalAudioStream(true) to see if work or not

littleGnAl avatar Mar 22 '22 06:03 littleGnAl

I removed the muteLocalAudioStream, and then also added similar code to switch the playback device as well (else I thought maybe the recordings just end up in a void), but I still can't exchange any audio with other participants after turning the headphones off…

The following code seems to (by the prints) to switch both the playback and recording devices, but in the end no sound / recording comes out:

            Future<void> ensureAudioPlaybackIsConnected() async {
              bool foundPlaybackDevice = false;

              try {
                final playbackDeviceId =
                    await engine.deviceManager.getAudioPlaybackDevice();

                print('playbackDeviceId: $playbackDeviceId');

                foundPlaybackDevice =
                    playbackDeviceId?.trim().isNotEmpty == true;
              } catch (e) {
                // ignore, error expected when there is no device
              }

              print('foundPlaybackDevice: $foundPlaybackDevice');

              if (!foundPlaybackDevice) {
                final playbackDevices =
                    await engine.deviceManager.enumerateAudioPlaybackDevices();

                if (playbackDevices.isNotEmpty) {
                  final newDevice = playbackDevices.first;
                  await engine.deviceManager.setAudioPlaybackDevice(
                    newDevice.deviceId,
                  );

                  await engine.deviceManager.setAudioPlaybackDeviceMute(false);

                  print(
                    '[Agora Engine] switched to playback device ${newDevice.deviceName}',
                  );

                  print('playback');
                  print(await engine.deviceManager.getAudioPlaybackDevice());
                }
              }
            }

            Future<void> ensureAudioRecordingIsConnected() async {
              bool foundRecordingDevice = false;

              try {
                final recordingDeviceId =
                    await engine.deviceManager.getAudioRecordingDevice();

                foundRecordingDevice =
                    recordingDeviceId?.trim().isNotEmpty == true;
              } catch (e) {
                // ignore, error expected when there is no device
              }

              print('foundRecordingDevice: $foundRecordingDevice');

              if (!foundRecordingDevice) {
                final audioDevices =
                    await engine.deviceManager.enumerateAudioRecordingDevices();

                print(
                  'Available audio devices: ${audioDevices.map((d) => d.deviceName)}',
                );

                if (audioDevices.isNotEmpty) {
                  final newDevice = audioDevices.first;

                  await engine.deviceManager.setAudioRecordingDevice(
                    newDevice.deviceId,
                  );

                  await engine.deviceManager.setAudioRecordingDeviceMute(false);

                  await engine.enableLocalAudio(true);

                  print('value.enableMicrophone: ${value.enableMicrophone}');

                  await engine.muteLocalAudioStream(!value.enableMicrophone);

                  print(
                    '[Agora Engine] switched to recording device ${newDevice.deviceName}',
                  );

                  print('recording');
                  print(await engine.deviceManager.getAudioRecordingDevice());
                }
              }
            }

            ensureAudioRecordingIsConnected().then(
              (_) => ensureAudioPlaybackIsConnected(),
            );

The only suspicious thing I saw is that the getAudioPlaybackDevice / getAudioRecordingDevice just says default, whereas I would've expected some device-specific identifier (as it fell back to an external monitor).

tp avatar Mar 22 '22 08:03 tp

If you still face this problem, please try upgrading the latest version of agora_rtc_engine to see if it works or not.

littleGnAl avatar Dec 27 '23 02:12 littleGnAl

is there a way to getRecordingDevice() and change the audio input device for phones???

nati000 avatar Jan 01 '24 08:01 nati000

Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. If you find this problem please file a new issue with the same description, what happens, logs and the output. All system setups can be slightly different so it's always better to open new issues and reference the related ones. Thanks for your contribution.

github-actions[bot] avatar Jan 10 '24 03:01 github-actions[bot]

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please raise a new issue.

github-actions[bot] avatar Jan 17 '24 04:01 github-actions[bot]