本文介绍如何集成火山引擎 RTC SDK,并实现实时音视频通话。根据如下步骤操作,即可从 0 开始构建一个简单的音视频通话应用。
你也可以参考示例项目,了解更完整的项目实现。
在开始集成 RTC SDK 前,请确保满足以下要求:
Android Studio Arctic Fox | 2020.3.1 或以上版本(本文使用 Android Studio Giraffe | 2022.3.1 版本)
Android 4.4 或以上版本的 Android 真机或模拟器
说明
推荐使用真机进行调试,连接指南参看在硬件设备上运行应用。
Android 设备和开发电脑可以正常访问互联网
打开 Android Studio。在 Welcome to Android Studio 窗口中,单击 New Project。
在项目模板页选择 Empty Views Activity,然后单击 Next。
在项目配置页,设置项目名称、软件包名称、存储路径等信息,开发语言选择 Java,最低 API 级别选择 19 或以上,build 配置语言选择 Groovy,完成后单击 Finish。经过一段时间的处理后,Android Studio 主窗口会出现,此时你已经构建好了 Android 工程,可以开始编码了。
SDK 已在内部声明所需权限,无需手动添加。对于敏感权限,你需要在 Activity 中动态申请,本文动态申请权限章节将提供示例代码。
说明
AndroidManifest.xml
文件中声明前台服务类型 android:foregroundServiceType:microphone
。此外,如果你的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须针对前台服务将要执行的工作类型请求 FOREGROUND_SERVICE_MICROPHONE
权限。摄像头权限同理。详细说明和示例代码参看前台服务权限适配方法。AndroidManifest.xml
文件中额外声明 BLUETOOTH_CONNECT
权限,并在 Activity 中动态申请。示例代码参看应用的 targetSDKVersion >= 31 时如何配置蓝牙权限?在 Android Studio 左上角将工程视图切换为 Project 模式,在项目根目录的 settings.gradle
文件配置 Maven 仓库地址。
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { ... maven { url 'https://artifact.bytedance.com/repository/Volcengine/' } } }
说明
如果你的 Android Gradle Plugin 版本低于 v7.1.0,则应在项目根目录的 build.gradle
文件配置 Maven 仓库地址。
allprojects { repositories { ... maven { url 'https://artifact.bytedance.com/repository/Volcengine/' } } }
在 App 的 build.gradle
文件中添加 RTC SDK 依赖。
说明
你需要将 '3.x.y.z'
替换为具体的版本号,最新版本号请参看下载 SDK。
dependencies { ... implementation 'com.volcengine:VolcEngineRTC:3.x.y.z' // 填写需要接入的 RTC SDK 版本号 }
在项目根目录的 gradle.properties
文件中添加 android.enableJetifier=true
,解决兼容性问题。
设置完成后,单击 Sync now 完成同步。
说明
本章节介绍全量集成 RTC SDK 的方法。如需减小 App 体积,请参看按需集成插件。
下载并解压火山引擎 RTC SDK文件。
在 Android Studio 左上角将工程视图切换为 Project 模式,将解压后的 VolcEngineRTC-lite.aar
、effectAAR-release_V4.4.3Lite.aar
放在 app/libs
目录下,解压的四个架构文件夹放置在 app/jniLibs
目录下。
在 App 的 build.gradle
文件中添加 RTC SDK 依赖和 .so
文件依赖。
android { ... sourceSets { main { // 指定 .so 文件所在目录 jniLibs.srcDirs = ['jniLibs'] } } } ... dependencies { ... // 集成主库 implementation files('libs/VolcEngineRTC-lite.aar') // 集成特效库,用于实现美颜和特效相关功能 implementation files('libs/effectAAR-release_V4.4.3Lite.aar') }
在项目根目录的 gradle.properties
文件中添加 android.enableJetifier=true
,解决兼容性问题。
设置完成后,单击 Sync now 完成同步。
根据场景需要,为你的项目创建音视频通话的用户界面。为实现基本的音视频通话,建议用户界面中至少包含本地视频窗口、远端视频窗口、加入房间按钮、退出房间按钮。
将以下示例代码在 src/main/res/layout/activity_main.xml
中进行替换,即可快速创建用户界面。
<?xml version="1.0" encoding="utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".examples.QuickStartActivity" android:layout_marginHorizontal="15dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="300dp" android:orientation="horizontal"> <FrameLayout android:id="@+id/local_view_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" app:layout_constraintTop_toTopOf="parent" /> <FrameLayout android:id="@+id/remote_view_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" app:layout_constraintTop_toTopOf="parent" /> </LinearLayout> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_join_room" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="加入房间"/> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_leave_room" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="退出房间"/> </androidx.appcompat.widget.LinearLayoutCompat>
本章节将先向你提供 API 调用时序图和完整的实现代码,再对具体的实现步骤展开介绍。
下图为使用火山引擎 RTC SDK 实现基础音视频通话的 API 调用时序图。
复制以下示例代码,替换 MainActivity.java
文件中 package com.example.<projectname>;
后的全部内容,此处的 com.example.<projectname>
即为你新建项目时指定的软件包名称。连接并选择你的 Android 设备,单击 Android Studio 窗口右上角的运行按钮(Run 'app'),即可快速实现音视频通话。
说明
你需要将代码中的 roomId、userId、APP_ID、token 替换为你在控制台上生成临时 Token 时所使用的房间 ID 和用户 ID,以及获取到的 AppID 和临时 Token。
// package com.example.<projectname>; 你的软件包名称 import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatButton; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.view.TextureView; import android.widget.FrameLayout; import com.ss.bytertc.engine.RTCRoom; import com.ss.bytertc.engine.RTCRoomConfig; import com.ss.bytertc.engine.RTCVideo; import com.ss.bytertc.engine.UserInfo; import com.ss.bytertc.engine.VideoCanvas; import com.ss.bytertc.engine.data.RemoteStreamKey; import com.ss.bytertc.engine.data.StreamIndex; import com.ss.bytertc.engine.handler.IRTCRoomEventHandler; import com.ss.bytertc.engine.handler.IRTCVideoEventHandler; import com.ss.bytertc.engine.type.ChannelProfile; import com.ss.bytertc.engine.type.MediaStreamType; public class MainActivity extends AppCompatActivity { private static final String APP_ID = ""; // 填写 appId private static final String roomId = ""; // 填写房间号 private static final String userId = ""; //填写 userId private static final String token = ""; // 填写临时 token private FrameLayout localViewContainer; private FrameLayout remoteViewContainer; private RTCVideo rtcVideo; private RTCRoom rtcRoom; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestPermission(); localViewContainer = findViewById(R.id.local_view_container); remoteViewContainer = findViewById(R.id.remote_view_container); ((AppCompatButton) findViewById(R.id.btn_join_room)).setOnClickListener(v -> { joinRoom(roomId); }); ((AppCompatButton) findViewById(R.id.btn_leave_room)).setOnClickListener(v -> { leaveRoom(); }); // 创建引擎 rtcVideo = RTCVideo.createRTCVideo(this, APP_ID, videoEventHandler, null, null); // 设置本端渲染视图 setLocalRenderView(); // 开启音视频采集 rtcVideo.startVideoCapture(); rtcVideo.startAudioCapture(); } public void requestPermission() { String[] PERMISSIONS_STORAGE = { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; 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(PERMISSIONS_STORAGE, 22); } } } /** * 设置本地渲染视图,支持TextureView和SurfaceView */ private void setLocalRenderView() { TextureView textureView = new TextureView(this); localViewContainer.removeAllViews(); localViewContainer.addView(textureView); VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = textureView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; // 设置本地视频渲染视图 rtcVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas); } private void setRemoteRenderView(String uid) { TextureView remoteTextureView = new TextureView(this); remoteViewContainer.removeAllViews(); remoteViewContainer.addView(remoteTextureView); VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = remoteTextureView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; RemoteStreamKey remoteStreamKey = new RemoteStreamKey(roomId, uid, StreamIndex.STREAM_INDEX_MAIN); // 设置远端视频渲染视图 rtcVideo.setRemoteVideoCanvas(remoteStreamKey, videoCanvas); } /** * 引擎回调信息 */ IRTCVideoEventHandler videoEventHandler = new IRTCVideoEventHandler() { }; IRTCRoomEventHandler rtcRoomEventHandler = new IRTCRoomEventHandler() { @Override public void onUserPublishStream(String uid, MediaStreamType type) { super.onUserPublishStream(uid, type); runOnUiThread(() -> setRemoteRenderView(uid)); } }; /** * 加入房间 * @param roomId */ private void joinRoom(String roomId) { rtcRoom = rtcVideo.createRTCRoom(roomId); rtcRoom.setRTCRoomEventHandler(rtcRoomEventHandler); // 用户信息 UserInfo userInfo = new UserInfo(userId, ""); // 设置房间配置 boolean isAutoPublish = true; boolean isAutoSubscribeAudio = true; boolean isAutoSubscribeVideo = true; RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_CHAT_ROOM, isAutoPublish, isAutoSubscribeAudio, isAutoSubscribeVideo); // 加入房间 rtcRoom.joinRoom(token, userInfo, roomConfig); } /** * 离开房间 */ private void leaveRoom() { if (rtcRoom != null) { rtcRoom.leaveRoom(); rtcRoom.destroy(); rtcRoom = null; } } @Override protected void onDestroy() { super.onDestroy(); if (rtcVideo != null) { rtcVideo.stopVideoCapture(); rtcVideo.stopAudioCapture(); } RTCVideo.destroyRTCVideo(); } }
引入 SDK 需要的类。
import com.ss.bytertc.engine.RTCRoom; import com.ss.bytertc.engine.RTCRoomConfig; import com.ss.bytertc.engine.RTCVideo; import com.ss.bytertc.engine.UserInfo; import com.ss.bytertc.engine.VideoCanvas; import com.ss.bytertc.engine.data.RemoteStreamKey; import com.ss.bytertc.engine.data.StreamIndex; import com.ss.bytertc.engine.handler.IRTCRoomEventHandler; import com.ss.bytertc.engine.handler.IRTCVideoEventHandler; import com.ss.bytertc.engine.type.ChannelProfile; import com.ss.bytertc.engine.type.MediaStreamType;
启动应用程序时,检查是否已在 App 中授予了所需的权限。
public void requestPermission() { String[] PERMISSIONS_STORAGE = { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; 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(PERMISSIONS_STORAGE, 22); } } }
调用 createRTCVideo 初始化 RTCVideo 引擎,所有 RTC 相关的 API 调用都要在创建引擎之后。
// 创建引擎 rtcVideo = RTCVideo.createRTCVideo(this, APP_ID, videoEventHandler, null, null);
创建引擎后,调用 startVideoCapture 开启视频采集,调用 startAudioCapture 开启音频采集。
// 开启音视频采集 rtcVideo.startVideoCapture(); rtcVideo.startAudioCapture();
调用 setLocalVideoCanvas 设置本端渲染窗口,支持 TextureView 和 SurfaceView。
/** * 设置本地渲染视图,支持TextureView和SurfaceView */ private void setLocalRenderView() { TextureView textureView = new TextureView(this); localViewContainer.removeAllViews(); localViewContainer.addView(textureView); VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = textureView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; // 设置本地视频渲染视图 rtcVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas); }
按以下步骤创建并加入房间:
调用 createRTCRoom 创建 RTC 房间,所有和房间相关的 API 都在 RTCRoom 类。
调用 setRTCRoomEventHandler 设置房间回调监听。
调用 joinRoom 加入房间。
/** * 加入房间 * @param roomId */ private void joinRoom(String roomId) { rtcRoom = rtcVideo.createRTCRoom(roomId); rtcRoom.setRTCRoomEventHandler(rtcRoomEventHandler); // 用户信息 UserInfo userInfo = new UserInfo(userId, ""); // 设置房间配置 boolean isAutoPublish = true; boolean isAutoSubscribeAudio = true; boolean isAutoSubscribeVideo = true; RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_CHAT_ROOM, isAutoPublish, isAutoSubscribeAudio, isAutoSubscribeVideo); // 加入房间 rtcRoom.joinRoom(token, userInfo, roomConfig); }
当远端用户加入频道并发布视频流时,在 onUserPublishStream 回调中调用 setRemoteVideoCanvas 设置并渲染远端视图。
IRTCRoomEventHandler rtcRoomEventHandler = new IRTCRoomEventHandler() { @Override public void onUserPublishStream(String uid, MediaStreamType type) { super.onUserPublishStream(uid, type); runOnUiThread(() -> setRemoteRenderView(uid)); } }; private void setRemoteRenderView(String uid) { TextureView remoteTextureView = new TextureView(this); remoteViewContainer.removeAllViews(); remoteViewContainer.addView(remoteTextureView); VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = remoteTextureView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; RemoteStreamKey remoteStreamKey = new RemoteStreamKey(roomId, uid, StreamIndex.STREAM_INDEX_MAIN); // 设置远端视频渲染视图 rtcVideo.setRemoteVideoCanvas(remoteStreamKey, videoCanvas); }
调用 leaveRoom 离开房间, 并调用 destroy 销毁房间实例。
/** * 离开房间 */ private void leaveRoom() { if (rtcRoom != null) { rtcRoom.leaveRoom(); rtcRoom.destroy(); rtcRoom = null; } }
退出 app 时,调用 destroyRTCVideo 销毁 RTCVideo 引擎。
RTCVideo.destroyRTCVideo();
开启 Android 设备的开发者选项,打开 USB 调试,通过 USB 连接线将 Android 设备接入电脑,并在 Android 设备选项中勾选你的 Android 设备。详情参看在硬件设备上运行应用。
单击 Android Studio 窗口右上角的运行按钮(Run 'app')开始编译。
你的 Android 设备会自动打开 Demo 应用,在弹窗中选择开启摄像头和麦克风权限。
(可选)在第二台设备上使用相同的 AppID 和 RoomID,更换 UserID 并生成新的临时 Token,即可加入同一个房间体验双端通话。