Add Peer to Peer Video Calling to your iOS app using Agora.io

A plethora of mobile developers are looking to add video chat in mobile apps to drive engagement and interactions between users. Whether we look at dating, telemedicine, gaming, and even e-commerce, more apps are adding live video chat or broadcasting to as a part of their offerings.

Building out video chat in your mobile applications using out of the box WebRTC is not an easy task. There are many limitations ranging from OS support, browser support, global scalability. The Agora Video SDK can help get past these limitations and allow you to focus on your app experience rather than the video stack.

Let’s get started by building a simple 1-to-1 video calling feature in your existing iOS app using Swift.


Project Setup

You can use CocoaPods to automatically add the Agora SDK to your application.

pod init
open Podfile

Add the following to the Podfile:

target 'YourProjectName' do
use_frameworks!
pod 'AgoraRtcEngine_iOS'
end

Install the Pod by running the following:

pod install

Open the newly created .xcworkspace project and navigate to the `Info.plist` file. Add the following keys in `Info.plist`:

* Privacy — Camera Usage Description

* Privacy — Microphone Usage Description

For each key, you will need to add a quick explanation on why you are using the camera/microphone


Storyboard Setup:

In Main.storyboard, add a new View Controller which contains 2 subviews: one for the local video feed (referred to `localVideo`) and the other for the remote video feed (referred to as `remoteVideo`). Add a few buttons for call functionality such as: muting the local video, muting the local audio, hanging up a call, and switching camera.


Create an Agora Engine Instance

The code samples in this section are in VideoCallViewController.swift.

AgoraRtcEngineKit is the interface of the Agora API that provides communication functionality. Once imported, create a singleton instance by invoking sharedEngine during initialization, passing the application ID and a reference to self as the delegate. The Agora API uses delegates to inform the application about Agora engine runtime events such as joining or leaving a channel and the addition of new participants.

In the sample project, a helper method called initializeAgoraEngine() contains this logic and is invoked by viewDidLoad():


Configure Video Mode

The next step is to enable video mode, configure the video encoding profile, and specify if the width and height can change when switching from portrait to landscape:

Create a helper method called setupVideo() which will contain this logic and is invoked by viewDidLoad(). It starts by enabling video with enableVideo(). The video configuration is then set to the following:

Dimensions -> 640x360,
Frame rate -> 15 fps, 
Bit rate -> Standard (same as base frame rate), 
Orientation -> adaptive (output video follows orientation of captured video)

If a device’s camera does not support the specified resolution, the SDK automatically chooses a suitable camera resolution.


Join a channel

A helper method called joinChannel() invokes agoraKit.joinChannel() enables a user to join a specific channel:

The channelName parameter provides the name of the channel to join (`demoChannel1` in this sample) and the uid parameter passes the assigned user ID for the user joining the channel. The value of 0 is passed for the uid parameter which allows Agora to auto-assign a random UID for the user that is joining. The call using agoraKit enables the speakerphone when using Agora, and UIApplication.shared.isIdleTimerDisabled disables the application’s idle timer to prevent the application from idling while the app is running.

The call using agoraKit enables the speakerphone when using Agora, and UIApplication.shared.isIdleTimerDisabled disables the application’s idle timer to prevent the application from idling while the app is running.

Note: Users in the same channel can talk to each other, but users with different app IDs cannot call each other even if they join the same channel.

In the sample, the helper method joinChannel() is invoked by viewDidLoad():


Set up local video

The logic for the local video feed is contained within a helper method called setupLocalVideo(), which is invoked by viewDidLoad():

setupLocalVideo() creates an AgoraRtcVideoCanvas object for the video stream and initializes the following properties:

  • uid: A value of 0 allows Agora to choose a random ID for the stream feed.
  • view: Set to the localVideo view from the storyboard.
  • renderMode: Set to hidden to uniformly scale the video until it fills the visible boundaries.

The call to setupLocalVideo() passes the AgoraRtcVideoCanvas object that was just created.


Set up Video Call View Controller

The VideoCallViewController class extends AgoraRtcEngineDelegate:

The rtcEngine(engine: AgoraRtcEngineKit, firstRemoteVideoDecodedOfUid uid: UInt, size: CGSize, elapsed: Int) delegate method is invoked once connected with another user and the first remote video frame is received and decoded. This method performs the following actions:

  • Checks if the remoteVideo view is hidden and unhides it if it is hidden.
  • Initializes the AgoraRtcVideoCanvas object.
  • Sets the uid property to 0 to allow Agora to choose a random UID for the stream feed.
  • Sets the view property to the remoteVideo view from the storyboard.
  • Sets renderMode to adaptive to ensure the video is resized proportionally to fit the display window.
  • Invokes agoraKit.setupRemoteVideo(videoCanvas) passing in the AgoraRtcVideoCanvas object that was just created.

The rtcEngine(_engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraRtcUserOfflineReason) delegate is invoked when another user leaves the channel. This method sets the remoteVideo view to be hidden when a user leaves the call.

The rtcEngine(engine: AgoraRtcEngineKit, didVideoMuted muted: UInt, byUid: UInt) is invoked when a remote user pauses their stream. This method toggles the remote video user interface elements.


Hang Up and End the Call

Video Call View Controller contains a helper function called leaveChannel() with the logic to leave the current video call (channel). This is invoked by the IBAction for the Hang-Up button:

The leaveChannel() method:

  • Invokes agoraKit.leaveChannel() to leave the channel
  • Invokes the helper method hideControlButtons() to hide the controlButtons view containing the bottom buttons.
  • Re-enables the application’s idle timer.
  • Removes both the local and remote video views.
  • Sets agoraKit to nil to remove the reference to the AgoraRtcEngineKit object.

Mute Audio and Video

To allow the user to mute local audio, the IBAction for the mute button invokes muteLocalAudioStream():

Once audio is muted, the helper method resetHideButtonsTimer() is invoked to cancel any view requests and to ensure the control buttons are hidden:

To allow the user to stop sharing local video, the IBAction for the video button invokes muteLocalVideoStream():

Once disabled, the views related to the local view are hidden and the helper method resetHideButtonsTimer() is invoked to cancel any perform requests and to ensure the control buttons are hidden.


Flip Camera

To enable the user to choose between the front and rear cameras, an IBAction for the camera switch button invokes switchCamera() to add the camera switch functionality:


Hide Video Views

To hide all the image views that are meant to appear when either the remote or local video feeds are paused, the sample defines the hideVideoMuted() helper method. This method is invoked from viewDidLoad() to ensure the videos are hidden on startup:

The sample uses a tap gesture recognizer (of type: UITabGestureRecognizer) as part of the view which performs the action of calling the viewTapped() function:


And that’s about it! Go ahead and run the app on your iOS device. You can connect multiple devices to test out or use a web demo to test if you don’t have another device on hand. Make sure to enable Web Interoperability using the enableWebSdkInteroperability method.

If you have any questions go ahead and ask them via Stack Overflow or shoot me an email.