Real-time video calling is not a new feature in many products, such as video conferencing, social applications, online education, and even in some metacosmic scenarios.

This article will teach you how to implement a video calling application on Android through the Audio Net Agora video SDK. Soundnet SDK will provide 10000 minutes of free usage per month, which can realize various real-time audio and video scenes. Without further ado, we got down to business.

Experience video calls using open source Demo

For some of you, maybe you don’t know what it’s going to look like in the end. So we have an open source basic video calling sample project on GitHub that you can use to experience audio and video calling before you start development.

Github:github.com/AgoraIO/API…

Technical principles of video call

What we’re trying to achieve here is one-to-one video calls. You can understand it as two users by joining the same channel, the realization of audio and video communication. The data from this channel will be transmitted over sonnet’s Agora SD-RTN real-time network with low latency.

After the App integrates Agora SDK, the basic working process of video call is as follows:

As shown in the figure, the steps for video call are as follows:

  1. Get a Token: When the app client joins a channel, you need to use the Token to verify the user’s identity. In a test or production environment, get the Token from the APP server.
  2. Join channel: calljoinChannelCreate and join a channel. App clients that use the same channel name join the same channel by default. A channel can be understood as a channel dedicated to transmitting real-time audio and video data.
  3. Publish and subscribe audio and video stream in channel: After joining the channel, all APP clients can publish and subscribe audio and video in channel.

The App client needs the following information to join the channel:

  • App ID: a randomly generated string used by Agora to identify your App, available from the Agora console, as detailed in this tutorial.
  • User ID: unique ID of a user. You need to set your own user ID and make sure it is unique within the channel.
  • Tokens: In a test or production environment, the App client obtains tokens from your server. In the process described in this article, you can obtain temporary tokens from the Agora console. The temporary Token is valid for 24 hours.
  • Channel name: Character string that identifies the video call channel.

The development environment

Sound net Agora SDK has good compatibility and low requirements for hardware devices and software systems. The development environment and test environment should meet the following conditions:

  • Android SDK API Level >= 16
  • Android Studio 2.0 or later
  • A real machine that supports voice and video functions
  • App requires Android 4.1 or above

Here are the development and test environments for this article:

The development environment

  • Windows 10 Home Chinese Edition
  • Java Version SE 8
  • Android Studio 3.2 Canary 4

The test environment

  • Samsung Nexus (Android 4.4.2 API 19)
  • Mi Note 3 (Android 7.1.1 API 25)

If you haven’t been exposed to the Agora SDK before, here’s what you need to do:

  • Create an AppID and obtain a Token. For details, please refer to this tutorial.

sso2.agora.io/cn/signup?

  • Download the latest video SDK from Sonnet

Project Settings

1. Before implementing a video call, follow these steps to set up your project:

To create a new project, in Android Studio, select Phone and Tablet > Empty Activity to create the Android project.

After the project is created, Android Studio automatically starts synchronizing Gradle. Ensure that the synchronization is successful before going to the next step.

2. Integrate SDK. This article recommends using Gradle to integrate Agora SDK:

A. Add the following code to the /Gradle Scripts/build. Gradle (Project:) file to add the mavenCentral dependency:

buildscript {
     repositories {
         ...
         mavenCentral()
     }
     ...
}
 
  allprojects {
     repositories {
         ...
         mavenCentral()
     }
}
Copy the code

B. Add the following code to /Gradle Scripts/build.gradle(Module:.app) to integrate the Agora video SDK into your Android project:

. dependencies { ... // X.Y.Z, please fill in the SDK version number, for example, 3.5.0. // Obtain the latest version number from the release instructions. implementation 'io.agora.rtc:full-sdk:x.y.z' }Copy the code

3. The permissions, in/app/Manifests/AndroidManifest. XML files ` ` behind permissions and add the following network equipment:

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA"  /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" />Copy the code

Client-side implementation

Here are some tips on how to implement video calling in your app using the Agora Video SDK:

1. Obtain required rights

When you start the app, check to see if you have granted the permissions you need to make video calls in the app.

Call the following code in the onCreate function:

private static final int PERMISSION_REQ_ID = 22; private static final String[] REQUESTED_PERMISSIONS = { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA }; private boolean checkSelfPermission(String permission, int requestCode) { if (ContextCompat.checkSelfPermission(this, permission) ! = PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode); return false; } return true; }Copy the code

2. Implement video call logic

The following figure shows the sequence of VIDEO call API calls:

A. Initialize the engine

The RtcEngine class contains the main methods for application calls. It is best to call the RtcEngine interface on the same thread, not on different threads at the same time.

Currently, the Agora Native SDK supports only one instance of RtcEngine, and only one RtcEngine object is created per application. All interface functions of the RtcEngine class, unless otherwise specified, are called asynchronously. It is recommended that calls to the interface be made in the same thread. All apis that return an int, unless otherwise specified, return 0 for success, and return less than 0 for failure.

The IRtcEngineEventHandler interface class is used by the SDK to send callback event notifications to applications. Applications obtain SDK event notifications by inheriting methods from the IRtcEngineEventHandler interface class.

All methods of the interface class have default (empty) implementations, and applications can inherit only the events of interest as needed. In callback methods, the application should not make time-consuming or blocking calls to apis (such as SendMessage) that might affect the SDK.

engine = RtcEngine.create(context.getApplicationContext(), getString(R.string.agora_app_id), iRtcEngineEventHandler); . private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() { /**Reports a warning during  SDK runtime. * Warning code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_warn_code .html*/ @Override public void onWarning(int warn) { Log.w(TAG, String.format("onWarning code %d message %s", warn, RtcEngine.getErrorDescription(warn))); } /**Reports an error during SDK runtime. * Error code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_cod e.html*/ @Override public void onError(int err) { Log.e(TAG, String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err))); showAlert(String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err))); } /**Occurs when a user leaves the channel. * @param stats With this callback, the application retrieves the channel information, * such as the call duration and statistics.*/ @Override public void onLeaveChannel(RtcStats stats) { super.onLeaveChannel(stats); Log.i(TAG, String.format("local user %d leaveChannel!" , myUid)); showLongToast(String.format("local user %d leaveChannel!" , myUid)); } /**Occurs when the local user joins a specified channel. * The channel name assignment is based on channelName specified in the joinChannel method. * If the uid is not specified when joinChannel is called, the server automatically assigns a uid. * @param channel Channel name * @param uid User ID * @param elapsed Time elapsed  (ms) from the user calling joinChannel until this callback is triggered*/ @Override public void onJoinChannelSuccess(String channel, int uid, int elapsed) { Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid)); myUid = uid; joined = true; handler.post(new Runnable() { @Override public void run() { join.setEnabled(true); join.setText(getString(R.string.leave)); }}); } @Override public void onRemoteAudioStats(io.agora.rtc.IRtcEngineEventHandler.RemoteAudioStats remoteAudioStats) { statisticsInfo.setRemoteAudioStats(remoteAudioStats); updateRemoteStats(); } @Override public void onLocalAudioStats(io.agora.rtc.IRtcEngineEventHandler.LocalAudioStats localAudioStats) { statisticsInfo.setLocalAudioStats(localAudioStats); updateLocalStats(); } @Override public void onRemoteVideoStats(io.agora.rtc.IRtcEngineEventHandler.RemoteVideoStats remoteVideoStats) { statisticsInfo.setRemoteVideoStats(remoteVideoStats); updateRemoteStats(); } @Override public void onLocalVideoStats(io.agora.rtc.IRtcEngineEventHandler.LocalVideoStats localVideoStats) { statisticsInfo.setLocalVideoStats(localVideoStats); updateLocalStats(); } @Override public void onRtcStats(io.agora.rtc.IRtcEngineEventHandler.RtcStats rtcStats) { statisticsInfo.setRtcStats(rtcStats); }};Copy the code

B. Set local video parameters

The enableVideo() method is used to enableVideo mode. The call can be made before joining a channel or during a call. The video mode is automatically enabled when the call is made before joining a channel. The video mode is switched from audio mode to video mode when the call is made during a call. Call the disableVideo() method to turn off the video mode.

The setupLocalVideo(VideoCanvas local) method is used to set the local video display information. The application binds the view of the local video stream and sets the video display mode by calling this interface. In application development, this method is usually called after initialization to set up the local video and then join the channel. After exiting the channel, the binding remains in effect, and if you need to unbind, you can call setupLocalVideo(NULL).

// Create render view by RtcEngine
SurfaceView surfaceView = RtcEngine.CreateRendererView(context);
if(fl_local.getChildCount() > 0)
{
    fl_local.removeAllViews();
}
// Add to the local container
fl_local.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// Setup local video to render your local camera preview
engine.setupLocalVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, 0));
// Enable video module
engine.enableVideo();
Copy the code

C. Join a channel

JoinChannel () allows users to join a calling channel. Users in the same channel can call each other. If multiple users join the same channel, they can chat in groups. Applications with different App ids cannot communicate with each other. If the call is already in progress, the user must call leaveChannel() to exit the current call and proceed to the next channel.

ChannelMediaOptions option = new ChannelMediaOptions(); option.autoSubscribeAudio = true; option.autoSubscribeVideo = true; int res = engine.joinChannel(accessToken, channelId, "Extra Optional Data", 0, option); if (res ! = 0) { // Usually happens with invalid parameters // Error code description can be found at: // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_cod e.html // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_cod e.html showAlert(RtcEngine.getErrorDescription(Math.abs(res))); return; }Copy the code

D. Leave the current channel

The leaveChannel() method is used to leave the channel, that is, hang up or exit the call.

After calling the joinChannel() API method, leaveChannel() must be called to end the call; otherwise, the next call cannot begin. LeaveChannel () can be called regardless of whether you are currently on a call, with no side effects. This method frees all resources associated with the session. This method is an asynchronous operation, and the call returns without actually exiting the channel. After actually exiting the channel, the SDK triggers the onLeaveChannel callback.

E. Manage cameras

The switchCamera() method is used to switch between front and rear cameras. Agora also provides the following tips for managing the camera: Such as setCameraTorchOn (Boolean isOn) set whether to open the flash, setCameraAutoFocusFaceModeEnabled (Boolean enabled) Settings are open face focus function and so on.

F. Update the remote user interface when the remote user joins the channel.

private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() { ... /**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel. * @param uid ID of the user whose audio state changes. * @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole * until this  callback is triggered.*/ @Override public void onUserJoined(int uid, int elapsed) { super.onUserJoined(uid, elapsed); Log.i(TAG, "onUserJoined->" + uid); showLongToast(String.format("user %d joined!" , uid)); /**Check if the context is correct*/ Context context = getContext(); if (context == null) { return; } if(remoteViews.containsKey(uid)){ return; } else{ handler.post(() -> { /**Display remote video stream*/ SurfaceView surfaceView = null; // Create render view by RtcEngine surfaceView = RtcEngine.CreateRendererView(context); surfaceView.setZOrderMediaOverlay(true); ViewGroup view = getAvailableView(); remoteViews.put(uid, view); // Add to the remote container view.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // Setup remote video to render engine.setupRemoteVideo(new VideoCanvas(surfaceView, RENDER_MODE_HIDDEN, uid)); }); } } /**Occurs when a remote user (Communication)/host (Live Broadcast) leaves the channel. * @param uid ID of the user whose audio state changes. * @param reason Reason why the user goes offline: * USER_OFFLINE_QUIT(0): The user left the current channel. * USER_OFFLINE_DROPPED(1): The SDK timed out and the user dropped offline because no data * packet was received within a certain period of time. If  a user quits the * call and the message is not passed to the SDK (due to an unreliable channel), * the SDK assumes the user dropped offline. * USER_OFFLINE_BECOME_AUDIENCE(2): (Live broadcast only.) The client role switched from * the host to the audience.*/ @Override public void onUserOffline(int uid, int reason) { Log.i(TAG, String.format("user %d offline! reason:%d", uid, reason)); showLongToast(String.format("user %d offline! reason:%d", uid, reason)); handler.post(new Runnable() { @Override public void run() { /**Clear render view Note: The video will stay at its last frame, to completely remove it you will need to remove the SurfaceView from its parent*/ engine.setupRemoteVideo(new VideoCanvas(null, RENDER_MODE_HIDDEN, uid)); remoteViews.get(uid).removeAllViews(); remoteViews.remove(uid); }}); }... };Copy the code

Finish, run

Take two phones and install the compiled App. If you can see two of yourself, you’ve succeeded. If you encounter problems during the development process, you can visit the forum to ask questions and communicate with the sound network engineers

rtcdeveloper.agora.io/

You can also access the background for further technical support

console.agora.io/