In this tutorial, you'll learn how to allow your users to change their media devices, such as changing to a different camera or microphone, in a video conference.
Prerequisites
This tutorial requires the SFU connection app or any apps you have built earlier on top of it.
Change Audio Input Devices
We'll use LocalMedia.GetAudioSourceInputs to get a list of audio input devices, and then use LocalMedia.ChangeAudioSourceInput to change the audio input devices.
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.cs file:
//Switch audio input device.
public Future<SourceInput[]> GetAudioInputs()
{
return LocalMedia.GetAudioSourceInputs();
}
public void SetAudioSourceInput(SourceInput audioInput)
{
LocalMedia.ChangeAudioSourceInput(audioInput);
}
Currently, LiveSwitch doesn't support changing audio input and output on Android. We suggest using AudioManager to change audio output. Audio input as it stands can't be changed separately from the audio output.
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.swift file:
var _currentAudioInput: FMLiveSwitchSourceInput?
func setAudioSourceInput(sourceInputId: String) {
self._localMedia?.getAudioSourceInputs().then(resolveActionBlock: {[self] (result: Any!) in
let newResults = result as! [FMLiveSwitchSourceInput]
self._currentAudioInput = newResults.filter{input in
return input.id() == sourceInputId
}[0]
});
// Fall back if no input desired was found
if (self._currentAudioInput == nil) {
self._currentAudioInput = self._localMedia?.audioSourceInput()
}
self._localMedia?.changeAudioSourceInput(self._currentAudioInput!)
}
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.ts file:
// Switch audio input device.
public getAudioInputs(): fm.liveswitch.Future<fm.liveswitch.SourceInput[]> {
return this.localMedia.getAudioSourceInputs();
}
public setAudioInput(input: fm.liveswitch.SourceInput): void {
this.localMedia.changeAudioSourceInput(input);
}
Change Video Input Devices
We'll use LocalMedia.GetVideoSourceInputs to get a list of video input devices, and then use LocalMedia.ChangeVideoSourceInput to change the video input devices.
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.cs file:
// Get a list of video input devices.
public Future<SourceInput[]> GetVideoInputs()
{
return LocalMedia.GetVideoSourceInputs();
}
// Switch video input device.
public void SetVideoSourceInput(SourceInput videoInput)
{
LocalMedia.ChangeVideoSourceInput(videoInput);
}
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.java file:
// To determine whether local media is set or not.
public LocalMedia<View> getLocalMedia() {
return localMedia;
}
// Get a list of video input devices.
public SourceInput[] getVideoInputs() {
return localMedia.getVideoSourceInputs().waitForResult();
}
// Switch video input device.
public void setVideoSourceInput(SourceInput input) {
localMedia.changeVideoSourceInput(input);
}
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.swift file:
var _currentVideoInput: FMLiveSwitchSourceInput?
func setVideoSourceInput(sourceInputId: String) {
self._localMedia?.getVideoSourceInputs().then(resolveActionBlock: {[self] (result: Any!) in
let newResults = result as! [FMLiveSwitchSourceInput]
self._currentVideoInput = newResults.filter{input in
return input.id() == sourceInputId
}[0]
});
// Fall back if no input desired was found
if (self._currentVideoInput == nil) {
self._currentVideoInput = self._localMedia?.videoSourceInput()
}
self._localMedia?.changeVideoSourceInput(self._currentVideoInput!)
}
Paste the following code into the HelloWorldLogic class in the HelloWorldLogic.ts file:
// Get a list of video input devices.
public getVideoInputs(): fm.liveswitch.Future<fm.liveswitch.SourceInput[]> {
return this.localMedia.getVideoSourceInputs();
}
// Switch video input device.
public setVideoInput(input: fm.liveswitch.SourceInput): void {
this.localMedia.changeVideoSourceInput(input);
}
Change Audio Output Devices
Use RemoteMedia.GetAudioSinkOutputs to get a list of audio output devices, and then use RemoteMedia.ChangeAudioSinkOutput to change the audio output devices.
Paste the following code into the HelloWorldLogic class:
// Switch audio output device.
public Future<SinkOutput[]> GetAudioOutputs()
{
var remoteMedia = new RemoteMedia(false, false, _AecContext);
return remoteMedia.GetAudioSinkOutputs();
}
public void SetAudioSourceOutput(SinkOutput audioOutput)
{
// Set the audio output device for each downstream connection.
foreach (SfuDownstreamConnection connection in _DownStreamConnections.Values)
{
connection.AudioStream.RemoteMedia.ChangeAudioSinkOutput(audioOutput);
}
}
Currently, LiveSwitch doesn't support changing audio input and output on Android. We suggest using AudioManager. The following is an example of changing devices among Bluetooth speakers, wired speakers, and phone speakers. Note that you can't change audio input separately from the audio output.
Paste the following code into the HelloWorldLogic class:
// Available audio outputs.
final String[] audioOutputs = new String[]{"Bluetooth", "Wired Speaker", "Phone Speaker"};
// Switch audio output device.
public String[] getAudioOutputs() {
// Get the audio manager and the available audio devices from the OS.
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
HashSet<String> availableAudioOutputs = new HashSet<>();
for (AudioDeviceInfo deviceInfo : audioDevices) {
switch (deviceInfo.getType()) {
case (AudioDeviceInfo.TYPE_BLUETOOTH_A2DP):
case (AudioDeviceInfo.TYPE_BLUETOOTH_SCO):
// Bluetooth Device
availableAudioOutputs.add(audioOutputs[0]);
break;
case (AudioDeviceInfo.TYPE_WIRED_HEADPHONES):
case (AudioDeviceInfo.TYPE_WIRED_HEADSET):
// Wired Speaker
availableAudioOutputs.add(audioOutputs[1]);
break;
case (AudioDeviceInfo.TYPE_BUILTIN_SPEAKER):
// Phone Speaker
availableAudioOutputs.add(audioOutputs[2]);
break;
default:
break;
}
}
return availableAudioOutputs.toArray(new String[0]);
}
public void setAudioSourceOutput(String deviceType) {
// Get the audio manager from the OS.
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (deviceType.equals(audioOutputs[0])) {
// Bluetooth Device
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.startBluetoothSco();
audioManager.setBluetoothScoOn(true);
} else if (deviceType.equals(audioOutputs[1])) {
// Wired Speaker
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.stopBluetoothSco();
audioManager.setBluetoothScoOn(false);
audioManager.setSpeakerphoneOn(false);
} else {
// Phone Speaker
audioManager.setMode(AudioManager.MODE_NORMAL);
audioManager.stopBluetoothSco();
audioManager.setBluetoothScoOn(false);
audioManager.setSpeakerphoneOn(true);
}
}
Currently, LiveSwitch doesn't support changing audio output on iOS. We suggest using AVAudioSession to handle this. The following is an example of changing devices among Bluetooth speakers, wired speakers, and phone speakers.
Paste the following code into the HelloWorldLogic class:
func setAudioToExternal() {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playAndRecord)
try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
try audioSession.setActive(true)
} catch {
FMLiveSwitchLog.error(withMessage: "Unable to set audio output to external speaker/ear piece speaker.")
}
}
func setAudioToSpeaker() {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playAndRecord)
try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
try audioSession.setActive(true)
} catch {
FMLiveSwitchLog.error(withMessage: "Unable to set audio output to speaker.")
}
}
Paste the following code into the HelloWorldLogic class:
// Switch audio output device.
public getAudioOutputs(): fm.liveswitch.Future<fm.liveswitch.SinkOutput[]> {
const remoteMedia = new fm.liveswitch.RemoteMedia(true, true);
return remoteMedia.getAudioSinkOutputs();
}
public setAudioOutput(output: fm.liveswitch.SinkOutput): void {
// Set the audio output device for each downstream connection.
for (const connectionID in this.downstreamConnections) {
const connection = this.downstreamConnections[connectionID];
const remoteMedia = connection.getAudioStream().getRemoteMedia();
remoteMedia.changeAudioSinkOutput(output);
}
}
Uncomment UI Components
Now, go to the files for the UI components and uncomment the code for changing devices.
In the MainWindow.xaml.cs file, there are a few places tagged with <Change Devices> and </Change Devices>. Uncomment the code between these tags.
In the DeviceSwitchingFragment.java file, uncomment the code that's commented out.
In the ViewModel.swift file, uncomment all the commented out code between the <DeviceSwitching> and </DeviceSwitching> tags.
In the DeviceSwitchingUI.swift file, uncomment all the commented out code.
If the device list for video doesn't show up, simply select the other buttons and reselect Device Switching for refresh.
In the index.tsfile, uncomment the code between the <Change Devices> and </Change Devices> tags.
Run Your App
Tip
In the mobile app, some buttons are hidden. To show hidden buttons, swipe the button (not the screen) from left to right.
Run your app in your project IDE. Click Join and then click Device Switching. You should be able to change to a different camera or microphone on your device. Your app UI should look similar to the following.