Android集成多人通话
跑通Demo
示例代码
前提条件
Android Studio 3.0 或以上版本
Android SDK API 等级 19 或以上
支持 Android 4.4 或以上版本的设备
运行Demo
Demo代码目录简介
源码目录如上所示,主要目录如以下介绍
ui是有关所有Activity的具体实现
runtimepermissions是有关运行是有关动态权限的获取的封装
untils是有关工具类的封装
DemoHelper和DemoApplication类是全局单例,用来做一些初始化
工程设置,SDK导入
选择如下任意一种方式将环信语音SDK集成到你的项目中:
方法一:使用 JCenter 自动集成
在项目的 /app/build.gradle 文件中,添加如下行
dependencies {
...
// x.x.x 请填写具体版本号,如:3.7.0
// 可通过 SDK 发版说明取得最新版本号
api 'com.hyphenate:hyphenate-sdk:x.x.x'
}
方法二:手动复制 SDK 文件
前往SDK载页面,获取最新版的环信音视频SDK,然后解压;
将SDK包内libs路径下的如下文件,拷贝到你的项目路径下
hyphenatechat_3.7.0.jar 文件 /app/libs/
arm-v8a 文件夹 /app/src/main/jniLibs/
armeabi-v7a 文件夹 /app/src/main/jniLibs/
x86文件夹 /app/src/main/jniLibs/
x86_64文件夹 /app/src/main/jniLibs/
运行项目
用Android Studio 运行examples\ChatDemoUI3.0 或者 连接Android 手机,直接运行即可
需要注意的的是项目build.gradle中的,配置成和自己Android Studio相匹配的 gradle 版本,如下所示
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
快速集成
1 环信后台注册appkey
环信为管理者与开发者提供了方便易用的App工作台–环信管理后台;
通过环信管理后台可以完成应用创建、服务购买、企业信息修改基础功能;
同时,管理后台也提供了发送消息、用户管理、群组管理、聊天室管理和数据统计等管理监控功能;
有关注册appkey的详细操作大家可以参考注册AppKey 。
2 创建项目
打开 Android Studio点击Start a new Android Studio project;
在Select a Project Template界面,选择 Phone and Tablet > Empty Activity;
然后点击 Next;
在 Configure Your Project界面,依次填入以下内容:Name:你的Android项目名称,如 HelloWrold;
Package name:你的项目包的名称,如 package.easemob.helloworld;
Save location:项目的存储路径;
Language:项目的编程语言,如 Java;
Minimum API level:项目的最低 API 等级;(注意本SDK 支持API 19或以上)
然后点击 Finish;
根据屏幕提示,安装可能需要的插件;
上述步骤使用 Android Studio 3.6.2 示例;
你也可以直接参考Android Studio 官网文档创建首个应用。
3 导入SDK到工程
新建项目完成以后,可以选择以上任何一种导入SDK的方法,把环信音视频SDK集成到你的项目里面,参考导入环信SDK。
4 添加权限
根据场景需要,在 /app/src/main/AndroidManifest.xml 文件中添加如下行,获取相应的设备权限;
在清单文件 AndroidManifest.xml 里加入以下权限,以及写上你注册的 AppKey;
权限配置(实际开发中可能需要更多的权限,可参考 Demo);
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.a.b">
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.VIBRATE" />
<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.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- 设置环信应用的appkey -->
<meta-data
android:name="EASEMOB_APPKEY"
android:value="easemob-demo#chatdemoui" />
<!-- 声明sdk所需的service -->
<service
android:name="com.hyphenate.chat.EMChatService"
android:exported="true"
tools:ignore="ExportedService" />
<service
android:name="com.hyphenate.chat.EMJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<!-- 声明sdk所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
...
</manifest>
注意:关于EASEMOB_APPKEY对应的value获取,在创建应用后,
申请 AppKey并进行相关配置,环信Demo中AppKey为easemob-demo#chatdemoui;
5 打包混淆
在 app/proguard-rules.pro 文件中添加如下行,防止混淆环信SDK的代码。
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
//3.6.8版本之后移除apache,无需再添加
-keep class internal.org.apache.http.entity.** {*;}
//如果使用了实时音视频功能
-keep class com.superrtc.** {*;}
-dontwarn com.superrtc.**
6 初始化SDK
初始化环信SDK,可以参考使用DemoHelper中的init方法,可设置私有部署地址或者是否允许日志输出,调用如下:
public void init(Context context) {
EMOptions options = initChatOptions(context);
appContext = context;
//可设置私有服务地址
options.setRestServer("a.b.c");
options.setIMServer("1.2.3.4");
options.setImPort(8081);
EMClient.getInstance().init(context, options);
//打开日志输出
EMCallManager manager = EMClient.getInstance().callManager();
EMClient.getInstance().setDebugMode(true);
}
7 音视频功能初始化
设置会议监听
DemoHelper还有一个重要的功能就是设置EMConferenceListener 进行会议监听
通过这个监听可以再加入会议的时候获取到已经在会议进入会议之前的流或人员
调用EMConferenceManager#addConferenceListener(EMConferenceListener listener)方法指定回调监听
成员加入或离开会议,数据流更新等 注意:该回调监听中的所有方法运行在子线程中,请勿在其中操作UI
调用EMConferenceManager#addConferenceListener(EMConferenceListener listener)方法指定回调监听
EMConferenceListener listener = new EMConferenceListener() {
@Override public void onMemberJoined(String username) {
// 有成员加入
}
@Override public void onMemberExited(String username) {
// 有成员离开
}
@Override public void onStreamAdded(EMConferenceStream stream) {
// 有流加入
}
@Override public void onStreamRemoved(EMConferenceStream stream) {
// 有流移除
}
@Override public void onStreamUpdate(EMConferenceStream stream) {
// 有流更新
}
@Override public void onPassiveLeave(int error, String message) {
// 被动离开
}
@Override public void onConferenceState(ConferenceState state) {
// 聊天室状态回调
}
@Override public void onStreamSetup(String streamId) {
// 流操作成功回调
}
@Override public void onSpeakers(final List<String> speakers) {
// 当前说话者回调
}
@Override public void onReceiveInvite(String confId, String password, String extension) {
// 收到会议邀请
if(easeUI.getTopActivity().getClass().getSimpleName().equals("ConferenceActivity")) {
return;
}
Intent conferenceIntent = new Intent(appContext, ConferenceActivity.class);
conferenceIntent.putExtra(Constant.EXTRA_CONFERENCE_ID, confId);
conferenceIntent.putExtra(Constant.EXTRA_CONFERENCE_PASS, password);
conferenceIntent.putExtra(Constant.EXTRA_CONFERENCE_IS_CREATOR, false);
conferenceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(conferenceIntent);
}
// 在Activity#onCreate()中添加监听
EMClient.getInstance().conferenceManager().addConferenceListener(conferenceListener);
});
8 加入会议
在进行音视频通话前,需要首先登录IM账户,登录过程参见账号登录。
若您还没有IM账户,需要先注册账户,注册过程参见账号注册
登录环信ID以后,可以加入会议了,通过 EMConferenceManager#joinRoom API加入房间
创建会议成功以后,默认超时时间为三分钟,超过三分钟没有人加入,会议会自动销毁;
另外当会议中所有人离开2分钟后,会议也会被销毁;
joinRoom api在会议不存在会自动去创建。
joinRoom有以下两个接口,请看下面详细介绍(第二个多参的方法可以指定所创建会议的各种属性)
/**
* \~chinese
* 加入多人音视频加议房间
* @param room 会议房间名
* @param password 会议房间密码
* @param role 当前用户加入时指定角色 (EMConferenceRole类型)
* @param callback 回调
*/
public void joinRoom(final String room ,final String password,final EMConferenceRole role, final EMValueCallBack<EMConference> callback)
/**
* \~chinese
* 加入多人音视频加议房间
* @param room 会议房间名
* @param password 会议房间密码
* @param roletype 当前用户加入时指定角色 (EMConferenceRole类型)
* @param param 设置会议参数 (EMRoomConfig类型)
* @param callback 回调函数
*/
public void joinRoom(final String room ,final String password,final EMConferenceRole roletype ,final EMRoomConfig param, final EMValueCallBack<EMConference> callback)
有关EMRoomConfig定义如下:
/**
* 会议房间属性类
* confrTyp 会议类型
* isSupportVxmini 是否支持小程序
* isRecord 是否支持录制
* isMergeRecord 是否支持和流
* nickName 用户昵称
* ext 用户头像
* maxTalkerCount 房间最多主播
* maxVideoCount 房间最多视频流媒体
* maxAudienceCount 房间最多观众
* maxPubDesktopCount 房间最多共享桌面流
*/
public class EMRoomConfig {
EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;
private boolean isSupportMiniProgram = false;
private boolean isRecord = false;
private boolean isMergeRecord = false;
private String nickName = null;
private String ext = null;
private int maxTalkerCount = -1;
private int maxVideoCount = -1;
private int maxAudienceCount = -1;
private int maxPubDesktopCount = -1;
private EMLiveConfig liveConfig = null;
public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
boolean isSupportMiniProgram,boolean isRecord,
boolean isMergeRecord , String nickName,
String ext, int maxTalkerCount,
int maxVideoCount,int maxAudienceCount,
int maxPubDesktopCount)
{...};
}
9 发布流
展示从DemoHelper类EMConferenceListener中的onStreamAdded回调和onMemberJoined获取到的流和主播列表
在ConferenceActivity中实现EMConferenceListener,然后直接把 ConferenceActivity 注册监听
可以用以下方法EMClient.getInstance().conferenceManager().addConferenceListener(this)
这样就可实现 EMConferenceListener 事件的处理,比如主播 进出房间 增加流 移除流 角色变更 管理员信息变更等事件
进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流
可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置
比如是否上传视频 是否上传音频 使用前置或后置摄像头 视频码率 显示视频页面等等
具体实现可以参考中发布订阅视频流的内容,关于以上的代码逻辑如以 如以下:
pirvate void pubLocalStream() {
EMStreamParam param = new EMStreamParam();
param.setStreamType(EMConferenceStream.StreamType.NORMAL);
param.setVideoOff(false);
param.setAudioOff(false);
param.setVideoWidth(720);
param.setVideoHeight(480);
EMClient.getInstance().conferenceManager().publish(param, new EMValueCallBack<String>() {
@Override
public void onSuccess(String streamId) {
}
@Override
public void onError(int error, String errorMsg) {
EMLog.e(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
}
注意:如果是纯音频会议,只需要在发布数据流时将EMStreamParam中的setVideoOff置为true即可
共享桌面时候发布的是桌面流 EMConferenceStream.StreamType.DESKTOP
设置视频流的参数
如果用户想对发布的音频或者视频流的参数进行调整,可以使用EMStreamParam中的参数进行设置。
只发布音频流,可以设置关闭视频。
设置视频流的分辨率
normalParam.setVideoOff(true);
normalParam.setVideoHeight(720);
normalParam.setVideoWidth(1280);
10 订阅流
成员A成功发布数据流后,会议中其他成员会收到
监听类回调EMConferenceListener#onStreamAdded
如果成员B想看成员A的音视频,可以调用subscribe接口进行订阅
@Override
public void onStreamAdded(final EMConferenceStream stream) {
runOnUiThread(new Runnable() {
@Override
public void run() {
ConferenceMemberView memberView = new ConferenceMemberView(activity);
// 添加当前view到界面
callConferenceViewGroup.addView(memberView);
// 设置当前view的一些状态
memberView.setUsername(stream.getUsername());
memberView.setStreamId(stream.getStreamId());
memberView.setAudioOff(stream.isAudioOff());
memberView.setVideoOff(stream.isVideoOff());
memberView.setDesktop(stream.getStreamType() == EMConferenceStream.StreamType.DESKTOP);
EMClient.getInstance().conferenceManager().subscribe(stream, memberView.getSurfaceView(), new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}
@Override
public void onError(int error, String errorMsg) {
}
});
}
});
}
11 退出会议
成员B可以调用调用EMConferenceManager#exitConference接口离开会议
会议中的其他成员会收到回调EMConferenceListener#onMemberExited
注意:当最后一个成员调用leave接口后,会议会自动销毁
EMClient.getInstance().conferenceManager().exitConference()
@Override
public void onMemberExited(final EMConferenceMember member) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, member.memberName + " removed conference!", Toast.LENGTH_SHORT).show();
}
});
}
进阶功能
会议管理
取日志
SDK会写入日志文件到本地。日志文件路径如下:sdcard/Android/data/(自己App包名)/(appkey)/core_log/easemob.log;
以Demo为例,通过adb命令获取本地的log:
adb pull /sdcard/Android/data/com.hyphenate.chatuidemo/easemob-demo#chatdemoui/core_log/easemob.log
创建会议并加入
SDK还提供了createAndJoinConference接口创建并加入会议
A调用该接口后,将拥有一个会议实例Conference,同时A将成为该Conference的成员且角色是Admin
用户创建会议时可以设置参数指定是否支持小程序音视频,是否需要在服务器端录制,录制时是否合并流
通过这个AIP创建会议以后,其他人可以通过joinConference通过会议ID,密码方式加入会议
createAndJoinConference可以有以下四种方法去创建,请看以下方法详细介绍
**方法一:参数(会议类型,会议密码,是否支持小程序,是否录制,是否合流, 回调);**
public void createAndJoinConference(final EMConferenceType type, final String password,
final boolean isSupportMiniProgram,final
boolean recordOnServer,final boolean mergeStream,
final EMValueCallBack<EMConference> callback)
EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,
password, true, false, false, new EMValueCallBack<EMConference>() {
@Override
public void onSuccess(EMConference value) {
// 返回当前会议对象实例 value
// 可进行推流等相关操作
// 运行在子线程中,勿直接操作UI
}
@Override
public void onError(int error, String errorMsg) {
// 运行在子线程中,勿直接操作UI
}
});
** 方法二:参数(会议类型,会议密码,回调);**
public void createAndJoinConference(final EMConferenceType type, final String password,
final EMValueCallBack<EMConference> callback){}
EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,
password, new EMValueCallBack<EMConference>() {
@Override
public void onSuccess(EMConference value) {
// 返回当前会议对象实例 value
// 可进行推流等相关操作
// 运行在子线程中,勿直接操作UI
}
@Override
public void onError(int error, String errorMsg) {
// 运行在子线程中,勿直接操作UI
}
});
**方法三:参数(会议类型,会议密码,是否录制,是否合流, 回调);**
public void createAndJoinConference(final EMConferenceType type, final String password,
final boolean recordOnServer,final boolean mergeStream,
final EMValueCallBack<EMConference> callback) {};
EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,
password, false, false, new EMValueCallBack<EMConference>() {
@Override
public void onSuccess(EMConference value) {
// 返回当前会议对象实例 value
// 可进行推流等相关操作
// 运行在子线程中,勿直接操作UI
}
@Override
public void onError(int error, String errorMsg) {
// 运行在子线程中,勿直接操作UI
}
});
**方法四:参数(会议类型,会议密码,房间信息对象,推流信息, 回调);**
public void createAndJoinConference(final @NonNull EMConferenceType type, final String password,
final EMRoomConfig roomConfig, final EMStreamParam param,
final EMValueCallBack<EMConference> callback){};
有关EMRoomConfig定义如下:
/**
* 会议房间属性类
* confrTyp 会议类型
* isSupportVxmini 是否支持小程序
* isRecord 是否支持录制
* isMergeRecord 是否支持和流
* nickName 用户昵称
* ext 用户头像
* maxTalkerCount 房间最多主播
* maxVideoCount 房间最多视频流媒体
* maxAudienceCount 房间最多观众
* maxPubDesktopCount 房间最多共享桌面流
*/
public class EMRoomConfig {
EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;
private boolean isSupportMiniProgram = false;
private boolean isRecord = false;
private boolean isMergeRecord = false;
private String nickName = null;
private String ext = null;
private int maxTalkerCount = -1;
private int maxVideoCount = -1;
private int maxAudienceCount = -1;
private int maxPubDesktopCount = -1;
private EMLiveConfig liveConfig = null;
public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
boolean isSupportMiniProgram,boolean isRecord,
boolean isMergeRecord , String nickName,
String ext, int maxTalkerCount,
int maxVideoCount,int maxAudienceCount,
int maxPubDesktopCount)
{...};
}
EMClient.getInstance().conferenceManager().joinConference(confId,
password,new EMValueCallBack<EMConference>() {
@Override
public void onSuccess(EMConference value) {
// 返回当前会议对象实例 value
// 可进行推流等相关操作
// 运行在子线程中,勿直接操作UI
}
@Override
public void onError(int error, String errorMsg) {
// 运行在子线程中,勿直接操作UI
}
});
邀请成员加入会议
SDK没有提供邀请接口,你可以自己实现,比如使用环信IM通过发消息邀请
比如通过发邮件邀请等等。 至于需要发送哪些邀请信息,可以参照SDK中的join接口
目前是需要Conference的confrId和password比如用环信IM发消息邀请,如下所示
final EMMessage message = EMMessage.createTxtSendMessage("邀请你观看直播", to);
message.setAttribute(Constant.EM_CONFERENCE_ID, conference.getConferenceId());
message.setAttribute(Constant.EM_CONFERENCE_PASSWORD, conference.getPassword());
EMClient.getInstance().chatManager().sendMessage(message);
管理员销毁会议
会议中的成员可以调用SDK中的
EMConferenceManager#destoryConference接口销毁会议
会议被销毁以后,会议中的其他成员会收到回调EMConferenceListener#onPassiveLeave
EMClient.getInstance().conferenceManager().destoryConference()
注意:只有管理员角色可以调用这个接口,可以在会议中显式调用这个接口,强制结束进行中的会议,会议中其他人在EMConferenceListener#onPassiveLeave回调里收到 error为 -411,message为 reason-conference-dismissed,收到这个以后调EMClient.getInstance().conferenceManager().exitConference() 主动退出会议。
EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "destroy conference succeed!", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onError(int error, String errorMsg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "destroy conference failed:"+errorMsg, Toast.LENGTH_SHORT).show();
}
});
}
});
设置会议人数限制
通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以设置
房间最多视频主播数 房间最多视频数 房间最多观众 房间最多共享桌面流等会议信息
默认最多主播是100个,最大视频数是9个,房间最多观众是600个 ,最多共享桌面流2个
设置的人数限制有效,如下所示:
/**
* \~chinese
* 加入多人音视频加议房间
* @param room 会议房间名
* @param password 会议房间密码
* @param roletype 当前用户加入时指定角色 (EMConferenceRole类型)
* @param param 设置会议参数 (EMRoomConfig类型)
* @param callback 回调函数
*/
public void joinRoom(final String room ,final String password,final EMConferenceRole roletype ,final EMRoomConfig param, final EMValueCallBack<EMConference> callback)
有关EMRoomConfig定义如下:
/**
* 会议房间属性类
* confrTyp 会议类型
* isSupportVxmini 是否支持小程序
* isRecord 是否支持录制
* isMergeRecord 是否支持和流
* nickName 用户昵称
* ext 用户头像
* maxTalkerCount 房间最多主播
* maxVideoCount 房间最多视频流媒体
* maxAudienceCount 房间最多观众
* maxPubDesktopCount 房间最多共享桌面流
*/
public class EMRoomConfig {
EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;
private boolean isSupportMiniProgram = false;
private boolean isRecord = false;
private boolean isMergeRecord = false;
private String nickName = null;
private String ext = null;
private int maxTalkerCount = -1;
private int maxVideoCount = -1;
private int maxAudienceCount = -1;
private int maxPubDesktopCount = -1;
private EMLiveConfig liveConfig = null;
public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
boolean isSupportMiniProgram,boolean isRecord,
boolean isMergeRecord , String nickName,
String ext, int maxTalkerCount,
int maxVideoCount,int maxAudienceCount,
int maxPubDesktopCount)
{...};
}
注意:设置会议人数限制只有是加入会议的第一个设置才有效,也就是会议的创建者。
获取会议信息
在会议进行中,可以通过getConferenceInfo 方法来查询会议信息,从而可以拿到主播列表,观众人数等信息。
/**
* \~chinese
* 查询会议信息
*
* @param confId 会议id
* @param password 会议密码
* @param callback 获取结果
*/
public void getConferenceInfo(final String confId, final String password,
final EMValueCallBack<EMConference> callback)
/**
* 获取主播列表
*/
public String[] getTalkers()
/**
* 获取观众总数
*/
public int getAudienceTotal()
cdn合流推流
多人音视频支持将会议中的音视频流合并成一个流,推送到第三方的cdn直播服务器。
整个合流推流过程包括开启cdn推流,更新推流布局,停止推流。
开启cdn推流
会议的创建者在创建会议时使用EMRoomConfig的接口,可以决定是否开启cdn推流,推流配置EMLiveConfig是EMRoomConfig的一个参数,可设置cdn推流的相关信息,然后调用 创建会议接口,可以开启cdn推流(注意:只有会议创建者才能开启cdn推流,如果会议已经创建好,其他人再调用开启cdn推流无效)。 开启过程如下:
EMCDNCanvas canvas = new EMCDNCanvas(ConferenceInfo.CanvasWidth, ConferenceInfo.CanvasHeight, 0,30,900,"H264");
String url = PreferenceManager.getInstance().getCDNUrl();
EMLiveConfig liveConfig = new EMLiveConfig(url, canvas);
roomConfig.setLiveConfig(liveConfig);
EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig,
new EMValueCallBack<EMConference>()
当EMCDNCanvas设置的width、height为0时,cdn推流为纯音频推流
推流成功后,可以在EMConferenceManager中 getLiveCfgs() 方法可以获取到liveId,liveCfgs存储了所有的推流信息,liveCfgs为一个Map,key为liveId,value为对应的推流Url。
EMLiveConfig可设置的参数如下:
/**
* \~chinese
* CDN 画布设置,创建会议时使用
* width (画布 宽)
* height(画布 高)
* bgclr (画布 背景色 ,格式为 RGB 定义下的 Hex 值,不要带 # 号,
* 如 0xFFB6C1 表示浅粉色。默认0x000000,黑色)
* fps (推流帧率,可设置范围10-30 )
* kpbs (推流码率,单位kbps,width和height较大时,码率需要提高,可设置范围1-5000)
* codec (推流编码格式,目前只支持"H264")
*
* \~english
* The cdn canvas config
* width (Canvas width)
* height(Canvas height)
* bgclr (Canvas background color, Hex value defined by RGB, no #,
* such as 0xFFB6C1 means light pink.Default 0x000000, black))
* fps (The fps of cdn live,valid valuei is 10-30)
* kpbs (The birateBps of cdn live,the unit is kbps,valid value is 1-5000 )
* codec (The codec of cdn live,now only support H264)
*/
public class EMCDNCanvas {
private int width = -1;
private int height = -1;
private int bgclr = 0;
private int fps = -1;
private int kpbs = -1;
private String codec = null;
public EMCDNCanvas(){
}
public EMCDNCanvas(int width, int height, int bgclr,int fps,int kpbs,String codec){
this.width = width;
this.height = height;
this.bgclr = bgclr;
this.fps = fps;
this.kpbs = kpbs;
this.codec = codec;
}
}
/**
\~chinese
* 音频录制的配置信息
* bps 音频比特率,类型为 EMAudioBpsType枚举;
* channels 音频通道数,可选(1,2),类型为ChannelsType枚举;
* samples 音频采样率,类型为 EMAudioSamplesType枚举;
*
*
* \~english
* The config of audio record.
* bps audio bit rate, type is EMAudioBpsType enum;
* channels number of audio channels, optional (1,2), type is ChannelsType enum;
* samples audio sampling rate, type is EMAudioSamplesType enum;
*
*/
public class EMAudioConfig {
EMAudioBpsType bps = EMAudioBpsType.BPS_64K;
ChannelsType channels = ChannelsType.DUAL;
EMAudioSamplesType samples = EMAudioSamplesType.SAMPLES_16K;
public enum ChannelsType{
SINGLE, //单声道
DUAL //双声道
}
public EMAudioConfig(EMAudioBpsType bps, ChannelsType channels,EMAudioSamplesType samples){
this.bps = bps;
this.channels = channels;
this.samples = samples;
}
}
/**
* \~Chinese
* CDN推流设置
* cdnurl cdn推流地址
* cdnCanvas 画布设置 (cdnCanvas可以缺省)
* record 设置是否录制推往CDN的流
* audioConfig 设置音频录制的配置信息
* liveLayoutStyle 设置CDN推流使用的画布类型
*
* \~English
* The CDN push stream config
* cdnurl cdn push stream address
* cdnCanvas canvas settings (cdnCanvas can be default)
* record whether to enable the recording push network CDN to remain
* audioConfig set the audio recording configuration information
* liveLayoutStyle sets the canvas type used by the CDN push stream
*/
public class EMLiveConfig {
private String cdnurl;
private EMCDNCanvas cdnCanvas = null;
private boolean record = false;
private EMAudioConfig audioConfig = null;
private EMLiveLayoutStyle liveLayoutStyle = EMLiveLayoutStyle.GRID;
public EMLiveConfig(){
}
}
**获取cdn推流LiveId**
推流成功后,可以在EMConferenceManager中 getLiveCfgs() 方法可以获取到liveId,liveCfgs存储了所有的推流信息,liveCfgs为一个Map,key为liveId,value为对应的推流Url。
/**
* CDN推流liveID (key为推流liveID,value为推流URL)
* @return
*/
public Map<String, String> getLiveCfgs();
更新布局
当用户调用更新布局接口后,cdn推流方式将强制变成CUSTOM模式,所有流的位置信息都由用户自己定义。 更新布局的接口如下:
/**
* \~chinese
* CDN 推流更新布局(只有管理员可操作)
* 用户角色: Admin > Talker > Audience
* 注意: 更新布局只允许Admin 去操作
*
* @param liveId 推流CDN的liveId
* @param regions EMCanvasRegion布局对象列表
* @param callback 结果回调
*
* \~english
* CDN pushes to update the layout
*
* @param liveId Push the liveId of CDN
* @param regions Layout EMCanvasRegion list
* @param callback Result callback
*/
public void updateLiveLayout(String liveId ,List<EMLiveRegion> regions , final EMValueCallBack<String> callback){}
EMLiveRegion的结构如下:
/**
* \~Chinese
* 视频流在画布宽高及显示位置等参数
* x 在画布横坐标位置
* y 在画布纵坐标位置
* width 视频流宽度(64~1280)
* height 视频流高度(64~1280)
* zorder 视频流zorder层位置(最小值为 0(默认值),表示该区域图像位于最下层
* 最大值为 100,表示该区域图像位于最上层)
* style 视频流显示方式(fit模式或者fill模式)
* streamId 视频流ID
*
*
* \~English
* Video streaming in the canvas width and height and display location and other parameters
* x On the horizontal position of the canvas
* y On the vertical position of the canvas
* width Video stream width
* height Video stream height
* zorder Video stream zorder layer location
* style Video stream display mode (fit mode or fill mode)
* streamId The video stream ID
*/
public class EMLiveRegion {
private int x;
private int y;
private int width;
private int height;
private int zorder;
private EMRegionStyle style;
private String streamId;
public EMLiveRegion(){}
public EMLiveRegion(int x,int y, int width, int height, int zorder, EMRegionStyle style, String streamId){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.zorder = zorder;
this.style = style;
this.streamId = streamId;
}
public enum EMRegionStyle {
FIT, // fit模式
FILL // fill模式
}
}
使用方法如下:
List<EMLiveRegion> regionsList = new LinkedList<>();
EMLiveRegion region = new EMLiveRegion();
region.setStreamId(streamInfo.getStreamId());
region.setStyle(EMLiveRegion.EMRegionStyle.FILL);
region.setX(80);
region.setY(60);
region.setWidth(320);
region.setHeight(640);
region.setZorder(1);
regionsList.add(region);
Map<String, String> livecfgs = EMClient.getInstance().conferenceManager().getLiveCfgs();
String liveId = null;
Iterator<String> iter = livecfgs.keySet().iterator();
while(iter.hasNext()) {
liveId = iter.next();
break;
}
EMClient.getInstance().conferenceManager().updateLiveLayout(liveId,regionsList,new EMValueCallBack<String>();
多路推流
多人音视频支持加入会议后,增加一路推流,只有管理员权限可进行次操作。增加一路推流的api方法如下:
/**
* \~chinese
* 增加CDN多路推流
* @param liveConfig CDN推流布局配置
* @param callback 结果回调
*
* \~english
* Add CDN multiplex push stream
* @param liveConfig CDN push stream layout configuration
* @param callback Result callback
*/
public void addLiveStream(EMLiveConfig liveConfig , final EMValueCallBack<String> callback){}
自定义录制布局
在推流的EMLiveConfig设置里,设record为true,可以开启自定义录制,开启后会把推流到cdn的音视频按照推流布局录制下来。如果推流时未开启,也可以在推流后进行开启/停止自定义录制布局操作。开启/停止自定义录制布局的api如下:
/**
* \~chinese
* 开启或停止录制CDN的流
* @param liveId CDN推流 liveID
* @param enable 是否开启录制,true为开启 false为停止
* @param callback 结果回调
*
* \~english
* Start or stop recording CDN streams
* @param liveId CDN liveID push stream
* @param enable Whether recording is on or off, true means false or stop
* @param callback Result callback
*
*/
public void enableRecordLivestream(String liveId, boolean enable, final EMValueCallBack<String> callback){}
停止推流
多人音视频支持停止向某一个地址的推流,停止推流接口如下:
/**
* \~chinese
* 停止CDN推流
* @param liveId 推流CDN的liveId
* @param callback 结果回调
*
* \~english
*@param liveId Push the liveId of CDN
*@param callback Result callback
* Stop the CDN push
*
* @param callback Result callback
*/
public void stopLiveStream(String liveId, final EMValueCallBack<String> callback)
云端录制
通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以通过
isRecord(是否支持录制)和isMergeRecord(是否支持和流)这两个参数
可以设置是否开始云端录制和合流,如下所示:
/**
* \~chinese
* 加入多人音视频加议房间
* @param room 会议房间名
* @param password 会议房间密码
* @param roletype 当前用户加入时指定角色 (EMConferenceRole类型)
* @param param 设置会议参数 (EMRoomConfig类型)
* @param callback 回调函数
*/
public void joinRoom(final String room ,final String password,final EMConferenceRole roletype ,
final EMRoomConfig param, final EMValueCallBack<EMConference> callback)
有关EMRoomConfig定义如下:
/**
* 会议房间属性类
* confrTyp 会议类型
* isSupportVxmini 是否支持小程序
* isRecord 是否支持录制
* isMergeRecord 是否支持和流
* nickName 用户昵称
* ext 用户头像
* maxTalkerCount 房间最多主播
* maxVideoCount 房间最多视频流媒体
* maxAudienceCount 房间最多观众
* maxPubDesktopCount 房间最多共享桌面流
*/
public class EMRoomConfig {
EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;
private boolean isSupportMiniProgram = false;
private boolean isRecord = false;
private boolean isMergeRecord = false;
private String nickName = null;
private String ext = null;
private int maxTalkerCount = -1;
private int maxVideoCount = -1;
private int maxAudienceCount = -1;
private int maxPubDesktopCount = -1;
private EMLiveConfig liveConfig = null;
public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
boolean isSupportMiniProgram,boolean isRecord,
boolean isMergeRecord , String nickName,
String ext, int maxTalkerCount,
int maxVideoCount,int maxAudienceCount,
int maxPubDesktopCount)
{...};
}
注意:设置会议人数限制只有是加入会议的第一个设置才有效,也就是会议的创建者。
设置昵称 头像
通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以通过
nickName(昵称)ext(可设置扩展字段比如 用户头像等)这两个参数
可以设置昵称和头像等扩展字段,如下所示:
/**
* \~chinese
* 加入多人音视频加议房间
* @param room 会议房间名
* @param password 会议房间密码
* @param roletype 当前用户加入时指定角色 (EMConferenceRole类型)
* @param param 设置会议参数 (EMRoomConfig类型)
* @param callback 回调函数
*/
public void joinRoom(final String room ,final String password,final EMConferenceRole roletype ,
final EMRoomConfig param, final EMValueCallBack<EMConference> callback)
有关EMRoomConfig定义如下:
/**
* 会议房间属性类
* confrTyp 会议类型
* isSupportVxmini 是否支持小程序
* isRecord 是否支持录制
* isMergeRecord 是否支持和流
* nickName 用户昵称
* ext 用户头像
* maxTalkerCount 房间最多主播
* maxVideoCount 房间最多视频流媒体
* maxAudienceCount 房间最多观众
* maxPubDesktopCount 房间最多共享桌面流
*/
public class EMRoomConfig {
EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;
private boolean isSupportMiniProgram = false;
private boolean isRecord = false;
private boolean isMergeRecord = false;
private String nickName = null;
private String ext = null;
private int maxTalkerCount = -1;
private int maxVideoCount = -1;
private int maxAudienceCount = -1;
private int maxPubDesktopCount = -1;
private EMLiveConfig liveConfig = null;
public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
boolean isSupportMiniProgram,boolean isRecord,
boolean isMergeRecord , String nickName,
String ext, int maxTalkerCount,
int maxVideoCount,int maxAudienceCount,
int maxPubDesktopCount)
{...};
}
会议属性
会议属性是会议的状态信息,由一组(key,value)组成 会议中的所有角色成员(管理员、主播、观众)都可以设置/删除会议频道属性 设置的会议属性会通知给会议中的所有人 设置会议属性的api方法如下
/**
* \~chinese
* 设置频道属性,该会议中的所有人(包括自己)都会收到{@link EMConferenceListener#onAttributesUpdated}回调.
* 该方法需要在加入会议后调用.
*
* @param key
* @param value
* @param callback
*
* \~english
* Set conference attribute,All members in this conference(include myself) will receive a callback
* in {@link EMConferenceListener#onAttributesUpdated}.
* this method can only be used after join a conference.
*
* @param key
* @param value
* @param callback
*/
public void setConferenceAttribute(@NonNull String key, @NonNull String value, final EMValueCallBack<Void> callback) {}
删除会议属性的api方法如下:
/**
* \~chinese
* 删除频道属性,该会议中的所有人(包括自己)都会收到{@link EMConferenceListener#onAttributesUpdated}回调.
* 该方法需要在加入会议后调用.
*
* @param key
* @param callback
*
* \~english
* Delete conference attribute,All members in this conference(include myself) will receive a callback
* in {@link EMConferenceListener#onAttributesUpdated}.
* this method can only be used after join a conference.
*
* @param key
* @param callback
*/
public void deleteConferenceAttribute(@NonNull String key, final EMValueCallBack<Void> callback) {}
当会议属性信息改变时,会议中的在EMConferenceListener中的 onAttributesUpdated 回调方法收到通知,如下:
public void onAttributesUpdated(EMConferenceAttribute[] attributes) {}
每一个EMConferenceAttribute包括了会议属性中的key,value,以及本次修改的action,action包括ADD、UPDATE、DELETE
海外代理
1v1通话支持不同集群区域的人员通话使用代理,减小延迟;
使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,并禁用相关appkey的直连,
启用多集群代理功能需要配置如下方法:
EMOptions options = new EMOptions();
//开启代理
options.setUseRtcConfig(true);
私有部署
私有部署设置方法参见私有云sdk集成配置。
音视频管理
设置视频流参数
如果用户想对发布的音频或者视频流的参数进行调整,可以进行一些设置
用EMStreamParam中的参数进行设置,只发布音频流,可以设置关闭视频
设置视频流的分辨率 最小码率 最大码率 最大音频码率等,如下所示:
//设置分辨率
normalParam.setVideoOff(true);
normalParam.setVideoHeight(720);
normalParam.setVideoWidth(1280);
//设置最大 最小码率
normalParam.setMinVideoKbps(200);
normalParam.setMaxVideoKbps(300);
normalParam.setMaxAudioKbps(300);
设置流畅度或者清晰度优先
可以通过以下方法设置视频流畅度优先还是清晰度优先(true 为清晰度优先,false为流畅度优先)。
EMClient.getInstance().callManager().getCallOptions().setClarityFirst(true);
停止发布流
成员A可以调用unpublish接口取消自己已经发布的数据流,操作成功后,会议中的其他成员会收到回调EMConferenceListener#onStreamRemoved(EMConferenceStream stream)
,将对应的数据流信息移除
@Override
public void onStreamRemoved(final EMConferenceStream stream) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, stream.getUsername() + " stream removed!", Toast.LENGTH_SHORT).show();
if (streamList.contains(stream)) {
removeConferenceView(stream);
}
}
});
}
停止订阅流
成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe
EMClient.getInstance().conferenceManager().unsubscribe(emConferenceStream, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}
@Override
public void onError(int error, String errorMsg) {
}
});
通话中音视频控制
会议过程中可以做一些音视频进行控制,比如开启关闭音频 视频 切换前后摄像头
具体API如下所示:
// 开启音频传输
EMClient.getInstance().conferenceManager().openVoiceTransfer();
// 关闭音频传输
EMClient.getInstance().conferenceManager().closeVoiceTransfer();
// 开启视频传输
EMClient.getInstance().conferenceManager().openVideoTransfer();
// 关闭视频传输
EMClient.getInstance().conferenceManager().closeVideoTransfer();
PS:以上这四个方法都是修改 stream,群里其他成员都会收到 EMConferenceListener.onStreamUpdate()回调
// 切换摄像头
EMClient.getInstance().conferenceManager().switchCamera();
// 设置展示本地画面的 view
EMClient.getInstance().conferenceManager().setLocalSurfaceView(localView);
// 更新展示本地画面 view
EMClient.getInstance().conferenceManager().updateLocalSurfaceView(localView);
// 更新展示远端画面的 view
EMClient.getInstance().conferenceManager().updateRemoteSurfaceView(streamId, remoteView);
音视频首帧回调
当成员发布流成功,发送第一帧音视频数据时,会触发EMConferenceListener的以下回调函数
/**
* \~chinese
* Pub 首帧回调
* streamId 流ID
* frameType the first frame callback type of the stream
*
* \~english
* Pub first frame callback
* streamId streamId
* frameType the first frame callback type of the stream
*/
public void onFirstFrameSent(String stremId,StreamFrameType frameType){}
当成员订阅流成功,收到第一帧音视频数据时,会触发EMConferenceListener的以下回调函数
/**
* \~chinese
* Sub 首帧回调
* streamId 流ID
* frameType the first frame callback type of the stream
*
* \~english
* Sub first frame callback
* streamId streamId
* state the first frame callback type of the stream
*
*/
public void onFirstFrameRecived(String streamId,StreamFrameType frameType){}
音视频无数据回调
当会议中的成员A因断网或异常退出,而无音视频数据上传时,订阅该流的其他成员会收到EMConferenceListener 中的以下回调通知。
/**
* \~chinese
* 订阅流的数据状态回调
* @param streamId 订阅的流ID
* @param state 流的视频或音频数据状态
*
* \~english
* A data state callback for a subscription stream
* @param streamId StreamId
* @param state Stream video or audio data status
*/
default void onStreamStateUpdated(String streamId,StreamState state){};
该功能需要会议中开启质量统计
/**
* \~chinese
* 启用统计
*
* @param enable 是否启用统计
*
* \~english
* enable statistics
* @params enable enable statistics
*/
public void enableStatistics(boolean enable) {};
弱网监控
在会议中可以实时获取到自己的网络状态,通过EMConferenceListener中的以下回调通知
@Override
public void onConferenceState(final ConferenceState state) {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
//有以下状态
STATE_NORMAL, // 正常状态
STATE_DISCONNECTION, // 连接断开
STATE_RECONNECTION, // 重新连接
STATE_POOR_QUALITY, // 通话质量差
通话质量
通过过程中可以实时获取到自己活着订阅流的通话信息,比如视频宽高 丢包率 帧率等等
具体信息可参考 EMStreamStatistics
该功能需要会议中开启质量统计,如下面的接口:
/**
* \~chinese
* 启用统计
*
* @param enable 是否启用统计
*
* \~english
* enable statistics
* @params enable enable statistics
*/
public void enableStatistics(boolean enable) {};
开启质量统计后,在视频过程中会实时触发EMConferenceListener 中的回调函数 onStreamStatistics
如下所示,从而可以获取到每路流的质量统计信息:
@Override
public void onStreamStatistics(EMStreamStatistics statistics) {
EMLog.e(TAG, "Encode Resolution: " + statistics.getLocalEncodedWidth() + " "
+ statistics.getLocalEncodedHeight() + " bps: " +statistics.getLocalVideoActualBps() +
" FPS: " + statistics.getLocalEncodedFps());
}
监听谁在说话
多人音视频会议可以实时监听谁在说话,该功能需要开启,启动/停止控制如下:
//开始监听说话者,参数为间隔时间
EMClient.getInstance().conferenceManager().startMonitorSpeaker(int interval);
//停止监听说话者
EMClient.getInstance().conferenceManager().stopMonitorSpeaker();
有人说话时,会议成员会收到如下回调通知
@Override
public void onSpeakers(final List<String> speakers)(){ }
mute远端音视频流
在视频会议过程中可以 mute订阅的流的音频或者视频 方法如下所示:
/**
* \~chinese
* mute远端音频
*
* \~english
* Mute remote audio
*
* @param mute
*/
public void muteRemoteAudio(String streamId, boolean mute) {
mediaManager.muteRemoteAudio(streamId, mute);
}
/**
* ~\chinese
* mute远端视频
*
* \~english
* Mute remote video
*
* @param mute
*/
public void muteRemoteVideo(String streamId, boolean mute) {
mediaManager.muteRemoteVideo(streamId, mute);
}
变声/自定义音频
开启外边音频输入
用户使用自定义音频数据时,需要配置外部输入音频数据的开关,
以及音频采样率,通道数(当前通道数只支持1),开启方式如下(true 为开启,false为不开启)。
EMClient.getInstance().callManager().getCallOptions().setExternalAudioParam(true, 44100,1);
输入音频数据
音频数据采集可参考Demo中的ExternalAudioInputRecord.java类实现,
音频数据的输入必须在会话接通后开始,否则会导致网络阻塞,影响通话质量。
建议用户将音频数据采集的开始放在会话接通的回调里,及callDidAccept回调中
EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
//如果启动外部音频输入 ,启动音频录制
if(PreferenceManager.getInstance().isExternalAudioInputResolution()){
ExternalAudioInputRecord.getInstance().startRecording();
}
....
}
音频采集过程参考Demo中的ExternalAudioInputRecord类实现;
音频采集过程开始后,在音频数据采集线程里调用外部输入音频数据接口,具体参考Demo中的实现。
EMClient.getInstance().conferenceManager().inputExternalAudioData(byteBuffer.array(), byteBuffer.capacity());
停止音频输入
会话挂断时,停止音频采集及输入过程
if(PreferenceManager.getInstance().isExternalAudioInputResolution()){
ExternalAudioInputRecord.getInstance().stopRecording();
}
美颜/自定义视频
如果用户需要自己采集特定的数据或者对于数据需要先进行一些处理,可以使用SDK的外部输入数据的方法进行。
例如如果想要使用美颜等功能,需要用户使用系统的摄像头,
然后启动监听系统设备,获取到数据后进行处理,处理后再调用我们输入数据的api发布出去。
使用自定义视频接口如下:开启外边视频输入;
用户使用自定义视频数据时,需要配置外部输入数据数据的开关(true 为开启,false为不开启)。
EMStreamParam normalParam = new EMStreamParam();
normalParam.setUsingExternalSource(true); //设置使用外部视频数据输入
EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {}
输入视频数据
然后就是自己获取视频数据,进行美颜等处理,循环调用以下方法输入数据就行了,
这个调用频率就相当于你的帧率,调用间隔可以自己进行控制,一般最大30帧/秒)
输入视频数据的方法如下:
/**
*
* 视频数据的格式是摄像头采集的格式即:NV21 420sp 自己手动传入时需要将自己处理的数据转为 yuv 格式输入
*/
EMClient.getInstance().conferenceManager().inputExternalVideoData(data, width, height, rotate);
视频水印
在Android系统可以将图片资源设置为视频流的水印。首先将图片资源转换为Bitmap对象,
然后设置WaterMarkOption中的属性,比如位置,分辨率及距离边缘的margin等。
try {
InputStream in = this.getResources().getAssets().open("watermark.png");
watermarkbitmap = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
watermark = new WaterMarkOption(watermarkbitmap, 75, 25, WaterMarkPosition.TOP_RIGHT, 8, 8);
//设置水印
EMClient.getInstance().conferenceManager().setWaterMark(watermark);
共享桌面
在android 5.0以上系统中,可以使用外部输入视频数据的方法,将采集的桌面图像数据发布出去。 主要方法如下:
desktopParam = new EMStreamParam();
desktopParam.setAudioOff(true);
desktopParam.setVideoOff(true);
desktopParam.setStreamType(EMConferenceStream.StreamType.DESKTOP);
public void publishDesktop() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
desktopParam.setShareView(null);
} else {
desktopParam.setShareView(activity.getWindow().getDecorView());
}
EMClient.getInstance().conferenceManager().publish(desktopParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.DESKTOP);
startScreenCapture();
}
@Override
public void onError(int error, String errorMsg) {
}
});
}
private void startScreenCapture() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (ScreenCaptureManager.getInstance().state == ScreenCaptureManager.State.IDLE) {
ScreenCaptureManager.getInstance().init(activity);
ScreenCaptureManager.getInstance().setScreenCaptureCallback(new ScreenCaptureManager.ScreenCaptureCallback() {
@Override
public void onBitmap(Bitmap bitmap) {
EMClient.getInstance().conferenceManager().inputExternalVideoData(bitmap);
}
});
}
}
}
具体实现可以参考videocall-android中的ConferenceActivity.java文件
角色管理
角色管理
申请主播
会议中的观众角色可以向管理员发申请成为主播,管理员可以选择同意或者拒绝
观众申请管理员的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName
然后根据memName以及成员加入的回调中获取到的EMConferenceMember,获取到memId接口如下
/**
* \~chinese
* 发送上麦请求
*
* @param memberId 管理员的memberId(只有管理员可处理上麦请求);
*
* \~english
* Request to be speaker
*
* @param memberId of the admin (only the admin can process the request on the mic);
*/
public void applyTobeSpeaker(String memberId);
观众发出申请后,管理员将会收到以下回调:
/**
* \~chinese
* 请求上麦通知 (只有管理员能收到)
*
* \~english
* Request On wheat notification
*/
default void onReqSpeaker(String memId,String memName,String nickName);
在回调中,管理员可以选择同意或者拒绝;
同意:先调用改变用户权限的接口,然后调用回复接口;
拒绝:则直接调用回复接口。
修改用户的权限接口如下:
/**
* \~chinese
* 用户角色: Admin > Talker > Audience
* 当角色升级时,用户需要给管理员发送申请,管理通过该接口改变用户接口.
* 当角色降级时,用户直接调用该接口即可.
* 注意: 暂时不支持Admin降级自己
*
* @param confId 会议id
* @param member {@link EMConferenceMember},目前使用memberName进行的操作
* @param toRole 目标角色,{@link EMConferenceRole}
* @param callback 结果回调
*
* \~english
* Role: Admin > Talker > Audience
* When role upgrade, you need to send a request to Admin, only Admin can upgrade a role.
* When role degrade, you can degrade with this method yourself.
* Attention: Admin can not degrade self.
*
* @param confId Conference id
* @param member {@link EMConferenceMember}
* @param toRole Target role,{@link EMConferenceRole}
* @param callback Result callback
*/
public void grantRole(final String confId, final EMConferenceMember member, final EMConferenceRole toRole,
final EMValueCallBack<String> callback);
回复接口如下:
/**
* \~chinese
* 管理员处理 上麦请求
* 注意:只允许Admin 去操作
*
* @param memberId 请求者 memberId;
* @param accept true 代表同意 false 代表不同意
*
* \~english
* Admin handle to be speak requests
*
* @param memberId requestor memberId;
* @param accept true means agree , false means disagree
* */
public void handleSpeakerApplication(String memberId, boolean accept);
申请管理员
会议中的主播角色可以向管理员发申请成为管理员,
管理员可以选择同意或者拒绝,
成为管理员后,各管理员之间的权限是相同的
主播申请管理员的接口需要管理员的memId,
先通过获取会议属性接口获取到管理员的memName,
然后根据memName以及成员加入的回调中获取到的EMConferenceMember,
获取到管理员memId。接口如下:
/**
* \~chinese
* 发送申请管理员请求
*
* @param memberId 管理员的memberId;
*
* \~english
* Request to be admin
*
* @param memberId of the admin;
*/
public void applyTobeAdmin(String memberId);
主播发出申请后,管理员将会收到以下回调:
/**
* \~chinese
* 请求成为管理员通知(只有管理员能收到)
*
* \~english
* Request administrator notification
*/
default void onReqAdmin(String memId,String memName,String nickName){}
在回调中,管理员可以选择同意或者拒绝;
同意:先调用改变用户权限的接口,然后调用回复接口;
拒绝:则直接调用回复接口。
修改用户的权限接口如下:
/**
* \~chinese
* 用户角色: Admin > Talker > Audience
* 当角色升级时,用户需要给管理员发送申请,管理通过该接口改变用户接口.
* 当角色降级时,用户直接调用该接口即可.
* 注意: 暂时不支持Admin降级自己
*
* @param confId 会议id
* @param member {@link EMConferenceMember},目前使用memberName进行的操作
* @param toRole 目标角色,{@link EMConferenceRole}
* @param callback 结果回调
*
* \~english
* Role: Admin > Talker > Audience
* When role upgrade, you need to send a request to Admin, only Admin can upgrade a role.
* When role degrade, you can degrade with this method yourself.
* Attention: Admin can not degrade self.
*
* @param confId Conference id
* @param member {@link EMConferenceMember}
* @param toRole Target role,{@link EMConferenceRole}
* @param callback Result callback
*/
public void grantRole(final String confId, final EMConferenceMember member, final EMConferenceRole toRole,
final EMValueCallBack<String> callback);
回复接口如下:
/**
* \~chinese
* 管理员处理 申请管理员请求
* 注意: 只允许Admin 去操作
*
* @param memberId 请求者 memberId;
* @param accept true 代表同意 false 代表不同意
*
* \~english
* Admin handle to be admin requests
*
* @param memberId requestor memberId;
* @param accept true means agree false means disagree
* */
public void handleAdminApplication(String memberId, boolean accept);
管理员踢人
管理员可以调用kickMember api强制将成员从会议中移除。
注意:只有管理员角色可以调用踢人接口,可以在会议中踢走主播被踢的主播在EMConferenceListener#onPassiveLeave回调里
收到 error为 -412,message为 reason-beenkicked 收到这个以后调用 EMClient.getInstance().conferenceManager().
exitConference()主动退出会议。
/**
* \~chinese
* 踢走会议中存在的主播
* 用户角色: Admin > Talker > Audience
* 注意: 踢人只允许Admin 去操作,Admin 不能踢自己
*
* @param confId 会议id
* @param members 移除的主播列表
* @param callback 结果回调
*
* \~english
* Remove talkers from the Conference
*
* @param confId Conference id
* @param members Removed list of talkers
* @param callback Result callback
*/
public void kickMember(final String confId, final List<String> members, final EMValueCallBack<String> callback)
调用踢人接口,会议中被踢的主播收到回调
EMConferenceListener#onPassiveLeave(final int error, final String message)
全体静音/管理员指定成员静音
管理员可以对会议中的指定成员进行静音/解除静音设置
被指定成员可以是主播也可以是管理员
设置后,被指定成员将静音/解除静音。只有管理员可以调用此接口。
接口API如下:
/**
* \~chinese
* 发送静音命令
* 注意: 只允许Admin 去操作
*
* @param memberId memberId 被静音的成员的memberId;
*
* \~english
* Send the mute command
*
* @param memberId MemberId of the member being mute (only admin can sends mute command);
*
* */
public void muteMember(String memberId);
/**
* \~chinese
* 发送解除静音命令
* 注意: 只允许Admin 去操作
*
* @param memberId 被取消静音的成员的memberId;
*
* \~english
* Send the unmute command
*
* @param memberId Send the memberId of the member whose unmute command is cancelled;
*
*
* */
public void unmuteMember(String memberId);
管理员调用此接口后,被指定的成员将收到静音状态的回调,回调函数如下
/**
* \~chinese
* 被静音通知
*
* \~english
* Be muted notification
*/
default void onMute(String adminId, String memId){};
/**
* \~chinese
* 被取消静音通知
*
* \~english
* Be unmuted notification
*/
default void onUnMute(String adminId, String memId){};
本身角色变更回调
在会议中如果 申请主播或者管理员成功等角色改变 则触发EMConferenceListener中以下角色变更回调
回调中参数为自己目前的角色
@Override
public void onRoleChanged(EMConferenceManager.EMConferenceRole role) {
EMLog.i(TAG, "onRoleChanged, role: " + role);
}
管理员变更回调
在会议中如果其他人新增管理员或者移除管理员则会触发 EMConferenceListener中以下回调函数
/**
* 管理员增加回调
*
* @param streamId
*/
@Override
public void onAdminAdded(String streamId){
...
}
/**
* 管理员移除回调
*
* @param streamId
*/
@Override
public void onAdminRemoved(String streamId) {
...
}
客户端api
多人视频会议的API包括以下接口
- EMConferenceManager 会议管理单例类
- EMConference 会议实体类
- EMConferenceListener 会议监听类
- EMConferenceMember 会议人员实体类
- EMConferenceStream 会议流实体类
- EMStreamParam 会议流参数类
- EMStreamStatistics 音视频通话统计信息实体类
- EMConferenceAttribute 会议属性实体类
- EMRoomConfig 会议房间属性类
- EMLiveConfig CDN推流设置类
- EMAudioConfig 音频录制的配置信息
- EMCDNCanvas CDN画布设置类
- EMLiveRegion 视频流在画布宽高及显示位置等参数类
EMConferenceManager
EMConference
属性 | 描述 |
---|---|
PubStreamId | pub流id |
ConferenceId | 会议id |
ConferenceType | 会议类型 |
Admins | 会议中的管理员列表 |
PubStreamId | pub流id |
Talkers | 会议中的主播列表 |
RecordOnServer | 是否开启云端录制 |
AudienceTotal | 会议中的观众人数 |
PubStreamId | pub流id |
PubStreamId | pub流id |
PubStreamId | pub流id |
Extension | 会议扩展信息 |
EMConferenceListener
方法 | 描述 |
---|---|
onMemberJoined | 成员加入会议 |
onMemberExited | 成员离开会议 |
onStreamAdded | 有新的成员推流 |
onStreamUpdate | 有成员更新自己的推流 |
onPassiveLeave | 被动离开会议 |
onAdminAdded | 管理员增加通知 |
onAdminRemoved | 管理员移除通知 |
onApplyAdminRefused | 申请管理员失败通知(只有申请管理者收到) |
onApplySpeakerRefused | 申请主播失败通知(只有申请管理者收到) |
onPubStreamFailed | pub 流失败 |
onUpdateStreamFailed | update 流失败 |
onConferenceState | 会议状态通知回调 |
onStreamStatistics | 统计信息回调 |
onStreamSetup | 推本地流或订阅成员流成功回调 |
onStreamStateUpdated | 订阅流的数据状态回调 |
onSpeakers | 当前说话者回调 |
onReceiveInvite | 收到会议邀请 |
onRoleChanged | 当前登录用户角色被管理员改变 |
onReqSpeaker | 请求成为主播通知 |
onReqAdmin | 请求成为管理员通知 |
onMuteAll | 被全体静音 |
onMute | 被静音通知 |
onUnMute | 被取消静音通知 |
onGetLivecfg | 获取直播推流CDN 信息 |
onGetLivecfg | 获取直播推流CDN 信息 |
onGetLocalStreamId | 获取自己StreamId |
onPubDesktopStreamFailed | 发布共享桌面流失败回调 |
onFirstFrameSent | Pub 首帧回调streamId流ID |
onFirstFrameRecived | Pub 首帧回调 |
onFirstFrameSent | Sub 首帧回调 |
EMConferenceMember
属性 | 描述 |
---|---|
memberName | 会议成员Name |
memberId | 会议成员Id |
nickName | 会议成员昵称 |
extension | 会议成员扩展字段 |
EMConferenceStream
属性 | 描述 |
---|---|
StreamId | 流id |
StreamName | 流Name |
MemberName | 流的所属成员的MemberName |
Username | 流的所属成员的Username |
Extension | 流的扩展字段 |
VideoOff | 流视频是否关闭 |
AudioOff | 流音频是否关闭 |
StreamType | 流的类型 |
EMStreamParam
属性 | 描述 |
---|---|
name | 推流配置名称 |
videoOff | 是否关闭视频 |
audioOff | 是否静音 |
useBackCamera | 使用后置摄像头 |
videoHeight | 视频高度 |
videoWidth | 视频宽度 |
extension | 扩展字段 |
maxAudioKbps | 设置最大音频比特率 |
minVideoKbps | 设置最小的网络带宽 |
maxVideoKbps | 最大视频码率 |
streamType | stream 类型 |
shareView | 分享的view |
audioSampleRate | 设置音频采样频率 |
EMStreamStatistics
音视频通话统计信息实体类,具体请看EMStreamStatistics 的详细介绍
EMConferenceAttribute
会议属性设置类,具体请看EMConferenceAttribute 的详细介绍
EMRoomConfig
会议房间属性类 ,具体请看EMRoomConfig 的详细介绍
EMLiveConfig
CDN推流设置类,具体请看EMLiveConfig 的详细介绍
EMAudioConfig
自定义录制格式定义类,具体请看EMAudioConfig 的详细介绍
EMCDNCanvas
CDN 画布设置类,具体请看EMCDNCanvas 的详细介绍
EMLiveRegion
视频流在画布宽高及显示位置等参数类,具体请看EMLiveRegion 的详细介绍