之前寫過關于集成騰訊直播的一些關鍵地方,但是比較分散,加之隨心播新版本對權限角色做了升級,這對優化流量計費有很大幫助,因此,升級必不可少。
隨心播下載地址: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;整個流程就這么多了,更詳細點的就參考代碼吧
新聞熱點
疑難解答