之前写过关于集成腾讯直播的一些关键地方,但是比较分散,加之随心播新版本对权限角色做了升级,这对优化流量计费有很大帮助,因此,升级必不可少。
随心播下载地址:https://github.com/zhaoyang21cn/Android_Suixinbo
首先是集成流程,文档都提到。
1,配置为jcenter库
2,使用proguard等工具做了代码混淆
-keep class com.tencent.**{*;}
-dontwarn com.tencent.**
-keep class tencent.**{*;}
-dontwarn tencent.**
-keep class qalsdk.**{*;}
-dontwarn qalsdk.**
3,配置权限和服务
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<!--<uses-feature android:name="android.hardware.camera" />-->
服务
<!--TLS Qal 一些服务 -->
<service
android:name="com.tencent.qalsdk.service.QalService"
android:exported="false"
android:process=":QALSERVICE" />
<receiver
android:name="com.tencent.qalsdk.QALBroadcastReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.tencent.qalsdk.broadcast.qal" />
</intent-filter>
</receiver>
<receiver
android:name="com.tencent.qalsdk.core.NetConnInfoCenter"
android:process=":QALSERVICE">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIME_SET" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
</receiver>
初步配置搭建完成。
其次看一下代码结构
demo采用的是MVP结构,便于重构。
Views: 所有界面类,包括登录、主界面、直播界面以及一些自定义控件。
Presenters : 所有界面的逻辑操作,包括初始化逻辑,进出房间逻辑,直播交互逻辑,登录逻辑等。以及逻辑操作的回调接口,这些接口会被需要对应功能的界面实现。
Model : 数据类包括当前房间信息类,个人状态类,文本消息类,AV成员类。
AVControllers 里面保留了AVSDK一些操作类包括显示控制类,AVSDK初始化类
典型MVP操作流程示例 View有某些功能,持有对应功能的Presenter类,View触发功能,调用对应的Persenter方法,Presenter将处理结果通过ViewInface接口类回调给对应的View. View根据数据进行界面显示。 View类只做界面相关事情,数据逻辑都丢给Presenter处理。
申请创建应用后,会得到两个参数,在constans中修改如下:
public static final int SDK_APPID = 1400001692;
public static final int ACCOUNT_TYPE = 884;
然后是登录注册流程
在LoginActivity中处理如上逻辑
在onCreate()中
//获取个人数据本地缓存
MySelfInfo.getInstance().getCache(getApplicationContext());
initView();
if (!needLogin()) {
//本地没有账户需要登录
mTvWelcome.setVisibility(View.VISIBLE);
//有账户登录直接IM登录
SxbLog.i(TAG, "LoginActivity onCreate");
mLoginHeloper.imLogin(MySelfInfo.getInstance().getId(), MySelfInfo.getInstance().getUserSig());
}
首先从本地获取账号,如果本地存在账号就用本地存在账号进行登录,调用imLogin()方法,传入用户id和usersig
needLogin
/**
* 判断是否需要登录
*
* @return true 代表需要重新登录
*/
public boolean needLogin() {
if (MySelfInfo.getInstance().getId() != null) {
return false;//有账号不需要登录
} else {
return true;//需要登录
}
}
至于用户id和用户签名(usersig)如何获取,在点击登录按钮的的时候,会调用
mLoginHeloper.tlsLogin(mUserName.getText().toString(), mPassWord.getText().toString());
登录TLS账号系统
/**
* 登录TLS账号系统
*
* @param id
* @param password
*/
public void tlsLogin(String id, String password) {
int ret = InitBusinessHelper.getmLoginHelper().TLSPwdLogin(id, password.getBytes(), new TLSPwdLoginListener() {
@Override
public void OnPwdLoginSuccess(TLSUserInfo tlsUserInfo) {//获取用户信息
// Toast.makeText(mContext, "TLS login succ ! " + tlsUserInfo.identifier, Toast.LENGTH_SHORT).show();
// SxbLog.i(TAG, "TLS OnPwdLoginSuccess " + tlsUserInfo.identifier);
String userSig = InitBusinessHelper.getmLoginHelper().getUserSig(tlsUserInfo.identifier);
MySelfInfo.getInstance().setId(tlsUserInfo.identifier);
MySelfInfo.getInstance().setUserSig(userSig);
imLogin(tlsUserInfo.identifier, userSig);
}
@Override
public void OnPwdLoginReaskImgcodeSuccess(byte[] bytes) {
}
@Override
public void OnPwdLoginNeedImgcode(byte[] bytes, TLSErrInfo tlsErrInfo) {
}
@Override
public void OnPwdLoginFail(TLSErrInfo tlsErrInfo) {
SxbLog.e(TAG, "OnPwdLoginFail " + tlsErrInfo.Msg);
Toast.makeText(mContext, "OnPwdLoginFail:\n" + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
}
@Override
public void OnPwdLoginTimeout(TLSErrInfo tlsErrInfo) {
SxbLog.e(TAG, "OnPwdLoginTimeout " + tlsErrInfo.Msg);
Toast.makeText(mContext, "OnPwdLoginTimeout:\n" + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
}
});
if (ret != -1001) {
Toast.makeText(mContext, "input invalid !", Toast.LENGTH_SHORT).show();
}
}
不管是第三方账号系统还是本地账号系统都需要去腾讯的tls账号系统进行注册登录。
可以看出,tls登录成功后返回
String userSig = InitBusinessHelper.getmLoginHelper().getUserSig(tlsUserInfo.identifier);
MySelfInfo.getInstance().setId(tlsUserInfo.identifier);
MySelfInfo.getInstance().setUserSig(userSig);
这里拿到id和usersig,为im登录做准备,IM登录
/**
* 登录imsdk
*
* @param identify 用户id
* @param userSig 用户签名
*/
public void imLogin(final String identify, String userSig) {
TIMUser user = new TIMUser();
user.setAccountType(String.valueOf(Constants.ACCOUNT_TYPE));
user.setAppIdAt3rd(String.valueOf(Constants.SDK_APPID));
user.setIdentifier(identify);
//发起登录请求
TIMManager.getInstance().login(
Constants.SDK_APPID,
user,
userSig, //用户帐号签名,由私钥加密获得,具体请参考文档
new TIMCallBack() {
@Override
public void onError(int i, String s) {
SxbLog.e(TAG, "IMLogin fail :" + i + " msg " + s);
Toast.makeText(mContext, "IMLogin fail :" + i + " msg " + s, Toast.LENGTH_SHORT).show();
if (mLoginView != null) {
mLoginView.loginFail();
}
}
@Override
public void onSuccess() {
SxbLog.i(TAG, "keypath IMLogin succ !");
// Toast.makeText(mContext, "IMLogin succ !", Toast.LENGTH_SHORT).show();
SxbLog.d(TAG, LogConstants.ACTION_HOST_CREATE_ROOM + LogConstants.DIV + identify + LogConstants.DIV + "request room id");
getMyRoomNum();
startAVSDK();
}
});
}
只有在IM登录成功之后,启动AVSDK,必须遵守这个顺序。
登录成功如果需要发布直播间,需要获取房间id,这个需要通过接口来获取。这样登录流程就结束了
注册流程,调用如下
/**
* 在TLS模块注册一个账号
*
* @param id
* @param psw
*/
public void tlsRegister(final String id, final String psw) {
int ret = InitBusinessHelper.getmAccountHelper().TLSStrAccReg(id, psw, new TLSStrAccRegListener() {
@Override
public void OnStrAccRegSuccess(TLSUserInfo tlsUserInfo) {
Toast.makeText(mContext, tlsUserInfo.identifier + " register a user succ ! ", Toast.LENGTH_SHORT).show();
//继续登录流程
tlsLogin(id, psw);
}
@Override
public void OnStrAccRegFail(TLSErrInfo tlsErrInfo) {
Toast.makeText(mContext, " register a user fail ! " + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
}
@Override
public void OnStrAccRegTimeout(TLSErrInfo tlsErrInfo) {
Toast.makeText(mContext, " register timeout ! " + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
}
});
if (ret != -1001) {
Toast.makeText(mContext, "input invalid !", Toast.LENGTH_SHORT).show();
}
}
这是在注册成功之后直接登录
发布一个直播流程
进入直播间(LiveActivity)
会直接调用mEnterRoomHelper.startEnterRoom();进入房间
startEnterRoom
/**
* 进入一个直播房间流程
*/
public void startEnterRoom() {
if (MySelfInfo.getInstance().isCreateRoom() == true) {
createLive();
} else {
SxbLog.i(TAG, "joinLiveRoom startEnterRoom ");
joinLive(CurLiveInfo.getRoomNum());
}
}
这里MySelfInfo.getInstance().isCreateRoom()在发布房间时设置为true,进入房间时设置为false,所以走
createLive()
/**
* 1_1 创建一个直播
*/
private void createLive() {
createIMChatRoom();
}
/**
* 1_2创建一个IM聊天室
*/
private void createIMChatRoom() {
final ArrayList<String> list = new ArrayList<String>();
final String roomName = "this is a test";
SxbLog.i(TAG,"createIMChatRoom room " +MySelfInfo.getInstance().getMyRoomNum());
TIMGroupManager.getInstance().createGroup("AVChatRoom", list, roomName, "" + MySelfInfo.getInstance().getMyRoomNum(), new TIMValueCallBack<String>() {
@Override
public void onError(int i, String s) {
SxbLog.i(TAG, "onError " + i + " " + s);
//已在房间中,重复进入房间
if (i == Constants.IS_ALREADY_IN_ROOM) {
isInChatRoom = true;
createAVRoom(MySelfInfo.getInstance().getMyRoomNum());
return;
}
// 创建IM房间失败,提示失败原因,并关闭等待对话框
SxbLog.standardEnterRoomLog(TAG, "create live im group", "" + LogConstants.STATUS.FAILED, "code:" + i + " msg:" + s);
Toast.makeText(mContext, "create IM room fail " + s + " " + i, Toast.LENGTH_SHORT).show();
quiteLive();
}
@Override
public void onSuccess(String s) {
SxbLog.standardEnterRoomLog(TAG, "create live im group", "" + LogConstants.STATUS.SUCCEED, "group id " + MySelfInfo.getInstance().getMyRoomNum());
isInChatRoom = true;
//创建AV房间
createAVRoom(MySelfInfo.getInstance().getMyRoomNum());
}
});
}
创建IM聊天室成功后创建AV房间,调用createAVRoom
/**
* 1_3创建一个AV房间
*/
private void createAVRoom(int roomNum) {
SxbLog.standardEnterRoomLog(TAG, "create av room", "", "room id " + MySelfInfo.getInstance().getMyRoomNum());
EnterAVRoom(roomNum);
}
进入AV房间,调用EnterAVRoom,房间号之前通过getMyRoomNum方法拿到。
EnterAVRoom
/**
* 进入AV房间
*
* @param roomNum
*/
private void EnterAVRoom(int roomNum) {
SxbLog.i(TAG, "createlive joinLiveRoom enterAVRoom " + roomNum);
AVContext avContext = QavsdkControl.getInstance().getAVContext();
byte[] authBuffer = null;//权限位加密串;TODO:请业务侧填上自己的加密串
AVRoomMulti.EnterParam.Builder enterRoomParam = new AVRoomMulti.EnterParam.Builder(roomNum);
if (MySelfInfo.getInstance().getIdStatus() == Constants.HOST) {
enterRoomParam.auth(Constants.HOST_AUTH, authBuffer).avControlRole(Constants.HOST_ROLE).autoCreateRoom(true).isEnableMic(true).isEnableSpeaker(true);//;TODO:主播权限 所有权限
} else {
enterRoomParam.auth(Constants.NORMAL_MEMBER_AUTH, authBuffer).avControlRole(Constants.NORMAL_MEMBER_ROLE).autoCreateRoom(false).isEnableSpeaker(true);
}
enterRoomParam.audioCategory(Constants.AUDIO_VOICE_CHAT_MODE).videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);
// enterRoomParam.isDegreeFixed(true);
if (avContext != null) {
// create room
avContext.enterRoom(mEventListener, enterRoomParam.build());
}
}
这里面主要区别主播还是观众,MySelfInfo.getInstance().getIdStatus() == Constants.HOST表示是主播
主播拥有所有权限,直播的角色跟权限都与观众不同,这里涉及到流量计费问题,之前的版本都是给予主播权限,也就是所有权限,会导致DC流量峰值增高,从而增加直播成本。
主播
enterRoomParam.auth(Constants.HOST_AUTH, authBuffer).avControlRole(Constants.HOST_ROLE).autoCreateRoom(true).isEnableMic(true).isEnableSpeaker(true);//;TODO:主播权限 所有权限
观众
enterRoomParam.auth(Constants.NORMAL_MEMBER_AUTH, authBuffer).avControlRole(Constants.NORMAL_MEMBER_ROLE).autoCreateRoom(false).isEnableSpeaker(true);
这里可以看Constans中的权限
public static final long HOST_AUTH = AVRoomMulti.AUTH_BITS_DEFAULT;//权限位;TODO:默认值是拥有所有权限。
public static final long VIDEO_MEMBER_AUTH = AVRoomMulti.AUTH_BITS_DEFAULT;//权限位;TODO:默认值是拥有所有权限。
public static final long NORMAL_MEMBER_AUTH = AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO;
这里HOST_AUTH 是主播权限,VIDEO_MEMBER_AUTH 是上麦权限,等同于直播权限,这个是动态赋予的,下麦之后要回复为NORMAL_MEMBER_AUTH ,NORMAL_MEMBER_AUTH 是观众权限,且暂无上麦。
进入房间回调mEventListener,来监听进入房间是否成功。
在EnterLiveHelper里面
private AVRoomMulti.EventListener mEventListener = new AVRoomMulti.EventListener()
这里因为代码太多,只对对调的几个方法做一下讲述。
创建房间成功回调
// 创建房间成功回调
public void onEnterRoomComplete(int result,String s) {
SxbLog.i(TAG,"enterAVRoom onEnterRoomComplete: "+result+" info " +s);
if (result == 0) {
SxbLog.standardEnterRoomLog(TAG, "enterAVRoom", "" + LogConstants.STATUS.SUCCEED, "room id" + MySelfInfo.getInstance().getMyRoomNum());
//只有进入房间后才能初始化AvView
QavsdkControl.getInstance().setAvRoomMulti(QavsdkControl.getInstance().getAVContext().getRoom());
isInAVRoom = true;
initAudioService();
if (null != mStepInOutView)
mStepInOutView.enterRoomComplete(MySelfInfo.getInstance().getIdStatus(), true);
} else {
quiteAVRoom();
SxbLog.standardEnterRoomLog(TAG, "enterAVRoom", "" + LogConstants.STATUS.FAILED, "result " + result);
}
}
当result==0时进入房间成功enterRoomComplete,回调是否是主播跟成功状态,进入失败就退出直播页面,这个不提。
enterRoomComplete回调处理
/**
* 完成进出房间流程
*/
@Override
public void enterRoomComplete(int id_status, boolean isSucc) {
Toast.makeText(LiveActivity.this, "EnterRoom " + id_status + " isSucc " + isSucc, Toast.LENGTH_SHORT).show();
//必须得进入房间之后才能初始化UI
mEnterRoomHelper.initAvUILayer(avView);
QavsdkControl.getInstance().setSlideListener(this);
bInAvRoom = true;
bDelayQuit = true;
updateHostLeaveLayout();
//设置预览回调,修正摄像头镜像
mLiveHelper.setCameraPreviewChangeCallback();
if (isSucc == true) {
//IM初始化
mLiveHelper.initTIMListener("" + CurLiveInfo.getRoomNum());
if (id_status == Constants.HOST) {//主播方式加入房间成功
//开启摄像头渲染画面
SxbLog.i(TAG, "createlive enterRoomComplete isSucc" + isSucc);
} else {
//发消息通知上线
mLiveHelper.sendGroupMessage(Constants.AVIMCMD_ENTERLIVE, "");
}
}
bReadyToChange = false;
}
主要做了一下初始化UI(初始化surfaceview),修正摄像头。这时候你会说摄像头啥时候开启的?没错,上面流程并没有看到开启摄像头,其实这个隐藏很深。必须的进入房间之后才能初始化SurfaceView,初始化完了之后才能打开摄像头。
分析一下:
mEnterRoomHelper.initAvUILayer(avView);—》initAvUILayer—mAVUIControl = new AVUIControl(context, view)—-》initCameraPreview();—》holder.addCallback(mSurfaceHolderListener);—》mSurfaceHolderListener—》mContext.sendBroadcast(new Intent(Constants.ACTION_SURFACE_CREATED)); 通过这个广播,在直播页面接收并处理
//打开摄像头
if (MySelfInfo.getInstance().getIdStatus() == Constants.HOST) {
mLiveHelper.openCameraAndMic();
onExitRoomComplete退出房间回调方法
最重要的还是onEndpointsUpdateInfo房间成员变化回调。
具体处理一下几种情况
private static final int TYPE_MEMBER_CHANGE_IN = 1;//进入房间事件。
private static final int TYPE_MEMBER_CHANGE_OUT = 2;//退出房间事件。
private static final int TYPE_MEMBER_CHANGE_HAS_CAMERA_VIDEO = 3;//有发摄像头视频事件。
private static final int TYPE_MEMBER_CHANGE_NO_CAMERA_VIDEO = 4;//无发摄像头视频事件。
private static final int TYPE_MEMBER_CHANGE_HAS_AUDIO = 5;//有发语音事件。
private static final int TYPE_MEMBER_CHANGE_NO_AUDIO = 6;//无发语音事件。
private static final int TYPE_MEMBER_CHANGE_HAS_SCREEN_VIDEO = 7;//有发屏幕视频事件。
private static final int TYPE_MEMBER_CHANGE_NO_SCREEN_VIDEO = 8;//无发屏幕视频事件。
具体代码不贴了。
加入一个直播流程
加入直播跟发布直播基本一致,加入直播进入直播间也是startEnterRoom方法,只不过这时候不需要创建IM房间,只需要加入IM房间即可
joinLive
/**
* 2_1加入一个房间
*/
private void joinLive(int roomNum) {
joinIMChatRoom(roomNum);
}
/**
* 2_2加入一个聊天室
*/
private void joinIMChatRoom(final int chatRoomId) {
SxbLog.standardEnterRoomLog(TAG, "join im chat room", "", "room id " + chatRoomId);
TIMGroupManager.getInstance().applyJoinGroup("" + chatRoomId, Constants.APPLY_CHATROOM + chatRoomId, new TIMCallBack() {
@Override
public void onError(int i, String s) {
//已经在是成员了
if (i == Constants.IS_ALREADY_MEMBER) {
SxbLog.i(TAG, "joinLiveRoom joinIMChatRoom callback succ ");
joinAVRoom(CurLiveInfo.getRoomNum());
isInChatRoom = true;
} else {
SxbLog.standardEnterRoomLog(TAG, "join im chat room", "" + LogConstants.STATUS.FAILED, "code:" + i + " msg:" + s);
if (mContext != null)
Toast.makeText(mContext, "join IM room fail " + s + " " + i, Toast.LENGTH_SHORT).show();
quiteLive();
}
}
@Override
public void onSuccess() {
SxbLog.standardEnterRoomLog(TAG, "join im chat room", "" + LogConstants.STATUS.FAILED, "room id " + chatRoomId);
isInChatRoom = true;
joinAVRoom(CurLiveInfo.getRoomNum());
}
});
}
这里面三个方法其实都是为了一个操作,加入聊天室joinIMChatRoom。加入成功就joinAVRoom,进入avRoom房间。
那又有问题,发布直播是本地渲染,加入直播是请求画面,那这两个方法分别在哪里实现的呢?
其实是在我们打开摄像头的时候,在EnterLiveHelper中的onEndpointsUpdateInfo会监听到TYPE_MEMBER_CHANGE_HAS_CAMERA_VIDEO 有发摄像头视频事件。他在下面的处理中发送了
Intent intent = new Intent(Constants.ACTION_CAMERA_OPEN_IN_LIVE);
intent.putStringArrayListExtra("ids", video_ids);
mContext.sendBroadcast(intent);
在直播页面接收广播并处理
if (action.equals(Constants.ACTION_CAMERA_OPEN_IN_LIVE)) {//有人打开摄像头
isScreenShare = false;
ArrayList<String> ids = intent.getStringArrayListExtra("ids");
//如果是自己本地直接渲染
for (String id : ids) {
if (!mRenderUserList.contains(id)) {
mRenderUserList.add(id);
}
updateHostLeaveLayout();
if (id.equals(MySelfInfo.getInstance().getId())) {
showVideoView(true, id);
return;
// ids.remove(id);
}
}
//其他人一并获取
SxbLog.d(TAG, LogConstants.ACTION_VIEWER_SHOW + LogConstants.DIV + MySelfInfo.getInstance().getId() + LogConstants.DIV + "somebody open camera,need req data"
+ LogConstants.DIV + LogConstants.STATUS.SUCCEED + LogConstants.DIV + "ids " + ids.toString());
int requestCount = CurLiveInfo.getCurrentRequestCount();
mLiveHelper.requestViewList(ids);
requestCount = requestCount + ids.size();
CurLiveInfo.setCurrentRequestCount(requestCount);
// }
}
如果是主播,调用showVideoView本地渲染,如果是观众requestViewList请求直播画面,这样整个流程就通了。
本地渲染 QavsdkControl.getInstance().setLocalHasVideo(true, MySelfInfo.getInstance().getId());
请求画面要区分是否分享屏幕,一是摄像头画面,二是分享屏幕画面
if (isScreenShare) {
QavsdkControl.getInstance().setRemoteHasVideo(true, id, AVView.VIDEO_SRC_TYPE_SCREEN);
isScreenShare = false;
} else {
QavsdkControl.getInstance().setRemoteHasVideo(true, id, AVView.VIDEO_SRC_TYPE_CAMERA);
}
主播请求观众连线。
1 信令请求和应答是通过IMSDK的C2C消息。应答信令消息之后是打开或者关闭自己摄像头。
2 直播过程中成员音视频状态发送改变会通过onEndpointsUpdateInfo回调传递给所有成员。根据Id去请求对方画面,这种请求是AVSDK的接口不再是C2C消息。请求成功渲染画面。
3 成员状态变化回调onEndpointsUpdateInfo回调会包括自己,所以无论视频互动成员还是普通成员只需要根据回调里面的ID去请求画面即可同步,保持一致。
通过发送 mLiveHelper.sendC2CMessage(Constants.AVIMCMD_MUlTI_HOST_INVITE, “”, id); (多人主播发送邀请消息, C2C消息),在liveHelper中处理并展示showInviteDialog给观众,观众点击确定,进行
agreeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// mVideoMemberCtrlView.setVisibility(View.VISIBLE);
// mNomalMemberCtrView.setVisibility(View.INVISIBLE);
SxbLog.d(TAG, LogConstants.ACTION_VIEWER_SHOW + LogConstants.DIV + MySelfInfo.getInstance().getId() + LogConstants.DIV + "accept invite" +
LogConstants.DIV + "host id " + CurLiveInfo.getHostID());
//上麦 ;TODO 上麦 上麦 上麦 !!!!!;
mLiveHelper.changeAuthandRole(true, Constants.VIDEO_MEMBER_AUTH, Constants.VIDEO_MEMBER_ROLE);
inviteDg.dismiss();
}
});
refusebtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mLiveHelper.sendC2CMessage(Constants.AVIMCMD_MUlTI_REFUSE, "", CurLiveInfo.getHostID());
inviteDg.dismiss();
}
});
确定就获取到上麦权限,从而打开mic和摄像头,根据摄像头监听从而展示数据
拒绝就发送c2c消息,在liveHelper中处理
case Constants.AVIMCMD_MUlTI_REFUSE:
if (null != mLiveView)
mLiveView.cancelInviteView(identifier);
if (null != mContext) {
Toast.makeText(mContext, identifier + " refuse !", Toast.LENGTH_SHORT).show();
}
break;
整个流程就这么多了,更详细点的就参考代码吧