集成 RTC SDK 后,你可以使用其中接口快速构建基础应用,实现基本实时音视频通话;你也能通过阅读代码,了解音视频通话的最佳实践。
如果你想了解完整的项目实现,参看快速开始 Demo。
本步骤为如何创建一个新项目,如集成到已有项目,请直接查看集成 SDK。
Hello Volcengine。com.example.volcenginedemo。
你可以选择 Maven 的方式或使用离线 SDK 的方式。
推荐使用 Maven 在项目中接入 RTC SDK,步骤如下:
build.gradle 文件中,配置 maven 仓库地址,如下所示:allprojects {
repositories {
...
maven { url "https://artifact.bytedance.com/repository/Volcengine/" }
}
}
groovyapp/build.gradle 文件中添加配置和依赖。dependencies {
...
implementation 'com.volcengine:VolcEngineRTC:+'// 接入最新版 RTC SDK
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72'// 使用 Maven 集成 3.37.1 ~ 3.42.1 版本 SDK 时,需要引入 kotlin,kotlin 版本号根据实际项目进行指定
}
groovy接入历史版本 RTC SDK,需要将相应配置替换为
implementation 'com.volcengine:VolcEngineRTC:3.xx.xxx',版本号参看发版说明。
VolcEngineRTC.aar 文件放在 app/libs/ 目录下。
build.gradle 文件,在 allprojects 的 repositories 属性里面增加如下代码:flatDir {
dirs 'libs'
}
groovy修改后的代码:
allprojects {
repositories {
flatDir {
dirs 'libs'
}
google()
jcenter()
}
}
groovybuild.gradle 的 dependencies 中增加 aar 的依赖,代码如下:dependencies {
...
implementation(name: 'VolcEngineRTC', ext: 'aar')
implementation "com.google.code.gson:gson:2.8.5"
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72' // RTC 3.37.1 及以后版本 SDK 开始添加 kotlin 依赖。kotlin 版本号根据实际项目需要进行指定
...
}
groovy根据实际应用需要,向系统申请所需权限。
进入 app/src/main 目录,在 AndroidManifest.xml 中添加如下权限。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- MODIFY_AUDIO_SETTINGS、BLUETOOTH 权限对于音频的路由是必须的。没有 BLUETOOTH 权限会导致蓝牙耳机连接时,声音从外放播放而不是蓝牙耳机-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 获取手机网络状态的权限 -->
<uses-permission android:name="READ_PHONE_STATE">
<!-- 仅在 App targetSDKVersion >= 31 时声明 BLUETOOTH_CONNECT 权限 -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> -->
xml如果 app targetSDKVersion >=31,除上述权限声明外,你必须在 Activity 中动态申请 BLUETOOTH_CONNECT 权限。参考以下代码进行申请:
private static final int PERMISSION_REQ_ID_RECORD_AUDIO = 22;// 可以任意值
private static final int PERMISSION_REQ_ID_CAMERA = PERMISSION_REQ_ID_RECORD_AUDIO + 1;
private static final int PERMISSION_REQ_ID_BLUETOOTH_CONNECT = PERMISSION_REQ_ID_CAMERA + 1;
// Check necessary permissions granted and
// if the permissions are not granted, use the built-in Android feature to request them.
private boolean checkSelfPermissions() {
return checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO) &&
checkSelfPermission(Manifest.permission.CAMERA, PERMISSION_REQ_ID_CAMERA) &&
// If targetSDKVersion >= 31 and want to use bluetooth headset, need request permission.
checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT, PERMISSION_REQ_ID_BLUETOOTH_CONNECT);
}
public boolean checkSelfPermission(String permission, int requestCode) {
if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
return false;
}
return true;
}
java
根据场景需要,为你的项目创建音视频通话的用户界面。
如果你想实现基本的音视频通话,我们建议你在项目中添加如下元素:
你也可以用如下示例代码在src/main/res/layout/activity_main.xml中进行替换,快速创建用户界面。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:background="@color/grey"
tools:context="com.ss.video.rtc.demo.quickstart.RTCRoomActivity"
tools:ignore="ContentDescription,RtlHardcoded,SpUsage">
<FrameLayout
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/switch_camera"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:src="@drawable/switch_camera"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/self_video_container" />
<TextView
android:id="@+id/room_id_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="end"
android:lines="1"
android:maxLength="15"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="18dp"
tools:text="ID:userid" />
<ImageView
android:id="@+id/switch_audio_router"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="16dp"
android:src="@drawable/speaker_on"
app:layout_constraintLeft_toRightOf="@id/switch_camera"
app:layout_constraintTop_toBottomOf="@id/self_video_container" />
</FrameLayout>
<FrameLayout
android:id="@+id/bottom_bar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/white"
android:paddingBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="@+id/switch_local_audio"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:src="@drawable/normal_audio" />
<ImageView
android:id="@+id/hang_up"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:src="@drawable/hang_up" />
<ImageView
android:id="@+id/switch_local_video"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="16dp"
android:src="@drawable/normal_video" />
</FrameLayout>
<FrameLayout
android:id="@+id/self_video_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/remote_video_1_container"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/remote_video_0_container"
app:layout_constraintTop_toBottomOf="@id/title_bar" />
<TextView
android:id="@+id/self_video_user_id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/purple_700"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="@id/self_video_container"
app:layout_constraintRight_toRightOf="@id/self_video_container" />
<FrameLayout
android:id="@+id/remote_video_0_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/remote_video_2_container"
app:layout_constraintLeft_toRightOf="@id/self_video_container"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_bar" />
<TextView
android:id="@+id/remote_video_0_user_id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/purple_700"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="@id/remote_video_0_container"
app:layout_constraintRight_toRightOf="@id/remote_video_0_container" />
<FrameLayout
android:id="@+id/remote_video_1_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/remote_video_2_container"
app:layout_constraintTop_toBottomOf="@id/self_video_container" />
<TextView
android:id="@+id/remote_video_1_user_id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/purple_700"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="@id/remote_video_1_container"
app:layout_constraintRight_toRightOf="@id/remote_video_1_container" />
<FrameLayout
android:id="@+id/remote_video_2_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintLeft_toRightOf="@id/remote_video_1_container"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/remote_video_0_container" />
<TextView
android:id="@+id/remote_video_2_user_id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/purple_700"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="@id/remote_video_2_container"
app:layout_constraintRight_toRightOf="@id/remote_video_2_container" />
</androidx.constraintlayout.widget.ConstraintLayout>
xml

public static synchronized RTCVideo RTCVideo.createRTCVideo(
Context context,
String appId,
IRTCVideoEventHandler handler,
Object eglContext,
JSONObject parameters)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| context | Context | Android Application Context |
| appId | String | 每个应用的唯一标识符,由 RTC 控制台随机生成的。不同的 AppId 生成的实例在 RTC 中进行音视频通话完全独立,无法互通。 |
| handler | IRTCVideoEventHandler | SDK 回调给应用层的 Handler,详见 IRTCVideoEventHandler |
| eglContext | Object | 如果需要支持外部纹理硬编码,则需要以 JObject 方式传入 eglContext。 |
| parameters | JSONObject | 用以覆盖默认参数的本引擎实例参数。JSON 字符串格式。 |
public int RTCVideo.setVideoEncoderConfig(
VideoEncoderConfig max_solution)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| max_solution | VideoEncoderConfig | 期望发布的最大分辨率视频流参数。参看 VideoEncoderConfig。 |
public int RTCVideo.setLocalVideoCanvas(
StreamIndex streamIndex,
VideoCanvas videoCanvas)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| streamIndex | StreamIndex | 视频流属性, 参看 StreamIndex |
| videoCanvas | VideoCanvas | 视图信息和渲染模式, 参看 VideoCanvas |
public void RTCVideo.startVideoCapture()
javapublic void RTCVideo.startAudioCapture()
javapublic RTCRoom RTCVideo.createRTCRoom(
String roomId)
javapublic void RTCRoom.setRTCRoomEventHandler(
IRTCRoomEventHandler rtcRoomEventHandler)
javapublic int RTCRoom.joinRoom(
String token,
UserInfo userInfo,
RTCRoomConfig roomConfig)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| token | String | 动态密钥。用于对进房用户进行鉴权验证。 进入房间需要携带 Token。测试时可使用控制台生成临时 Token,正式上线需要使用密钥 SDK 在你的服务端生成并下发 Token。 使用不同 AppID 的 App 是不能互通的。 请务必保证生成 Token 使用的 AppID 和创建引擎时使用的 AppID 相同,否则会导致加入房间失败。 |
| userInfo | UserInfo | 用户信息。参看 UserInfo。 |
| roomConfig | RTCRoomConfig | 房间参数配置,设置房间模式以及是否自动发布或订阅流。具体配置模式参看 RTCRoomConfig。 |
public void IRTCRoomEventHandler.onRoomStateChanged(
String roomId,
String uid,
int state,
String extraInfo)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| roomId | String | 房间 ID。 |
| uid | String | 用户 ID。 |
| state | int | 房间状态码。 • 0: 成功。 • !0: 失败,具体原因参看 ErrorCode 及 WarningCode。 |
| extraInfo | String | 额外信息。 joinType表示加入房间的类型,0为首次进房,1为重连进房。 elapsed表示加入房间耗时,即本地用户从调用 joinRoom 到加入房间成功所经历的时间间隔,单位为 ms。 |
public void IRTCRoomEventHandler.onUserJoined(
UserInfo userInfo,
int elapsed)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| roomId | String | 房间 ID。 |
| uid | String | 用户 ID。 |
public void IRTCVideoEventHandler.onFirstRemoteVideoFrameDecoded(
RemoteStreamKey remoteStreamKey,
VideoFrameInfo frameInfo)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| remoteStreamKey | RemoteStreamKey | 远端流信息,参看 RemoteStreamKey |
| frameInfo | VideoFrameInfo | 视频帧信息,参看 VideoFrameInfo |
public int RTCVideo.setRemoteVideoCanvas(
String userId,
StreamIndex streamIndex,
VideoCanvas videoCanvas)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| userId | String | 视频来源的远端用户 ID |
| streamIndex | StreamIndex | 视频流属性,参看 StreamIndex |
| videoCanvas | VideoCanvas | 视图信息和渲染模式,参看 VideoCanvas |
public void IRTCRoomEventHandler.onUserLeave(
String uid,
int reason)
java| 参数名 | 类型 | 说明 |
|---|---|---|
| uid | String | 离开房间,或切至不可见的的远端用户 ID。 |
| reason | int | 用户离开房间的原因。 |
public void RTCRoom.leaveRoom()
javapublic static synchronized void RTCVideo.destroyRTCVideo()
java至此,我们实现了基本的音视频通话。
1. 关于混淆配置
在 RTC SDK 334+ 版本中已优化混淆规则,无需手动配置。
若使用之前版本,需要在混淆文件里面增加对应内容,详见 FAQ: 在开发 Android 应用时,如何进行混淆配置?
2. 使用硬件耳返功能如何配置依赖项?
如果你需要使用华为硬件耳返功能,建议通过 Maven 集成 RTC SDK,不需要手动添加耳返依赖。如果你通过离线方式集成 RTC SDK,添加耳返功能依赖方法详见使用硬件耳返的依赖配置。