Introduction

In the dynamic world of video communication, ensuring a smooth and high-quality experience for users is paramount. One critical aspect of achieving this is through the implementation of a Precall system. This system allows users to test and configure their devices before joining a call, ensuring that audio and video are optimal. In this blog, we will explore the precall feature provided by VideoSDK's in Flutter SDK, discussing its significance, goals, and step-by-step implementation.

The goal of Precall Integration

The primary goal of the precall feature is to enhance user experience by allowing them to:

  • Verify and configure their audio and video devices.
  • Troubleshoot any potential issues before joining the actual call.

By incorporating a precall check, developers can significantly reduce the likelihood of technical difficulties during live video sessions, leading to more professional and uninterrupted communication.

Step-by-Step Guide: Integrating the Precall Feature

Step 1: Verify Permissions

First, ensure that your application has the required permissions to access user devices, including cameras, microphones, and speakers. Use the checkPermissions() method from the VideoSDK class to confirm if these permissions are granted.

import 'package:videosdk/videosdk.dart';

void checkMediaPermissions() async {
  try {
  //By default both audio and video permissions will be checked.
  Map<String, bool>? checkAudioVideoPermissions = await VideoSDK.checkPermissions();
  //For checking just audio permission.
  Map<String, bool>? checkAudioPermissions = await VideoSDK.checkPermissions(Permissions.audio);
  //For checking just video permission.
  Map<String, bool>? checkVideoPermissions = await VideoSDK.checkPermissions(Permissions.video);
  //For checking both audio and video permissions.
  Map<String, bool>? checkAudioVideoPermissions = await VideoSDK.checkPermissions(Permissions.audio_video);
  } catch (ex) {
    print("Error in checkPermissions()");
  }
  // Output: Map object for both audio and video permission:
  /*
        Map(2)
        0 : {"audio" => true}
            key: "audio"
            value: true
        1 : {"video" => true}
            key: "video"
            value: true
    */
};

Enable Permissions on iOS: Add the following to your Podfile to check for microphone and camera permissions:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
    //Add this into your podfile
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        'PERMISSION_CAMERA=1',
        'PERMISSION_MICROPHONE=1',
      ]
    end
  end
end
Note:  checkPermissions() method is not supported on Desktop Applications and Firefox Browser.
Note: When microphone and camera permissions are blocked, rendering device lists is not possible:
Video SDK Image

Step 2: Request Permissions (if Necessary)

If the required permissions are not granted, use the requestPermission() method from the VideoSDK class to prompt users to allow access to their devices.

To request microphone and camera permissions on iOS devices, add the following to your Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
    //Add this into your podfile
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        'PERMISSION_CAMERA=1',
        'PERMISSION_MICROPHONE=1',
      ]
    end
  end
end
NOTE: In case permissions are blocked by the user, the permission request dialogue cannot be re-rendered programmatically. In such cases, consider providing guidance to users on manually adjusting their permissions.
void requestMediaPermissions() async {
  try {
      //By default both audio and video permissions will be requested.
      Map<String, bool>? reqAudioVideoPermissions = await VideoSDK.requestPermissions();
      //For requesting just audio permission.
      Map<String, bool>? reqAudioPermissions = await VideoSDK.requestPermissions(Permissions.audio);
      //For requesting just video permission.
      Map<String, bool>? reqVideoPermissions = await VideoSDK.requestPermissions(Permissions.video);
      //For requesting both audio and video permissions.
      Map<String, bool>? reqAudioVideoPermissions = await VideoSDK.requestPermissions(Permissions.audio_video);

  } catch (ex) {
    print("Error in requestPermission ");
  }
};
Note: The requestPermissions() method is not supported on Desktop Applications and Firefox Browser.
Video SDK Image

Step 3: Render Device Lists

After obtaining the necessary permissions, fetch and display lists of available video and audio devices using the getVideoDevices() and getAudioDevices() methods from the VideoSDK class. Allow users to select their preferred devices from these lists.

Note: For iOS devices, the EARPIECE is not supported when a WIRED_HEADSET or BLUETOOTH device is connected. Additionally, WIRED_HEADSET and BLUETOOTH devices cannot be used simultaneously; the most recently connected device takes priority.

List<AudioDeviceInfo> audioInputDevices = [];
List<AudioDeviceInfo> audioOutputDevices = [];

const getMediaDevices = async () => {
  try {
    //Method to get all available video devices.
    List<VideoDeviceInfo>? videoDevices = await VideoSDK.getVideoDevices();
    //Method to get all available Microphones.
    List<AudioDeviceInfo>? audioDevices = await VideoSDK.getAudioDevices();
    for (AudioDeviceInfo device in audioDevices!) {
      //For Mobile Applications
      if (!kIsWeb) {
        if (Platform.isAndroid || Platform.isIOS) {
          audioOutputDevices.add(device);
        }
      } else {
        //For Web and Desktop Applications
          if (device.kind == 'audioinput') {
            audioInputDevices.add(device);
          } else {
            audioOutputDevices.add(device);
          }
        }
      }
  } catch (err) {
    print("Error in getting audio or video devices");
  }
};
  • Displaying device lists once permissions are granted:
Video SDK Image

Step 4: Handle Device Changes

Implement the deviceChanged callback in the VideoSDK class to dynamically update and re-render device lists whenever new devices are connected or removed. This ensures that users can seamlessly interact with newly connected devices without any disruptions.

Note: The deviceChanged event is not supported in macOS applications.
VideoSDK.on(
  Events.deviceChanged,
  (devices) {
    print("device changed ${devices}");
  },
);

Step 5: Ensure Selected Devices Are Used in the Meeting

Make sure all relevant states, such as microphone and camera status (on/off) and selected devices, are transferred from the precall screen to the meeting. This can be achieved by passing these crucial states and media streams to the VideoSDK and Room methods.

For the video device:

  • Create a custom track using the selected CameraID and pass it to the createRoom method.

For audio devices in web and desktop applications:

  • Use the switchAudioDevice method to select the desired audio output device.
  • Use the changeMic method to select the desired audio input device once the room is joined.

For audio devices in mobile applications:

  • Use the switchAudioDevice method to select both the input and output audio devices.

By ensuring this integration, users can smoothly transition from the precall setup to the actual meeting while maintaining their preferred settings.

import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class MeetingScreen extends StatefulWidget {
  final String meetingId;
  final String token;

  const MeetingScreen(
      {super.key, required this.meetingId, required this.token});

  
  State<MeetingScreen> createState() => _MeetingScreenState();
}

class _MeetingScreenState extends State<MeetingScreen> {
  late Room _room;

  
  void initState() {
    //Create a custom track with the selectedVideoDevice Id
    CustomTrack cameraTrack = await VideoSDK.createCameraVideoTrack(
      cameraId: selectedVideoDevice?.deviceId);
    // create room
    _room = VideoSDK.createRoom(
      roomId: widget.meetingId,
      token: widget.token,
      displayName: "John Doe",
      micEnabled: true,
      camEnabled: true,
      customCameraVideoTrack: cameraTrack, //For Video Devices
    );
    registerMeetingEvents(room);
    super.initState();
  }

  //For Audio Devices
void registerMeetingEvents(Room _meeting) {
    // Called when joined in meeting
  _meeting.on(
    Events.roomJoined,
    () {
      //When meeting is joined, call the switchAudioDevice method to switch Audio Output.
      _meeting.switchAudioDevice(widget.selectedAudioOutputDevice!);

      //When meeting is joined, call the changeMic method to switch Audio Input.(Onlyrequired for web and desktop applications)
        if (kIsWeb || Platform.isWindows || Platform.isMacOS) {
          _meeting.changeMic(widget.selectedAudioInputDevice!);
        }
    },
  );
}

  
  Widget build(BuildContext context) {
    return YourMeetingWidget();
  }
}

This example demonstrates best practices for implementing precall functionality and can serve as a starting point for your own implementation.

git clone https://github.com/videosdk-live/videosdk-rtc-flutter-sdk-example.git

Conclusion:

Effective precall implementation is key to creating a polished and professional video calling experience. By leveraging VideoSDK's precall capabilities, you can ensure that your users enter calls with properly configured devices and necessary permissions, setting the stage for successful and hassle-free video conferences.

Remember, the precall phase is your opportunity to address potential issues before they impact the user experience. By investing time in robust precall implementation, you're laying the groundwork for high-quality, reliable video calls that will keep your users coming back.