本文档将介绍如何在您的Android项目中集成 ARTC SDK, 快速实现一个简单的实时音视频互动App,适用于互动直播和视频通话等场景。
功能简介
在开始之前,了解以下几个关键概念会很有帮助:
ARTC SDK:这是阿里云的实时音视频产品,帮助开发者快速实现实时音视频互动的SDK。
GRTN:阿里云全球实时传输网络,提供超低延时、高音质、安全可靠的音视频通讯服务。
频道:相当于一个虚拟的房间,所有加入同一频道的用户都可以进行实时音视频互动。
主播:可在频道内发布音视频流,并可订阅其他主播发布的音视频流。
观众:可在频道内订阅音视频流,不能发布音视频流。
实现实时音视频互动的基本流程如下:
用户需要调用
setChannelProfile
(设置频道场景),后调用joinChannel加入频道:视频通话场景:所有用户都是主播角色,可以进行推流和拉流。
互动直播场景:需要调用
setClientRole
(设置角色),在频道内推流的用户设置主播角色;如果用户只需要拉流,不需要推流,则设置观众角色。
加入频道后,不同角色的用户有不同的推拉流行为:
所有加入频道内的用户都可以接收频道内的音视频流。
主播角色可以在频道内推音视频流。
观众如果需要推流,需要调用
setClientRole
方法,将用户角色切换成主播,便可以推流。
前提条件
在运行示例项目之前,请确保开发环境满足以下要求:
开发工具:Android Studio 2020.3.1 及以上版本。
测试设备:Android 5.0(SDK API Level 21)及以上版本的测试设备。
说明推荐使用真机测试,模拟机可能存在功能缺失。
网络环境:需要稳定的网络连接。
应用准备:获取实时音视频应用的AppID和AppKey,详情请参见创建应用。
创建项目(可选)
本节将介绍如何创建项目并为项目添加体验音视频互动必须的权限。如果已有项目可跳过。
打开 Android Studio,选择 New Project。
选择 Phone and Tablet 并选择一个初始模板,在此以 Empty Views Activity 为例。
配置项目信息,包含项目名、包名、项目保存路径、开发语言(在此以 Java 为例)、构建配置语言(在此以 Groovy DSL 为例)。
点击 Finish 完成创建,等待项目同步完成。
配置项目
步骤一:导入SDK
Maven自动集成(推荐)
打开项目根目录下的
settings.gradle
文件,在dependencyResolutionManagement/repositories
字段中添加 ARTC SDK 所需的Maven地址,如下所示:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// 添加ARTC SDK所在的Maven地址
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/public' }
}
}
注意,如果您使用的 Android Gradle Plugin 版本低于 7.1.0,可能无法在settings.gradle
文件中找到对应字段,相关信息请参考Android Gradle 插件 7.1。此时,请采用如下方案作为替换:
打开
app/build.gradle
文件,在dependencies
中添加对 ARTC SDK 的依赖,你可以在SDK下载中获取版本信息,并将${latest_version}
替换为具体版本号。
dependencies {
// 引入实时音视频SDK依赖
// ${latest_version}替换为具体版本号,例如7.5.0
implementation 'com.aliyun.aio:AliVCSDK_ARTC:${latest_version}'
// 7.4.0及以下版本需添加keep
// implementation 'com.aliyun.aio.keep:keep:1.0.1'
}
如果使用的 Android Gradle Plugin 为 8.1 以上版本,Android Studio 推荐将依赖库信息迁移到版本目录,相关信息请参考迁移依赖项目到版本目录。
下载SDK手动集成
在SDK下载中下载所需版本的 ARTC SDK aar 文件,例如
AliVCSDK_ARTC-7.3.0.aar
。将下载的 aar 文件拷贝到您的项目目录下,例如
app/libs
,如果没有该文件夹则新建。打开项目根目录下的
settings.gradle
文件,在dependencyResolutionManagement/repositories
下添加 aar 所在文件夹,如下所示:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// 添加ARTC SDK所在位置的相对目录
flatDir {
dir 'app/libs'
}
}
}
注意,如果您使用的 Android Gradle Plugin 版本低于 7.1.0,可能无法在settings.gradle
文件中找到对应字段,相关信息请参考Android Gradle 插件 7.1。此时,请采用如下方案作为替换:
打开项目根目录下的build.gradle
文件,在allprojects/repositories
中添加如下字段:
allprojects {
repositories {
...
// 添加ARTC SDK所在位置的相对目录
flatDir {
dir 'app/libs'
}
}
}
打开
app/build.gradle
文件,在dependencies
下添加 aar 文件的依赖,如下所示:
implementation(name:'AliVCSDK_ARTC', version: '7.3.0', ext:'aar')
构建后即可在 External Libraries 下生成对应依赖。
步骤二:配置项目支持的 CPU 架构
打开app/build.gradle
文件,在defaultConfig
中指定项目支持的 CPU 架构,如下所示。可选架构包括armeabi-v7a、arm64-v8a、x86、x86_64
,根据实际需要进行配置。
android {
defaultConfig {
// ...其他默认配置
// 支持 armeabi-v7a 和 arm64-v8a 架构
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
步骤三:设置权限
根据实际需求设置应用所需的权限,具体流程如下:
进入app/src/main
目录,打开AndroidManifest.xml
文件,添加所需权限。
<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" />
注意,Android 6.0(API 23)之后危险权限需要动态申请,除了在AndroidManifest.xml
文件中静态申请外,还需要在代码运行时请求权限。
其中部分权限需要动态申请, 需要动态申请的权限包含:
Manifest.permission.CAMERA
Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.RECORD_AUDIO
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.READ_PHONE_STATE
当Android系统版本>=12(API_LEVEL>=31) 时, 需要额外动态申请以下权限:
Manifest.permission.BLUETOOTH_CONNECT
可能涉及到的部分权限说明如下:
权限名 | 权限说明 | 申请原因 | 必要性 | 是否动态权限 |
CAMERA | 摄像头权限。 | 访问设备摄像头以采集视频流。 | 是 | Android >= 6.0 |
RECORD_AUDIO | 麦克风权限。 | 访问设备麦克风以采集音频流。 | 是 | Android >= 6.0 |
INTERNET | 网络权限。 | 音视频数据通过网络传输(如 WebRTC等协议)。 | 是 | 否 |
ACCESS_NETWORK_STATE | 允许应用获取网络状态。 | 监控网络连接状态以优化音视频传输质量,例如断网重连。 | 按需 | 否 |
ACCESS_WIFI_STATE | 允许应用获取 WiFi 状态。 | 获取当前 WiFi 连接信息以优化网络性能。 | 按需 | 否 |
MODIFY_AUDIO_SETTINGS | 允许应用修改音频配置。 | 调整系统音量、切换音频输出设备等。 | 按需 | 否 |
BLUETOOTH | 蓝牙权限(基础功能) | 连接蓝牙设备(如蓝牙耳机)。 | 按需 | 否 |
BLUETOOTH_CONNECT | 蓝牙连接权限 | 与已配对的蓝牙设备进行通信(如传输音频流)。 | 按需 | android >= 12 |
READ_PHONE_STATE | 允许应用访问与设备电话状态相关的信息 | 根据电话状态启停音频。 | 按需 | android >= 6.0 |
READ_EXTERNAL_STORAGE | 允许应用读取外部存储中的文件。 | 播放本地音乐等。 | 按需 | android >= 6.0 |
WRITE_EXTERNAL_STORAGE | 允许应用写入外部存储。 | 保存音视频文件、日志等。 | 按需 | android >= 6.0 |
步骤四:防止混淆代码(可选)
在app/proguard-rules.pro
文件中,为 SDK 配置规则,防止 SDK 对外提供的接口被混淆导致无法正常调用。
-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.** {
*;
}
步骤五:创建用户界面
根据实时音视频互动场景需要,创建相应的用户界面。我们提供了一个以视频通话场景为例,创建两个视图,分别用于展示本地视频和远端视频的示例代码,作为开发中的参考。
实现步骤
本节介绍如何使用阿里云 ARTC SDK 快速实现一个基础的实时音视频互动应用。你可以先将完整示例代码复制到项目,快速体验功能,再通过以下步骤了解核心 API 的调用。
下图展示了实现音视频互动的基本流程:
下面是一段实现音视频通话基本流程的完整参考代码:
完整示例代码的详情与运行请参见:跑通Android Demo示例。
1、申请权限请求
进入音视频通话时,检查是否已在App中授予了所需要的权限:
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、鉴权Token
加入ARTC频道需要一个鉴权Token,用于鉴权用户的合法身份,其鉴权Token生成规则详情请参见:Token鉴权。Token 生成有两种方式:单参数方式和多参数方式,不同的Token生成方式需要调用SDK不同的加入频道(joinChannel
)的接口。
上线发布阶段:
由于Token的生成需要使用AppKey,写死在客户端存在泄露的风险,因此强烈建议线上业务通过业务Server生成下发给客户端。
开发调试阶段:
开发调试阶段,如果业务Server还没有生成Token的逻辑,可以暂时参考APIExample上的Token生成逻辑,生成临时Token,其参考代码如下:
public final class ARTCTokenHelper {
/**
* RTC AppId
*/
public static String AppId = "";
/**
* RTC AppKey
*/
public static String AppKey = "";
/**
* 根据channelId,userId, timestamp, nonce 生成单参数入会 的token
* 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;
}
/**
* 根据channelId,userId, timestamp 生成单参数入会 的token
* 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、导入ARTC SDK 相关类
导入 ARTC SDK 相关的类和接口:
// 导入ARTC相关类
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;
4、创建并初始化引擎
创建RTC引擎
调用
getInstance[1/2]
接口创建引擎AliRTCEngine
private AliRtcEngine mAliRtcEngine = null; if(mAliRtcEngine == null) { mAliRtcEngine = AliRtcEngine.getInstance(this); }
初始化引擎
调用
setChannelProfile
设置频道为AliRTCSdkInteractiveLive
(互动模式)。根据具体的业务需求,可以选择适用于互动娱乐场景的互动模式,或者适合一对一或一对多广播的通信模式。正确的模式选择能够确保用户体验的流畅性并有效利用网络资源。您可以根据业务场景选择合适的模式。
模式
推流
拉流
模式介绍
互动模式
有角色限制,只有被赋予主播身份的用户可以进行推流操作。
在整个过程中,参与者可以灵活地切换角色。
无角色限制,所有参与者都拥有拉流的权限。
在互动模式中,主播加入或退出会议、以及开始推送直播流的事件都会实时通知给观众端,确保观众能够及时了解主播的动态。反之,观众的任何活动不会通告给主播,保持了主播的直播流程不受干扰。
在互动模式下,主播角色负责进行直播互动,而观众角色则主要接收内容,通常不参与直播的互动过程。若业务需求未来可能发生变化,导致不确定是否需要支持观众的互动参与,建议默认采用互动模式。这种模式具有较高的灵活性,可通过调整用户角色权限来适应不同的互动需求。
通信模式
无角色限制,所有参与者都拥有推流权限。
无角色限制,所有参与者都拥有拉流的权限。
在通信模式下,会议参与者能够相互察觉到彼此的存在。
该模式虽然没有区分用户角色,但实际上与互动模式中的主播角色相对应;目的是为了简化操作,让用户能够通过调用更少的API来实现所需的功能。
调用
setClientRole
设置用户角色为AliRTCSdkInteractive
(主播)或者AliRTCSdkLive
(观众)。注意:主播角色默认推拉流,观众角色默认关闭预览和推流,只拉流。说明当用户在频道内切换角色时,系统会相应调整音视频流的推流状态:
从主播切换为观众(“下麦”):系统将停止推送本地音视频流,但已订阅的远端流不受影响,用户仍可继续观看其他人的音视频。
从观众切换为主播(“上麦”):系统将开始推送本地音视频流,同时已订阅的远端流保持不变,用户可以继续观看其他参与者的内容。
// 设置频道模式为互动模式,RTC下都使用AliRTCSdkInteractiveLive mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive); // 设置用户角色,既需要推流也需要拉流使用AliRTCSdkInteractive, 只拉流不推流使用AliRTCSdkLive mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
设置常用的回调
SDK 在运行过程中如遇到异常情况,会优先尝试内部重试机制以自动恢复。对于无法自行解决的错误,SDK 会通过预定义的回调接口通知您的应用程序。
以下是一些 SDK 无法处理、需由应用层监听和响应的关键回调:
异常发生原因
回调及参数
解决方案
说明
鉴权失败
onJoinChannelResult回调中的result返回AliRtcErrJoinBadToken
发生错误时App需要检查Token是否正确。
在用户主动调用API时,若鉴权失败,系统将在调用API的回调中返回鉴权失败的错误信息。
鉴权将要过期
onWillAuthInfoExpire
发生该异常时App需要重新获取最新的鉴权信息后,再调用refreshAuthInfo刷新鉴权信息。
鉴权过期错误在两种情况下出现:用户调用API或程序执行期间。因此,错误反馈将通过API回调或通过独立的错误回调通知。
鉴权过期
onAuthInfoExpired
发生该异常时App需要重新入会。
鉴权过期错误在两种情况下出现:用户调用API或程序执行期间。因此,错误反馈将通过API回调或通过独立的错误回调通知。
网络连接异常
onConnectionStatusChange回调返回AliRtcConnectionStatusFailed。
发生该异常时APP需要重新入会。
SDK具备一定时间断网自动恢复能力,但若断线时间超出预设阈值,会触发超时并断开连接。此时,App应检查网络状态并指导用户重新加入会议。
被踢下线
onBye
AliRtcOnByeUserReplaced:当发生该异常时排查用户userid是否相同。
AliRtcOnByeBeKickedOut:当发生该异常时,表示被业务踢下线,需要重新入会。
AliRtcOnByeChannelTerminated:当发生该异常时,表示房间被销毁,需要重新入会。
RTC服务提供了管理员可以主动移除参与者的功能。
本地设备异常
onLocalDeviceException
发生该异常时App需要检测权限、设备硬件是否正常。
RTC服务支持设备检测和异常诊断的能力;当本地设备发生异常时,RTC服务会通过回调的方式通知客户本地设备异常,此时,若SDK无法自行解决问题,则App需要介入以查看设备是否正常。
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: 务必处理;建议业务提示客户,此时SDK内部已经尝试了各种恢复策略已经无法继续使用时才会上报 */ ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT); } else { /* TODO: 可选处理;增加业务代码,一般用于数据统计、UI变化 */ } } }); } @Override public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){ super.OnLocalDeviceException(deviceType, exceptionType, msg); /* TODO: 务必处理;建议业务提示设备错误,此时SDK内部已经尝试了各种恢复策略已经无法继续使用时才会上报 */ 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: 务必处理;Token即将过期,需要业务触发重新获取当前channel,user的鉴权信息,然后设置refreshAuthInfo即可 */ } @Override public void onRemoteUserOnLineNotify(String uid, int elapsed){ super.onRemoteUserOnLineNotify(uid, elapsed); } //在onRemoteUserOffLineNotify回调中解除远端视频流渲染控件的设置 @Override public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){ super.onRemoteUserOffLineNotify(uid, reason); } //在onRemoteTrackAvailableNotify回调中设置远端视频流渲染控件 @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); } } } } }); } /* 业务可能会触发同一个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、设置音视频属性
设置音频相关属性
调用
setAudioProfile
设置音频的编码模式和音频场景mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
设置视频相关属性
可以设置推出去的视频流的分辨率、码率、帧率等信息。
//设置视频编码参数 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、设置推拉流属性
设置推送音视频流及默认拉所有用户的流:
调用publishLocalAudioStream推送音频流
调用publishLocalVideoStream推送视频流,如果是语音通话,可以设置成false
//SDK默认会publish音频,publishLocalAudioStream可以不调用
mAliRtcEngine.publishLocalAudioStream(true);
//如果是视频通话,publishLocalVideoStream(true)可以不调用,SDK默认会publish视频
//如果是纯语音通话 则需要设置publishLocalVideoStream(false)设置不publish视频
mAliRtcEngine.publishLocalVideoStream(true);
//设置默认订阅远端的音频和视频流
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
SDK默认是自动推拉流模式,默认会推送音视频流及订阅频道内所有用户的音视频流,可以通过调用上面的接口关闭自动推拉流模式。
7、开启本地预览
调用setLocalViewConfig设置本地渲染视图,同时设置本地的视频显示属性。
调用startPreview 方法,开启本地视频预览
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、加入频道
调用joinChannel加入频道,如果token是单参数规则生成的,需要调用SDK单参数的joinChannel[1/3]接口,如果是多参数规则生成的,需要调用SDK多参数的joinChannel[2/3]接口。调用完加入频道后,可以在onJoinChannelResult回调中拿到加入频道结果,如果result为0,则表示加入频道成功,否则需要检查传进来的Token是否非法。
mAliRtcEngine.joinChannel(token, null, null, null);
入会后会按照入会前设定的参数执行相应的推流和拉流。
SDK默认会自动推拉流,以减少客户端需要调用的API数量。
9、设置远端视图
在初始化引擎的时候设置对应回调mAliRtcEngine.setRtcEngineNotify
,需要在onRemoteTrackAvailableNotify
回调中,为远端用户设置远端视图,示例代码如下:
@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、离开房间并销毁引擎
音视频互动结束,需要离开房间并销毁引擎,按照下列步骤结束音视频互动
调用
stopPreview
停止视频预览。调用
leaveChannel
离会。调用
destroy
销毁引擎,并释放相关资源。
@Override
private void destroyRtcEngine() {
mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;
}
11、效果演示
参考信息
示例项目
阿里云ARTC SDK提供了开源的实时音视频互动示例项目供客户参考,您可以前往下载或查看示例源码。