实现音视频会议
融云开发者账户是使用融云 SDK 产品的必要条件。在开始之前,请先[前往融云官网注册开发者账户]。注册后,控制台将自动为你创建一个应用,默认为开发环境应用,使用国内数据 中心。请获取该应用的 App Key,在本教程中使用。
首次使用融云音视频的用户,建议参考文档运行示例项目,以完成开发者账号注册、音视频服务开通等工作。
房间人数上限
考虑移动设备的带宽(主要是在多路视频情况下)和 UI 交互效果,建议单次通话或房间内,视频不超过 16 人,纯音频不超过 32 人。超过此上限可能影响通话效果。
环境要求
- (SDK ≧ 5.6.3)使用 Android 5.0(API 21)或更高版本;
- (SDK < 5.6.3)使用 Android 4.4(API 19)或更高版本;推荐使用 Android 5.0(API 21)。
- Android Studio 3.0 或以上版本。
步骤 1:服务开通
您在融云创建的应用默认不会启用音视频服 务。在使用融云提供的任何音视频服务前,您需要前往控制台,为应用开通音视频服务。
具体步骤请参阅开通音视频服务。
服务开通、关闭等设置完成后 30 分钟后生效。
步骤 2:SDK 导入
您需要导入融云音视频核心能力库 RTCLib,和 RTC 业务所依赖的即时通讯能力库 IMLib。根据您的业务需求,可选择导入美颜扩展库和 CDN 扩展库。
具体步骤请参阅导入 SDK。
步骤 3:代码混淆
若开发者发布的 App 启用代码混淆,请务必在 app/proguard-rules.pro 文件添加如下配置:
-keepattributes Exceptions,InnerClasses
-keepattributes Signature
#RongRTCLib
-keep public class cn.rongcloud.** {*;}
#RongIMLib
-keep class io.rong.** {*;}
-keep class cn.rongcloud.** {*;}
-keep class * implements io.rong.imlib.model.MessageContent {*;}
-dontwarn io.rong.push.**
-dontnote com.xiaomi.**
-dontnote com.google.android.gms.gcm.**
-dontnote io.rong.**
-ignorewarnings
步骤 4:权限配置
-
在
AndroidManifest.xml
中声明 SDK 需要的所有权限。<!-- 音视频需要网络权限 和 监听网络状态权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 摄像头采集需要 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 音频采集需要 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> -
如果您的应用需要支持 Android 6.0(API 级别 23)或更高版本的设备,您还需要在 App 用户使用对应功能时请求摄像头(
CAMERA
)、麦克风(RECORD_AUDIO
)权限。详见 Android 开发者官方文档运行时权限与请求权限的工作流。
步骤 5:使用 App Key 初始化
RTCLib 依赖 融云即时通讯客户端(IM)SDK 提供信令通道。要在您的应用程序中集成和运行,您需要先对 IM SDK 进行初始化。在成功建立 IM 连接后再初始化 RTCLib SDK。
融云即时通讯客户端 SDK 核心类为 RongCoreClient 和 RongIMClient
。在 Application 的 onCreate()
方法中,调用 RongIMClient
的初始化方法,传入生产或开发环境的 App Key。如果不换 AppKey,在整个应用生命周期中,初始化一次即可。
如果 IM SDK 版本 ≧ 5.4.2,请使用以下初始化方法。
String appKey = "Your_AppKey"; // example: bos9p5rlcm2ba
InitOption initOption = new InitOption.Builder().build();
RongIMClient.init(getApplicationContext(), appKey, initOption);
初始化配置(InitOption
)中封装了区域码(AreaCode),导航服务地址(naviServer
)、文件服务地址(fileServer
)、数据统计服务地址(statisticServer
)配置,以及是否开启推送的开关(enablePush
)和主进程开关(isMainProcess
)。不传入任何配置表示全部使用默认配置。SDK 默认连接北京数据中心。
如果 App Key 不属于中国(北京)数据中心,则必须传入有效的初始化配置。
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
// IMLib 初始化
RongIMClient.init(this, "从控制台申请的 AppKey");
}
}
关于 IM SDK 初始化的更多配置请参见初始化。
步骤 6:连接融云服务器
音视频用户之间的信令传输依赖于融云的即时通信(IM)服务,因此需要先调用 connect
与 IM 服务建立好 TCP 长连接。建议在功能模块的加载位置处调用,之后再进行音视频直播业务。当模块退出后调用 disconnect
或 logout
断开该连接。
IM 连接成功建立后可以初始化 RTCLib SDK。部分 RTC 引擎必须在初始化时提供,详见引擎配置。
RongIMClient.connect("从您服务器端获取的 Token", new RongIMClient.ConnectCallback() {
@Override
public void onSuccess(String userId) {
// 连接成功
// RTCLib 初始化
RCRTCConfig.Builder config = RCRTCConfig.Builder.create();
RCRTCEngine.getInstance().init(context, config.build());
}
@Override
public void onError(RongIMClient.ConnectionErrorCode code) {
// 连接失败
}
@Override
public void onDatabaseOpened(RongIMClient.DatabaseOpenStatus databaseOpenStatus) {
// 数据库打开失败
}
});
步骤 7:加入房间
-
调用 RCRTCEngine.getInstance().joinRoom() 加入房间。通过 onSuccess 和 onFailed 回调判断是否加入房间成功。
RCRTCEngine.getInstance().joinRoom("Your_Room_ID", new IRCRTCResultDataCallback<RCRTCRoom>() {
@Override
public void onSuccess(RCRTCRoom rcrtcRoom) {
}
@Override
public void onFailed(RTCErrorCode rtcErrorCode) {
}
}); -
进入房间成功后调用
RCRTCRoom.registerRoomListener()
注册房间信息回调。 -
调用
RCRTCEngine.getInstance().getDefaultVideoStream().setVideoView()
方法设置本地视频的预览视图。/**
* 初始化本地视频
*/
private void initLocalVideoView() {
// 初始化视图
RCRTCVideoView localVideoView = new RCRTCVideoView(getApplicationContext());
// 绑定视图
RCRTCEngine.getInstance().getDefaultVideoStream().setVideoView(localVideoView);
// 打开摄像机
RCRTCEngine.getInstance().getDefaultVideoStream().startCamera(null);
}
/**
* 处理入会成功后流程
*/
private void afterJoinRoomSuccess(RCRTCRoom rcrtcRoom) {
// 注册房间事件回调
rcrtcRoom.registerRoomListener(new IRCRTCRoomEventsListener(){
// 此处省略
});
// 开始推流
publishDefaultAVStream(rcrtcRoom);
// 订阅用户资源
subscribeAVStream(rcrtcRoom);
}
步骤 8:发布资源
-
调用
RCRTCEngine.getInstance().getDefaultVideoStream().startCamera()
开启摄像头。不开启摄像头,会导致对端订阅默认视频流后黑屏问题。 -
调用
RCRTCLocalUser
中的publishDefaultStreams
方法发布默认音频视频资源。
/**
* 发布默认视频流
*/
private void publishDefaultAVStream(RCRTCRoom room) {
room.getLocalUser().publishDefaultStreams(new IRCRTCResultCallback() {
@Override
public void onSuccess() {
}
@Override
public void onFailed(RTCErrorCode errorCode) {
}
});
}
步骤 9:订阅资源
-
调用
RCRTCLocalUser
中的subscribeStreams
方法订阅会议参与者的资源,当远端用户发布资源时,会通过onRemoteUserPublishResource
回调通知,需要订阅音视频流并显示视图。/**
* 主动订阅远端用户发布的流
* 视频流需要用户设置用于显示载体的 VideoView
*/
public void subscribeAVStream() {
final List<RCRTCInputStream> inputStreams = new ArrayList<>();
for (final RCRTCRemoteUser remoteUser : mRtcRoom.getRemoteUsers()) {
if (remoteUser.getStreams().size() == 0) {
continue;
}
List<RCRTCInputStream> userStreams = remoteUser.getStreams();
for (RCRTCInputStream inputStream : userStreams) {
if (inputStream.getMediaType() == RCRTCMediaType.VIDEO) {
RCRTCVideoInputStream videoInputStream = (RCRTCVideoInputStream) inputStream;
//如果未绑定过VideoView,则需要创建并绑定VideoView
if (videoInputStream.getVideoView() == null) {
// 创建流对应的视图
RCRTCVideoView videoView = new RCRTCVideoView(getApplicationContext());
// 将视图和 stream 进行绑定
videoInputStream.setVideoView(videoView);
// 将远端视图添加至布局
frameyout_remoteUser.addView(videoView);
}
}
}
inputStreams.addAll(remoteUser.getStreams());
}
if (inputStreams.size() == 0) {
return;
}
mRtcRoom.getLocalUser().subscribeStreams(inputStreams, new IRCRTCResultDataCallback<List<RCRTCInputStream>>() {
@Override
public void onSuccess() {
}
@Override
// 如果 SDK ≧ 5.3.4,您可以使用 IRCRTCResultDataCallback,onFailed 方法会返回订阅失败的流列表和错误码。
// 如果 SDK < 5.3.4,仅支持使用 IRCRTCResultCallback,onFailed 方法仅返回错误码。
public void onFailed(List<RCRTCInputStream> failedStreams, RTCErrorCode errorCode) {
}
});
} -
在注册的房间事件回调中可根据业务需求监听远端用户发布的资源,并进行 订阅。
new IRCRTCRoomEventsListener() {
/**
* 房间内用户发布资源
*
* @param rcrtcRemoteUser 远端用户
* @param list 发布的资源
*/
@Override
public void onRemoteUserPublishResource(RCRTCRemoteUser rcrtcRemoteUser, final List<RCRTCInputStream> list) {
subscribeAVStream();
}
}