Audio and video calls on Android
Learn how to integrate the ARTC SDK into your Android project to quickly build a simple interactive app that provides real-time audio and video for use cases like interactive live streaming and video calls.
Key concepts
Before you begin, it helps to understand the following key concepts:
-
ARTC SDK: Alibaba Cloud's SDK for quickly implementing real-time audio and video interactions.
-
GRTN: Alibaba Cloud's Global Realtime Transport Network, providing ultra-low latency, high-quality, secure, and reliable audio and video communication services.
-
channel: A virtual room for real-time audio and video interactions.
-
host: A role that allows a user to publish audio and video streams in a channel and subscribe to streams published by other hosts.
-
viewer: A role that enables a user to subscribe to audio and video streams in a channel but not publish streams.
-
Call
setChannelProfileto set the channel scenario, and then call joinChannel to join a channel:-
In a video call scenario, all users have the host role and can publish and subscribe to streams.
-
In an interactive streaming scenario, you must call
setClientRoleto set the user role. Set the role to host for users who will publish a stream. If a user only needs to subscribe to a stream, set their role to viewer.
-
-
After joining a channel, a user's role determines whether they can publish or subscribe to streams:
-
All users in a channel can subscribe to its audio and video streams.
-
A host can publish audio and video streams in the channel.
-
If a viewer needs to publish a stream, they must call the
setClientRolemethod to switch their role to host.
-
Sample project
The Alibaba Cloud ARTC SDK provides an open-source sample project for real-time audio and video interaction. You can download the project or view the sample source code.
Prerequisites
Before running the sample project, ensure your development environment meets the following requirements:
-
Development tool: Android Studio 2020.3.1 or later.
-
Test device: A test device running Android 5.0 (API level 21) or later.
NoteUse a physical device for testing. Emulators may lack required functionality.
-
Network environment: A stable network connection.
-
Application preparation: Obtain the AppID and AppKey for your application. For details, see Create an application.
Create a project
This section explains how to create a project and add the necessary permissions for real-time audio and video. Skip this section if you already have a project.
-
Open Android Studio and select New Project.
-
Select Phone and Tablet, then choose a starter template. This example uses Empty Views Activity.
-
Set the project name, package name, save location, development language (Java in this example), and build configuration language (Groovy DSL in this example).
-
Click Finish and wait for the project to sync.
Configure the project
Step 1: Import the SDK
Maven automatic integration (recommended)
-
Open the
settings.gradlefile in your project's root directory and add the Maven repositories required by the ARTC SDK to thedependencyResolutionManagement/repositoriesblock as follows:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// Add the Maven repositories for the ARTC SDK
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/public' }
}
}
Note: If you are using a version of the Android Gradle Plugin earlier than 7.1.0, you may not find this block in the settings.gradle file. For more information, see Android Gradle Plugin 7.1. In this case, use the following alternative:
-
Open the
app/build.gradlefile and add the ARTC SDK dependency to thedependenciesblock. You can find the version information in SDK Download. Replace${latest_version}with a specific version number. The latest version is 7.11.0.
dependencies {
// Add the dependency for the real-time audio and video SDK
// Replace ${latest_version} with a specific version number
implementation 'com.aliyun.aio:AliVCSDK_ARTC:${latest_version}'
// For versions 7.4.0 and earlier, you must add the keep dependency
// implementation 'com.aliyun.aio.keep:keep:1.0.1'
}
If you are using Android Gradle Plugin 8.1 or later, Android Studio recommends that you Migrate project dependencies to version catalogs.
Manual integration
-
Download the required version of the ARTC SDK AAR file from SDK Download. The latest version is 7.11.0, with a filename similar to
AliVCSDK_ARTC-x.y.z.aar. -
Copy the downloaded AAR file into your project directory, such as
app/libs. If this folder does not exist, create it. -
Open the
settings.gradlefile in your project's root directory and add the directory containing the AAR file todependencyResolutionManagement/repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// Add the relative path to the directory where the ARTC SDK is located
flatDir {
dir 'app/libs'
}
}
}
Note: If you are using a version of the Android Gradle Plugin earlier than 7.1.0, you may not find this block in the settings.gradle file. For more information, see Android Gradle Plugin 7.1. In this case, use the following alternative:
Open the build.gradle file in your project's root directory and add the following to the allprojects/repositories block:
allprojects {
repositories {
...
// Add the relative path to the directory where the ARTC SDK is located
flatDir {
dir 'app/libs'
}
}
}
-
Open the
app/build.gradlefile and add the AAR file dependency to thedependenciesblock:
// Replace x.y.z with the corresponding version number
implementation(name:'AliVCSDK_ARTC', version: 'x.y.z', ext:'aar')
-
After the build completes, the dependency appears in the External Libraries section.

Step 2: Specify supported CPU architectures
Open the app/build.gradle file and specify the supported CPU architectures in the defaultConfig block. Available architectures include armeabi-v7a, arm64-v8a, x86, and x86_64. Select the architectures you require.
android {
defaultConfig {
// ...other default configurations
// Support for armeabi-v7a and arm64-v8a architectures
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
Step 3: Set permissions
Set the permissions your app needs.
Go to the app/src/main directory, open the AndroidManifest.xml file, and add the required permissions.
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<!-- Needed only if your app communicates with already-paired Bluetooth devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Runtime Bluetooth permissions
The ARTC SDK's internal AndroidManifest.xml file declares several permissions. Note that you are responsible for requesting any required dynamic permissions at runtime. The BLUETOOTH_CONNECT permission, introduced in Android 12, is one such permission. After you import the ARTC SDK, its declared permissions are merged into your app's manifest. We recommend explicitly declaring all required permissions in your own manifest for clarity and to adhere to the principle of least privilege.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
Based on your business scenario, if you do not need one of the following permissions, you can declare its removal in your main project's AndroidManifest.xml file:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/>
Bluetooth permission scenarios:
Scenario 1: Bluetooth required
Your app's targetSdk is less than 31
When your project's targetSdk is less than 31, Bluetooth functionality is controlled by the legacy BLUETOOTH permission. Declare the following in your app's manifest:
<!-- Declare the Bluetooth permission -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- Override and remove the permission for API 31+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" />
-
The SDK includes a declaration for the
BLUETOOTH_CONNECTpermission. This permission was introduced in Android 12 (API level 31) and must be requested at runtime by callingrequestPermissions. -
On some devices, if an app includes the
BLUETOOTH_CONNECTdeclaration, the system requires a runtime request for this permission to avoid aSecurityException. If you encounter this issue, use one of the following approaches:-
Option 1: Remove the permission declaration by using
tools:node="remove". -
Option 2: Request the permission at runtime.
-
Your app's targetSdk is 31 or greater
When your project's targetSdk is 31 or greater, you must handle Bluetooth permissions to support both older and newer Android versions. Declare the permissions in your main app's AndroidManifest.xml file:
<!-- Declare the Bluetooth permission and set android:maxSdkVersion to 30 for compatibility with earlier devices -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- Declare the Bluetooth permission for API 31+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!-- Other Bluetooth permissions -->
Additionally, because BLUETOOTH_CONNECT is a dynamic permission, you must request it at runtime:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
String[] permissions = {
android.Manifest.permission.BLUETOOTH_CONNECT
};
ActivityCompat.requestPermissions(activity, permissions, REQUEST_BLUETOOTH);
}
Scenario 2: Bluetooth not required
To avoid crashes or permission prompts related to Bluetooth, we recommend removing unnecessary Bluetooth permissions.
In the AndroidManifest.xml file of the main project, use tools:node="remove" to override and remove the permission:
<!-- Override and remove the Bluetooth permission declarations from the ARTC SDK -->
<uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" />
Other runtime permissions
On Android 6.0 (API level 23) and later, you must request dangerous permissions at runtime. In addition to declaring them in the AndroidManifest.xml file, you must request them in your code at runtime.
The following permissions require runtime requests:
-
Manifest.permission.CAMERA
-
Manifest.permission.WRITE_EXTERNAL_STORAGE
-
Manifest.permission.RECORD_AUDIO
-
Manifest.permission.READ_EXTERNAL_STORAGE
-
Manifest.permission.READ_PHONE_STATE
On Android 12 (API level 31) or later, you must also request the following permission at runtime:
-
Manifest.permission.BLUETOOTH_CONNECT
The following table describes key permissions and their purpose:
|
Permission |
Description |
Purpose |
Required |
Runtime permission |
|
|
Grants access to the camera. |
To capture video for real-time communication. |
Yes |
Android >= 6.0 |
|
|
Grants access to the microphone. |
To capture audio for real-time communication. |
Yes |
Android >= 6.0 |
|
|
Grants access to the internet. |
To transmit audio and video data over the network. |
Yes |
No |
|
|
Allows the app to get the network status. |
To monitor network connectivity and optimize streaming quality, for example, by managing reconnections. |
Optional |
No |
|
|
Allows the app to get the Wi-Fi status. |
Gets information about the current Wi-Fi connection to optimize network performance. |
Optional |
No |
|
|
Allows the app to modify audio settings. |
To adjust system volume or switch between audio output devices (e.g., speakerphone, headset). |
Optional |
No |
|
|
Bluetooth permission (basic functionality) |
Connects to Bluetooth devices, such as Bluetooth headsets. |
Optional |
No |
|
|
Bluetooth connection permission |
Communicates with paired Bluetooth devices, such as for transmitting audio streams. |
Optional |
Android >= 12 |
|
|
Allows the app to access information related to the device's phone state. |
To manage audio streams during phone calls, for example, by pausing a session when a call starts. |
Optional |
Android >= 6.0 |
|
|
Allows the app to read files from external storage. |
To enable features like playing a local audio file as a sound effect or background music. |
Optional |
Android >= 6.0 |
|
|
Allows the app to write to external storage. |
To save files such as application logs or recorded media. |
Optional |
Android >= 6.0 |
Step 4: Prevent code obfuscation (optional)
In the app/proguard-rules.pro file, add the following ProGuard rules to prevent the SDK's public interfaces from being obfuscated.
-keep class com.aliyun.allinone.** {
*;
}
-keep class com.aliyun.rts.network.AliHttpTool {
*;
}
-keep class com.aliyun.common.AlivcBase {
*;
}
-keep class com.huawei.multimedia.alivc.** {
*;
}
-keep class com.alivc.rtc.** {
*;
}
-keep class com.alivc.component.** {
*;
}
-keep class org.webrtc.** {
*;
}
FAQ
ARTC SDK and additional permissions
After you import the ARTC SDK, the permissions from its manifest are automatically merged into your app's manifest. The exact list can vary by SDK version. For the most accurate list, inspect the AndroidManifest.xml file inside the SDK's .aar file.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
These permissions are primarily used to support features like network state detection, Wi-Fi information access, Bluetooth connections, and audio routing.
Handle permission conflicts
The ARTC SDK is designed to adapt to different Android versions. For example, it accounts for the new BLUETOOTH_CONNECT runtime permission on Android 12 (API level 31) and later. While this design generally prevents compilation errors, you may still encounter issues such as:
-
Unexpected runtime behavior, such as Manifest merging failures.
-
Google Play review risk: If your app declares a permission for a feature it does not use, it may be rejected for violating the principle of least privilege.
To mitigate these potential issues, consider the following strategies:
-
Evaluate permission requirements
First, determine if your app requires all the permissions that the SDK declares. If your app does not use a feature like Bluetooth, remove its associated permission declaration to follow the principle of least privilege. For example, if your app's targetSdk is less than 31, it does not need theBLUETOOTH_CONNECTpermission. -
Use Manifest merging directives to resolve conflicts
If a permission declared by the SDK conflicts with a declaration in another module (for example, a differentmaxSdkVersionattribute), use atoolsnamespace directive in your app's mainAndroidManifest.xmlto resolve the conflict:<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Example: Remove a permission declared by the SDK that your app does not need --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" /> <!-- Example: Override a permission attribute --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" tools:replace="android:maxSdkVersion" /> </manifest>
Step 5: Create the UI
Create the user interface for your real-time interactive scenario. To get you started, we provide sample code for a video call that creates two views to display the local and remote video.
Procedure
This section explains how to use the ARTC SDK to build a basic real-time audio and video application. You can copy the code sample to test the features quickly, then follow the steps to understand the core API calls.
The following diagram shows the basic workflow for a real-time audio and video call:
The following is a complete code sample that shows the basic process for implementing a video call:
For details on the complete sample code and how to run it, see Run the ARTC demo for Android.
1. Request permissions
When a user starts a video call, check if your app has been granted the necessary permissions:
private static final int REQUEST_PERMISSION_CODE = 101;
private static final String[] PERMISSION_MANIFEST = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
private static final String[] PERMISSION_MANIFEST33 = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CAMERA
};
private static String[] getPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return PERMISSION_MANIFEST;
}
return PERMISSION_MANIFEST33;
}
public boolean checkOrRequestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, "android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
requestPermissions(getPermissions(), REQUEST_PERMISSION_CODE);
return false;
}
}
return true;
}
2. Get an authentication token
Joining an ARTC channel requires an authentication token to verify the user's identity. For details on how to generate a token, see Token-based authentication. A token can be generated by using a single-parameter or multi-parameter method. The method you use determines which joinChannel API you must call.
Production environment:
Since generating a token requires your AppKey, hardcoding the AppKey on the client side is a security risk. In a production environment, we strongly recommend that you generate tokens on your App Server and send them to the client.
Development and debugging:
During development, if your App Server cannot yet generate tokens, you can use the logic in the following sample to create temporary tokens. The reference code is as follows:
public final class ARTCTokenHelper {
/**
* RTC AppId
*/
public static String AppId = "";
/**
* RTC AppKey
*/
public static String AppKey = "";
/**
* Generate a single-parameter meeting token based on channelId, userId, and nonce
*/
public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp, String nonce) {
StringBuilder stringBuilder = new StringBuilder()
.append(appId)
.append(appKey)
.append(channelId)
.append(userId)
.append(timestamp);
String token = getSHA256(stringBuilder.toString());
try{
JSONObject tokenJson = new JSONObject();
tokenJson.put("appid", AppId);
tokenJson.put("channelid", channelId);
tokenJson.put("userid", userId);
tokenJson.put("nonce", nonce);
tokenJson.put("timestamp", timestamp);
tokenJson.put("token", token);
String base64Token = Base64.encodeToString(tokenJson.toString().getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
return base64Token;
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Generate a single-parameter meeting token based on channelId, userId, and timestamp
*/
public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp) {
return generateSingleParameterToken(appId, appKey, channelId, userId, timestamp, "");
}
public static String getSHA256(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
return byte2Hex(hash);
} catch (NoSuchAlgorithmException e) {
// Consider logging the exception and/or re-throwing as a RuntimeException
e.printStackTrace();
}
return "";
}
private static String byte2Hex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
// Use single quote for char
stringBuilder.append('0');
}
stringBuilder.append(hex);
}
return stringBuilder.toString();
}
public static long getTimesTamp() {
return System.currentTimeMillis() / 1000 + 60 * 60 * 24;
}
}
3. Import ARTC SDK classes
Import the relevant classes and interfaces from the ARTC SDK:
// Import ARTC classes
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;
4. Create and initialize the engine
-
Create the RTC engine
Call the
getInstance[1/2]method to create anAliRTCEngineinstance.private AliRtcEngine mAliRtcEngine = null; if(mAliRtcEngine == null) { mAliRtcEngine = AliRtcEngine.getInstance(this); } -
Initialize the engine
-
Call
setChannelProfileto set the channel profile toAliRTCSdkInteractiveLive(interactive mode).You can choose between interactive mode for entertainment and communication mode for calls. Choosing the right mode ensures a smooth user experience and efficient network use.
Mode
Publishing
Subscribing
Description
Interactive mode
-
Role-based restrictions apply. Only users assigned the host role can publish streams.
-
Participants can switch roles at any time during the session.
No role restrictions. All participants have permission to subscribe to streams.
-
In interactive mode, events such as a host joining or leaving are sent to viewers. A viewer's activity is not sent to the host, which ensures an uninterrupted experience for the host.
-
If you anticipate that viewers may need to interact in the future, we recommend using interactive mode. Its flexibility lets you adjust user roles to meet different interaction needs.
Communication mode
No role restrictions. All participants have permission to publish streams.
No role restrictions. All participants have permission to subscribe to streams.
-
In communication mode, participants are aware of each other's presence in the session.
-
Although this mode does not differentiate user roles, it is functionally equivalent to the host role in interactive mode. This simplifies operations, allowing users to achieve the desired functionality with fewer API calls.
-
-
Call
setClientRoleto set the user role toAliRTCSdkInteractive(host) orAliRTCSdkLive(viewer). Note: The host role publishes and subscribes by default. The viewer role only subscribes by default, with preview and publishing disabled.NoteWhen a user switches roles in a channel, the system adjusts the publishing status of audio and video streams accordingly:
-
Switch from host to viewer: The system stops publishing local audio and video streams. Subscribed remote streams are not affected, and the user can continue to watch other participants.
-
Switch from viewer to host: The system starts publishing local audio and video streams. Subscribed remote streams remain unchanged, and the user can continue to watch other participants.
// Set the channel profile to interactive mode. In RTC, always use AliRTCSdkInteractiveLive. mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive); // Set the user role. Use AliRTCSdkInteractive to both publish and subscribe to streams, or AliRTCSdkLive to only subscribe. mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive); -
-
-
Set common callbacks
If the SDK encounters an issue, it first attempts to recover automatically with its internal retry mechanisms. For errors it cannot resolve, the SDK notifies your app through callbacks.
The following are key callbacks for issues that the SDK cannot handle, which your application must listen for and respond to:
Cause of exception
Callback and parameters
Solution
Description
Authentication failed
The
resultparameter of theonJoinChannelResultcallback isAliRtcErrJoinBadToken.Your app must check if the token is correct.
If authentication fails when a user calls an API, the API's callback returns an authentication failure error.
Token about to expire
onAuthInfoWillExpireGet a new token and call
refreshAuthInfoto update the authentication information.A token expiration error can occur either when an API is called or during runtime. The error is reported through an API callback or a separate error callback.
Token expired
onAuthInfoExpiredYour app must rejoin the channel.
A token expiration error can occur either when an API is called or during runtime. The error is reported through an API callback or a separate error callback.
Network connection issue
The
onConnectionStatusChangecallback returns AliRtcConnectionStatusFailed.Your app must rejoin the channel.
The SDK can automatically recover from brief network disconnections. If the disconnection time exceeds a preset threshold, a timeout occurs and the connection is dropped. Your app must check the network status and guide the user to rejoin the channel.
Kicked from the channel
onBye-
AliRtcOnByeUserReplaced: Check if another user has joined with the sameuserId. -
AliRtcOnByeBeKickedOut: The user was kicked from the channel by the App Server and must rejoin. -
AliRtcOnByeChannelTerminated: The channel was terminated, and the user must rejoin.
The RTC service allows an administrator to remove participants.
Local device exception
onLocalDeviceExceptionYour app must check permissions and whether the hardware is working correctly.
The RTC service supports device detection and diagnostics. When a local device exception occurs that the SDK cannot resolve, it notifies your app via a callback. Your app must then intervene to check the device status.
private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() { @Override public void onJoinChannelResult(int result, String channel, String userId, int elapsed) { super.onJoinChannelResult(result, channel, userId, elapsed); handleJoinResult(result, channel, userId); } @Override public void onLeaveChannelResult(int result, AliRtcEngine.AliRtcStats stats){ super.onLeaveChannelResult(result, stats); } @Override public void onConnectionStatusChange(AliRtcEngine.AliRtcConnectionStatus status, AliRtcEngine.AliRtcConnectionStatusChangeReason reason){ super.onConnectionStatusChange(status, reason); handler.post(new Runnable() { @Override public void run() { if(status == AliRtcEngine.AliRtcConnectionStatus.AliRtcConnectionStatusFailed) { /* TODO: This callback must be handled. It is triggered only after the SDK's internal recovery strategies fail. We recommend notifying the user. */ ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT); } else { /* TODO: Optional. Add business logic here, such as for data analytics or UI updates. */ } } }); } @Override public void onLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){ super.onLocalDeviceException(deviceType, exceptionType, msg); /* TODO: This callback must be handled. It is triggered only after the SDK's internal recovery strategies fail. We recommend notifying the user of the device error. */ handler.post(new Runnable() { @Override public void run() { String str = "OnLocalDeviceException deviceType: " + deviceType + " exceptionType: " + exceptionType + " msg: " + msg; ToastHelper.showToast(VideoChatActivity.this, str, Toast.LENGTH_SHORT); } }); } }; private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() { @Override public void onAuthInfoWillExpire() { super.onAuthInfoWillExpire(); /* TODO: This must be handled. The token is about to expire. Your app must get a new authentication token for the current channel and user, and then call refreshAuthInfo. */ } @Override public void onRemoteUserOnLineNotify(String uid, int elapsed){ super.onRemoteUserOnLineNotify(uid, elapsed); } // In the onRemoteUserOffLineNotify callback, unbind the renderer for the remote video stream. @Override public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){ super.onRemoteUserOffLineNotify(uid, reason); } // In the onRemoteTrackAvailableNotify callback, set up the renderer for the remote video stream. @Override public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){ handler.post(new Runnable() { @Override public void run() { if(videoTrack == AliRtcVideoTrackCamera) { SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this); surfaceView.setZOrderMediaOverlay(true); FrameLayout view = getAvailableView(); if (view == null) { return; } remoteViews.put(uid, view); view.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas(); remoteVideoCanvas.view = surfaceView; mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera); } else if(videoTrack == AliRtcVideoTrackNo) { if(remoteViews.containsKey(uid)) { ViewGroup view = remoteViews.get(uid); if(view != null) { view.removeAllViews(); remoteViews.remove(uid); mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera); } } } } }); } /* Your app must handle cases where multiple devices attempt to join with the same userId. */ @Override public void onBye(int code){ handler.post(new Runnable() { @Override public void run() { String msg = "onBye code:" + code; ToastHelper.showToast(VideoChatActivity.this, msg, Toast.LENGTH_SHORT); } }); } }; mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener); mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify); -
5. Set audio and video properties
-
Set audio properties
Call
setAudioProfileto set the audio encoding mode and audio scenario.mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode); -
Set video properties
Set properties for the published video stream, such as resolution, bitrate, and frame rate.
// Set video encoding parameters. AliRtcEngine.AliRtcVideoEncoderConfiguration aliRtcVideoEncoderConfiguration = new AliRtcEngine.AliRtcVideoEncoderConfiguration(); aliRtcVideoEncoderConfiguration.dimensions = new AliRtcEngine.AliRtcVideoDimensions( 720, 1280); aliRtcVideoEncoderConfiguration.frameRate = 20; aliRtcVideoEncoderConfiguration.bitrate = 1200; aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000; aliRtcVideoEncoderConfiguration.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive; mAliRtcEngine.setVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);
6. Set publishing and subscribing properties
Configure stream publishing and the default subscription behavior:
-
Call
publishLocalAudioStreamto publish the audio stream. -
Call
publishLocalVideoStreamto publish the video stream. For an audio-only call, set this tofalse.
// The SDK publishes audio by default, so you do not need to call publishLocalAudioStream.
mAliRtcEngine.publishLocalAudioStream(true);
// For a video call, you do not need to call publishLocalVideoStream(true) because the SDK publishes video by default.
// For an audio-only call, you must call publishLocalVideoStream(false) to disable video publishing.
mAliRtcEngine.publishLocalVideoStream(true);
// Set the default to subscribe to remote audio and video streams.
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
By default, the SDK automatically publishes local streams and subscribes to remote streams. You can call the methods above to disable this automatic behavior.
7. Start the local preview
-
Call
setLocalViewConfigto configure the local preview view. This requires anAliRtcVideoCanvasobject. -
Call the startPreview method to start the local video preview.
mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
localSurfaceView.setZOrderOnTop(true);
localSurfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_local = findViewById(R.id.fl_local);
fl_local.addView(localSurfaceView, layoutParams);
mLocalVideoCanvas.view = localSurfaceView;
mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
mAliRtcEngine.startPreview();
8. Join the channel
Call joinChannel to join a channel. If the token is generated based on the single-parameter rule, call the SDK's single-parameter joinChannel[1/3] interface. If the token is generated based on the multi-parameter rule, call the SDK's multi-parameter joinChannel[2/3] interface. After you join the channel, the onJoinChannelResult callback provides the result. If the result is 0, you have successfully joined the channel. Otherwise, check if the provided token is invalid.
mAliRtcEngine.joinChannel(token, null, null, null);
-
After you join the channel, the SDK publishes and subscribes to streams based on the parameters set before you joined.
-
The SDK automatically publishes and subscribes to streams by default to reduce the number of required API calls.
9. Set the remote view
When initializing the engine, set the mAliRtcEngine.setRtcEngineNotify callback. In the onRemoteTrackAvailableNotify callback, set the remote view for the remote user. The sample code is as follows:
@Override
public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
handler.post(new Runnable() {
@Override
public void run() {
if(videoTrack == AliRtcVideoTrackCamera) {
SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
surfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_remote = findViewById(R.id.fl_remote);
if (fl_remote == null) {
return;
}
fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
remoteVideoCanvas.view = surfaceView;
mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
} else if(videoTrack == AliRtcVideoTrackNo) {
FrameLayout fl_remote = findViewById(R.id.fl_remote);
fl_remote.removeAllViews();
mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
}
}
});
}
10. Leave the channel and destroy the engine
When the audio and video session ends, leave the channel and destroy the engine. Follow the steps below to end the session.
-
Call
stopPreviewto stop the video preview. -
Call
leaveChannelto leave the channel. -
Call
destroyto destroy the engine and release its resources.
private void destroyRtcEngine() {
mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;
}
11. Demonstration
