Migration Guide from Twilio to Agora: Android Edition

Shaocheng Yang
Agora.io
Published in
6 min readMar 1, 2021

Agora’s voice, video, and streaming software runs on a Software Defined Real-Time Network (SD-RTN™), which is a real-time transmission network built by Agora and the only network infrastructure specifically designed for real-time engagement in the world. All voice and video services provided by the Agora SDK are deployed and transmitted through the Agora SD-RTN™.

The Agora SD-RTN™ may be one motivation for adopting the Agora Android SDK for all of your voice, video, and streaming needs. Or you may be looking for ease of use or lower latency. Whatever the need may be, this guide details how a generically functional app with a working Twilio video integration may be disintegrated to provide essentially the same functionality but on a totally different infrastructure, with a smaller set of required methods and an entirely new feel.

Agora offers this guide to drive adoption. To make things straightforward, we will be using the Twilio Quickstart sample app as a baseline:

Overview

Twilio’s integration is heavily dependent on listeners. Twilio’s Video SDK requires implementation of three listeners: Room.Listener, RemoteParticipant.Listener, and Camera2Capturer.Listener. Each has an inordinately large number of required methods. The Agora Android SDK, however, is based on the singleton design pattern. While Agora does use delegates, only the IRtcEngineEventHandler is required, and you need to implement only the methods relevant to your application. As such, you can get your application up and running with significantly less boilerplate, and you need only implement the callbacks your application specifically needs. Nor do you need to worry about managing multiple listeners from many disparate pieces and making sure to maintain those connections as users join and leave.

What Is the Singleton Design Pattern?

Much like the Model View Controller design pattern, the singleton design pattern is a way of structuring your code for a specific purpose. A singleton is an instance where the same object is returned no matter which application makes the request. It provides a global point of access to its available methods. As stated in the Apple Design Documentation:

“A singleton class returns the same instance no matter how many times an application requests it.”

Used in situations where a single point of control is desirable, a singleton is great for services like those offered by Agora in the CPaaS market, such as live voice, video, and streaming.

In this guide, you learn how to easily replace Twilio’s Video with Agora’s RtcEngine to power live voice, video, and streaming integrations.

Step 1

The first step is to create a string in the String.xml file and put your Agora AppID into it.

<string name="agora_app_id"></string>

Follow this article to get the AppID.

Step 2

The next step is to replace the Twilio SDK dependency with the Agora Video SDK. In the app level build.gradle file, replace the com.twilio:audioswitch and com.twilio:video-android with io.agora.rtc:full-sdk.

dependencies {
...
// implementation "com.twilio:audioswitch:1.1.0"
//
implementation "com.twilio:video-android:5.13.0"
implementation 'io.agora.rtc:full-sdk:3.1.1'
}

On the top right of your Android studio, click Sync Now after making these changes.

Step 3

Step three requires replacing Twilio listeners with an instance of the Agora singleton. In the VideoActivity file, remove the listeners: Room.Listener and RemoteParticipant.Listener. Also, delete the room, cameraCapturerCompat, localAudioTrack, and localVideoTrack variables. Create an RtcEngine variable for the Agora singleton. Change the primaryVideoView and thumbnailVideoView variables to be SurfaceView instead of Twilio’s VideoView.

//    Video SDK components
// private Room room;
// private CameraCapturerCompat cameraCapturerCompat;
// private LocalAudioTrack localAudioTrack;
// private LocalVideoTrack localVideoTrack;
private RtcEngine mRtcEngine;
private VideoView primaryVideoView;
private VideoView thumbnailVideoView;

Replace the functions createAudioAndVideoTracks and setAccessToken in the onCreate method with a function for initializing the instance of the Agora singleton:

mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEngineEventHandler);

Here, you pass your AppID, which is stored in the String.xml file in the parameter. The mRtcEventHandler is a handler to manage various events occurring with the RtcEngine.

Step 4

In step 4 you replace Twilio’s local and remote functionality with Agora’s local and remote video setups.

  • Add a function for enabling video:
mRtcEngine.enableVideo();
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration(VideoEncoderConfiguration.VD_640x360, VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15, VideoEncoderConfiguration.STANDARD_BITRATE, VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_ADAPTIVE));

Agora’s AgoraVideoEncoderConfiguration is designed primarily to empower developers with the ability to set a video dimension, a frame rate, a bitrate, and an orientation so that you can control how to deliver your video.

Twilio uses LocalAudioTrack and LocalVideoTrack to set up the local video, while Agora is much simpler.

  • Add the following code for setting up the local video:
thumbnailVideoView = RtcEngine.CreateRendererView(getBaseContext());
mLocalContainer.addView(thumbnailVideoView);
thumbnailVideoView.setZOrderMediaOverlay(true);
VideoCanvas localVideoCanvas = new VideoCanvas(thumbnailVideoView, VideoCanvas.RENDER_MODE_HIDDEN, 0);
mRtcEngine.setupLocalVideo(localVideoCanvas);
mRtcEngine.startPreview();

This sets up the local video with Agora. The thumbnailVideoView is Alice’s device camera capturing video of her. On joining a channel (as you will see later) it streams into Bob’s phone as a remote video. Agora uses a VideoCanvas attached to a simple SurfaceView, so make sure that thumbnailVideoView is changed from a Twilio VideoView to a SurfaceView in both the VideoActivity and the .xml files.

  • Implement the basic event handlers for IRtcEngineEventHandler to configure the canvas or video:
private IRtcEngineEventHandler mRtcEngineEventHandler = new IRtcEngineEventHandler() {    @Override
public void onRemoteVideoStateChanged(final int uid, int state, int reason, int elapsed) {
super.onRemoteVideoStateChanged(uid, state, reason, elapsed);
if (state == Constants.REMOTE_VIDEO_STATE_DECODING) {
runOnUiThread(new Runnable() {
@Override
public void run() {
setUpRemoteView(uid);
}
});
}
}
@Override
public void onUserOffline(int uid, int reason) {
super.onUserOffline(uid, reason);
runOnUiThread(new Runnable() {
@Override
public void run() {
removeRemoteView();
}
});
}
};
private void removeRemoteView() {
if (primaryVideoView != null) {
mRemoteContainer.removeView(primaryVideoView);
}
primaryVideoView = null;
}

Replace the addRemoteParticipantVideo method to setUpRemoteView with the following:

private void setUpRemoteView(int uid) {
primaryVideoView = RtcEngine.CreateRendererView(getBaseContext());
mRemoteContainer.addView(primaryVideoView);
VideoCanvas remoteVideoCanvas = new VideoCanvas(primaryVideoView, VideoCanvas.RENDER_MODE_HIDDEN, uid);
mRtcEngine.setupRemoteVideo(remoteVideoCanvas);
}

Agora’s event handler onRemoteVideoStateChanged is triggered when a remote user’s video state is changed. For example, Alice’s local video on Bob’s device as a remote video, if that UID comes in, onRemoteVideoStateChanged will be triggered and return Alice’s UID.

Step 5

The final step is joining a channel. You join a user to a channel with .joinChannel, a single method in the Agora Android SDK that takes a token, a channel name, an optional info field, and a UID for a parameter. Replace the contents of the Video.connect function:

private void joinChannel(String channelName) {
//set the token to null for testing purpose
String token = null;
mRtcEngine.joinChannel(token, channelName, "", 0);
}

Note: This guide does not implement token authentication which is recommended for all RTE apps running in production environments. For more information about token based authentication within the Agora platform please refer to this guide: https://bit.ly/3sNiFRs.

Other Features

You can replace a few other functions with Agora functionality.

  • disconnect():
private View.OnClickListener disconnectClickListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Disconnect from room
*/
// if (room != null) {
// room.disconnect();
// }
mRtcEngine.leaveChannel();
removeRemoteView();
VideoActivity.this.intializeUI();
}
};
}
  • flipCamera():
private View.OnClickListener switchCameraClickListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
// if (cameraCapturerCompat != null) {
// CameraSource cameraSource = cameraCapturerCompat.getCameraSource();
// cameraCapturerCompat.switchCamera();
// if (thumbnailVideoView.getVisibility() == View.VISIBLE) {
// thumbnailVideoView.setMirror(cameraSource == CameraSource.BACK_CAMERA);
// } else {
// primaryVideoView.setMirror(cameraSource == CameraSource.BACK_CAMERA);
// }
// }
mRtcEngine.switchCamera();
}
};
}
  • toggleMic():
private View.OnClickListener muteClickListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Enable/disable the local audio track. The results of this operation are
* signaled to other Participants in the same Room. When an audio track is
* disabled, the audio is muted.
*/
// if (localAudioTrack != null) {
// boolean enable = !localAudioTrack.isEnabled();
// localAudioTrack.enable(enable);
// }
mRtcEngine.muteLocalAudioStream(audioEnabled);
audioEnabled = !audioEnabled;
int icon = audioEnabled ?
R.drawable.ic_mic_white_24dp : R.drawable.ic_mic_off_black_24dp;
muteActionFab.setImageDrawable(ContextCompat.getDrawable(
VideoActivity.this, icon));
}
};
}

These features are all very simple with the Agora API.

Run

There you go! If you programmed your twilioDisintegrationTool’s project according to the instructions in this guide, your code should run as well as, if not better than, the code in the agoraIntegrationTool. If so, then you have successfully adopted Agora! Congratulations! Welcome to the benefits of the Agora SD-RTN™!

Although it would be impossible to account for all of the different integrations, you should note that you replaced all of the salient generic features of Twilio with those of Agora in only five steps. Now it should be easy to simulate fully a large-scale adoption with Agora.

Additional Resources

You can find the completed project here. For further reading and details on how to implement more specific features of the Agora engine, you can take a look at our documentation, join our Slack community, or email us at devrel@agora.io.

--

--