Use this guide to quickly start the interactive live video streaming with the Agora Video SDK for Android.
In this section, we will create an Android project, integrate the Agora Video SDK into the project, and add the Android device permissions to prepare the development environment.
Now, let's build a project from scratch. Skip to Integrate the SDK if a project already exists.
Open Android Studio and click Start a new Android Studio project.
On the Select a Project Template panel, choose Phone and Tablet > Empty Activity, and click Next.
On the Configure Your Project panel, fill in the following contents:
Click Finish. Follow the on-screen instructions, if any, to install the plug-ins.
Choose either of the following methods to integrate the Agora Video SDK into your project.
Method 1: Automatically integrate the SDK with JCenter
Add the following line in the /app/build.gradle file of your project:
...
dependencies {
...
// For x.y.z, please fill in a specific SDK version number, such as 3.0.0.
// Get the latest version number through the release notes.
implementation 'io.agora.rtc:full-sdk:x.y.z'
}
Method 2: Manually copy the SDK files
File or subfolder | Path of your project |
---|---|
agora-rtc-sdk.jar file | /app/libs/ |
arm64-v8a folder | /app/src/main/jniLibs/ |
armeabi-v7a folder | /app/src/main/jniLibs/ |
include folder | /app/src/main/jniLibs/ |
x86 folder | /app/src/main/jniLibs/ |
x86_64 folder | /app/src/main/jniLibs/ |
libagora-crypto.so
file in the SDK package.Add the following permissions in the /app/src/main/AndroidManifest.xml file for device access according to your needs:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.agora.tutorials1v1acall">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
// Add the following permission if your scenario involves reading the external storage:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
// For devices running Android 10.0 or later, you also need to add the following permission:
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
...
</manifest>
If your targetSdkVersion
≥ 29, add the following line in the <application>
module in the AndroidManifest.xml file:
<application
android:requestLegacyExternalStorage="true">
</application>
Add the following line in the app/proguard-rules.pro file to prevent obfuscating the code of the Agora SDK:
-keep class io.agora.**{*;}
This section introduces how to use the Agora Video SDK to start the interactive live video streaming. The following figure shows the API call sequence of the interactive live video streaming.
Create the user interface (UI) for the interactive streaming in the layout file of your project. Skip to Import Classes if you already have a UI in your project.
If you are implementing the video streaming, we recommend adding the following elements into the UI:
You can also refer to the xml files under the layout path in the OpenLive-Android demo project.
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_video_chat_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.agora.tutorials1v1vcall.VideoChatViewActivity">
<RelativeLayout
android:id="@+id/remote_video_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/remoteBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/icon_padding">
<ImageView
android:layout_width="@dimen/remote_back_icon_size"
android:layout_height="@dimen/remote_back_icon_size"
android:layout_centerInParent="true"
android:src="@drawable/icon_agora_largest"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/icon_padding"
android:layout_width="match_parent"
android:layout_height="@dimen/remote_back_icon_margin_bottom"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<FrameLayout
android:id="@+id/local_video_view_container"
android:layout_width="@dimen/local_preview_width"
android:layout_height="@dimen/local_preview_height"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/local_preview_margin_right"
android:layout_marginRight="@dimen/local_preview_margin_right"
android:layout_marginTop="@dimen/local_preview_margin_top"
android:background="@color/localBackground">
<ImageView
android:layout_width="@dimen/local_back_icon_size"
android:layout_height="@dimen/local_back_icon_size"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@drawable/icon_agora_large" />
</FrameLayout>
<RelativeLayout
android:id="@+id/control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/control_bottom_margin">
<ImageView
android:id="@+id/btn_call"
android:layout_width="@dimen/call_button_size"
android:layout_height="@dimen/call_button_size"
android:layout_centerInParent="true"
android:onClick="onCallClicked"
android:src="@drawable/btn_endcall"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/btn_switch_camera"
android:layout_width="@dimen/other_button_size"
android:layout_height="@dimen/other_button_size"
android:layout_toRightOf="@id/btn_call"
android:layout_toEndOf="@id/btn_call"
android:layout_marginLeft="@dimen/control_bottom_horizontal_margin"
android:layout_centerVertical="true"
android:onClick="onSwitchCameraClicked"
android:src="@drawable/btn_switch_camera"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/btn_mute"
android:layout_width="@dimen/other_button_size"
android:layout_height="@dimen/other_button_size"
android:layout_toLeftOf="@id/btn_call"
android:layout_toStartOf="@id/btn_call"
android:layout_marginRight="@dimen/control_bottom_horizontal_margin"
android:layout_centerVertical="true"
android:onClick="onLocalAudioMuteClicked"
android:src="@drawable/btn_unmute"
android:scaleType="centerCrop"/>
</RelativeLayout>
</RelativeLayout>
Import the following classes in the activity file of your project:
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.video.VideoCanvas;
import io.agora.rtc.video.VideoEncoderConfiguration;
Call the checkSelfPermission
method to access the camera and the microphone of the Android device when launching the activity.
private static final int PERMISSION_REQ_ID = 22;
// Ask for Android device permissions at runtime.
private static final String[] REQUESTED_PERMISSIONS = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_chat_view);
// If all the permissions are granted, initialize the RtcEngine object and join a channel.
if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
initEngineAndJoinChannel();
}
}
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;
}
Create and initialize the RtcEngine
object before calling any other Agora APIs.
In the string.xml
file, replace agora_app_id
with your App ID. Call the create
method and pass in the App ID to initialize the RtcEngine
object.
You can also listen for callback events, such as when the local user joins the channel, and when the first video frame of a host is decoded. Do not implement UI operations in these callbacks.
private RtcEngine mRtcEngine;
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
@Override
// Listen for the onJoinChannelSuccess callback.
// This callback occurs when the local user successfully joins the channel.
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","Join channel success, uid: " + (uid & 0xFFFFFFFFL));
}
});
}
@Override
// Listen for the onFirstRemoteVideoDecoded callback.
// This callback occurs when the first video frame of the host is received and decoded after the host successfully joins the channel.
// You can call the setupRemoteVideo method in this callback to set up the remote video view.
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","First remote video decoded, uid: " + (uid & 0xFFFFFFFFL));
setupRemoteVideo(uid);
}
});
}
@Override
// Listen for the onUserOffline callback.
// This callback occurs when the host leaves the channel or drops offline.
public void onUserOffline(final int uid, int reason) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","User offline, uid: " + (uid & 0xFFFFFFFFL));
onRemoteUserLeft();
}
});
}
};
...
// Initialize the RtcEngine object.
private void initializeEngine() {
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
}
After initializing the RtcEngine
object, call the setChannelProfile
method to set the channel profile as LIVE_BROADCASTING
.
One RtcEngine object uses one profile only. If you want to switch to another profile, release the current RtcEngine
object with the destroy
method and create a new one before calling the setChannelProfile
method.
private void setChannelProfile() {
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
}
A live-streaming channel has two user roles: BROADCASTER
and AUDIENCE
, and the default role is AUDIENCE
. After setting the channel profile to LIVE_BROADCASTING
, your app may use the following steps to set the client role:
BROADCASTER
or AUDIENCE
. setClientRole
method and pass in the client role set by the user.Note that in the interactive live streaming, only the host can be heard and seen. If you want to switch the user role after joining the channel, call the setClientRole
method.
public void onClickJoin(View view) {
// Show a dialog box to choose a user role.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.msg_choose_role);
builder.setNegativeButton(R.string.label_audience, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.forwardToLiveRoom(Constants.CLIENT_ROLE_AUDIENCE);
}
});
builder.setPositiveButton(R.string.label_broadcaster, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.forwardToLiveRoom(Constants.CLIENT_ROLE_BROADCASTER);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
// Get the user role and channel name specified by the user.
// The channel name is used when joining the channel.
public void forwardToLiveRoom(int cRole) {
final EditText v_room = (EditText) findViewById(R.id.room_name);
String room = v_room.getText().toString();
Intent i = new Intent(MainActivity.this, LiveRoomActivity.class);
i.putExtra("CRole", cRole);
i.putExtra("CName", room);
startActivity(i);
}
// Pass in the user role set by the user.
private int mRole;
mRole = getIntent().getIntExtra("CRole", 0);
private void setClientRole() {
mRtcEngine.setClientRole(mRole);
}
After setting the channel profile and user role, set the local video view before joining the channel so that the host can see the local video in the live streaming. Follow these steps to configure the local video view:
enableVideo
method to enable the video module. createRendererView
method to create a SurfaceView object.setupLocalVideo
method to configure the local video display settings. private void setupLocalVideo() {
// Enable the video module.
mRtcEngine.enableVideo();
// Create a SurfaceView object.
private FrameLayout mLocalContainer;
private SurfaceView mLocalView;
mLocalView = RtcEngine.CreateRendererView(getBaseContext());
mLocalView.setZOrderMediaOverlay(true);
mLocalContainer.addView(mLocalView);
// Set the local video view.
VideoCanvas localVideoCanvas = new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0);
mRtcEngine.setupLocalVideo(localVideoCanvas);
}
After setting the user role and the local video view (for the video live streaming), you can call the joinChannel
method to join a channel. In this method, set the following parameters:
token
: Pass a token that identifies the role and privilege of the user. You can set it as one of the following values:
token
as "".channelName
: Specify the channel name that you want to join.
uid
: ID of the local user that is an integer and should be unique. If you set uid
as 0, the SDK assigns a user ID for the local user and returns it in the onJoinChannelSuccess
callback.
mute
methods accordingly.For more details on the parameter settings, see joinChannel
.
If a live-streaming channel uses both the RTC Native SDK and the RTC Web SDK, ensure that you call the enableWebSdkInteroperability
method before joining the channel.
private void joinChannel() {
// For SDKs earlier than v3.0.0, call this method to enable interoperability between the Native SDK and the Web SDK if the Web SDK is in the channel. As of v3.0.0, the Native SDK enables the interoperability with the Web SDK by default.
rtcEngine.enableWebSdkInteroperability(true);
// Join a channel with a token.
private String mRoomName;
mRoomName = getIntent().getStringExtra("CName");
mRtcEngine.joinChannel(YOUR_TOKEN, mRoomName, "Extra Optional Data", 0);
}
In the video streaming you should also be able to see all the hosts, regardless of your role. This is achieved by calling the setupRemoteVideo
method after joining the channel. The difference between setting the local and remote view is that you need to specify the ID of the remote user when setting up the remote view.
Shortly after a remote host joins the channel, the SDK gets the host's user ID in the onFirstRemoteVideoDecoded
callback. Call the setupRemoteVideo
method in the callback and pass in the uid to set the video view of the host.
@Override
// Listen for the onFirstRemoteVideoDecoded callback.
// This callback occurs when the first video frame of the host is received and decoded after the host successfully joins the channel.
public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("agora","First remote video decoded, uid: " + (uid & 0xFFFFFFFFL));
setupRemoteVideo(uid);
}
});
}
private void setupRemoteVideo(int uid) {
// Create a SurfaceView object.
private RelativeLayout mRemoteContainer;
private SurfaceView mRemoteView;
mRemoteView = RtcEngine.CreateRendererView(getBaseContext());
mRemoteContainer.addView(mRemoteView);
// Set the remote video view.
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid));
}
You can implement more advanced features and functionalities in the interactive live streaming.
Call the muteLocalAudioStream
method to stop or resume sending the local audio stream to mute or unmute the local user.
public void onLocalAudioMuteClicked(View view) {
mMuted = !mMuted;
mRtcEngine.muteLocalAudioStream(mMuted);
}
Call the switchCamera
method to switch the direction of the camera.
public void onSwitchCameraClicked(View view) {
mRtcEngine.switchCamera();
}
Call the leaveChannel
method to leave the current channel according to your scenario, for example, when the interactive live streaming ends, when you need to close the app, or when your app runs in the background.
@Override
protected void onDestroy() {
super.onDestroy();
if (!mCallEnd) {
leaveChannel();
}
RtcEngine.destroy();
}
private void leaveChannel() {
// Leave the current channel.
mRtcEngine.leaveChannel();
}
You can find the complete code logic in the OpenLive-Android demo project.
Run the project on your Android device. When you set the role as the host and successfully start the interactive live streaming, you can see the video view of yourself in the app. When you set the role as the audience and successfully join the interactive live streaming, you can see the video view of the host in the app.