之前写过关于集成腾讯直播的一些关键地方,但是比较分散,加之随心播新版本对权限角色做了升级,这对优化流量计费有很大帮助,因此,升级必不可少。
随心播下载地址: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;整个流程就这么多了,更详细点的就参考代码吧
新闻热点
疑难解答