亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

集成騰訊隨心播1.8.4代碼流程分析

2019-11-09 18:33:29
字體:
來源:轉載
供稿:網友

之前寫過關于集成騰訊直播的一些關鍵地方,但是比較分散,加之隨心播新版本對權限角色做了升級,這對優化流量計費有很大幫助,因此,升級必不可少。

隨心播下載地址: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;

整個流程就這么多了,更詳細點的就參考代碼吧


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91久久精品在线| 国产精品自在线| 日韩美女视频免费看| 亚洲精品美女在线观看| 久久乐国产精品| 亚洲经典中文字幕| 伊人成人开心激情综合网| 91精品久久久久久久久久另类| 欧美一区二粉嫩精品国产一线天| 国产999在线观看| 日韩成人小视频| 欧美日韩999| 亚洲美女av在线| 国产一区二区三区在线| 国外成人免费在线播放| 日韩成人性视频| 国产午夜精品免费一区二区三区| 欧美视频在线观看免费| 亚洲精品国产精品自产a区红杏吧| 国产精品久久久久一区二区| 蜜臀久久99精品久久久无需会员| 日本亚洲精品在线观看| 欧美xxxx18性欧美| 色黄久久久久久| 久久精品国产电影| 97国产精品人人爽人人做| 国产在线精品自拍| 国模精品视频一区二区| 亚洲视频在线看| 最近2019年中文视频免费在线观看| 久久综合五月天| 亚洲色图校园春色| 91性高湖久久久久久久久_久久99| 免费不卡在线观看av| 精品国内自产拍在线观看| 欧美中文字幕在线播放| 91精品国产综合久久香蕉922| 亚洲全黄一级网站| 久久久久中文字幕2018| 成人妇女免费播放久久久| 国产91对白在线播放| 国产午夜精品麻豆| 久久精品视频网站| 亚洲欧洲日产国码av系列天堂| 最近2019年手机中文字幕| 美女啪啪无遮挡免费久久网站| 亚洲free性xxxx护士白浆| 成人免费高清完整版在线观看| 国产精品成人aaaaa网站| 亚洲欧美中文另类| 精品久久久久久久久国产字幕| 国产视频丨精品|在线观看| 国产精品女主播| 久久天堂av综合合色| 久久人人爽人人爽人人片av高清| 狠狠操狠狠色综合网| 亚洲国产欧美一区二区三区久久| 国产精品高潮呻吟视频| 久久精品成人动漫| 日韩精品免费在线观看| 精品国产老师黑色丝袜高跟鞋| 午夜精品视频网站| 欧美成人免费在线观看| 日韩视频中文字幕| 国产精品国产三级国产专播精品人| 久久精品视频在线播放| 亚洲精品中文字| 午夜精品视频网站| 国产欧美日韩精品在线观看| 国模精品视频一区二区三区| 国产精品亚洲аv天堂网| 亚洲人成电影在线观看天堂色| 国产精品久久久久国产a级| 欧美一级电影免费在线观看| 91夜夜揉人人捏人人添红杏| 国产精选久久久久久| 欧美极品少妇xxxxx| 久久影院资源网| 亚洲欧美日韩在线一区| 91黑丝高跟在线| 国内精品伊人久久| 91精品国产免费久久久久久| 日韩av在线免播放器| 欧美怡红院视频一区二区三区| 国产成+人+综合+亚洲欧美丁香花| 欧美中文字幕视频在线观看| 亚洲片在线观看| 欧美视频在线观看免费网址| 91免费在线视频网站| 久久99精品久久久久久青青91| 欧美色视频日本版| 96国产粉嫩美女| 国产精品自拍偷拍| 亚洲欧美激情另类校园| 日韩欧美国产免费播放| 亚洲精品狠狠操| 久久久久久久久久婷婷| 欧美日韩亚洲精品内裤| 国产午夜精品全部视频在线播放| 2024亚洲男人天堂| 日韩在线不卡视频| www.xxxx欧美| 91视频国产高清| 91成人在线视频| 欧美视频中文字幕在线| 亚洲a在线播放| 日韩精品欧美国产精品忘忧草| 久久久精品在线观看| 日韩成人在线电影网| 国产精品中文字幕久久久| 欧洲成人在线视频| 国产成人一区二区三区小说| 午夜剧场成人观在线视频免费观看| 在线播放日韩精品| 亚洲精品一区二三区不卡| 久久激情视频免费观看| 亚洲国产高清福利视频| 亚洲女人初尝黑人巨大| 国产精品69久久| 亚洲色图五月天| 国产精品久久久久久久久久东京| 欧美男插女视频| 欧美高清视频在线观看| 亚洲春色另类小说| 欧美视频裸体精品| 亚洲欧美国产精品va在线观看| 青草青草久热精品视频在线观看| 国产综合视频在线观看| 亚洲欧美成人一区二区在线电影| 97精品一区二区三区| 亚洲第一av网| 欧美一区二区三区免费观看| 亚洲女同性videos| 视频在线一区二区| 日韩激情视频在线| 亚洲午夜久久久影院| 久久黄色av网站| 久久久久久亚洲精品不卡| 国产精品免费久久久久久| 成人免费黄色网| 亚洲人永久免费| 国产日韩在线观看av| 91在线观看免费| 91精品国产乱码久久久久久蜜臀| 亚洲成人999| 7m精品福利视频导航| 成人写真福利网| 成人伊人精品色xxxx视频| 亚洲精品电影网在线观看| 亚洲欧洲日产国码av系列天堂| 国产精品久久91| 亚洲色图第三页| 81精品国产乱码久久久久久| 亚洲视频一区二区| 久久av在线播放| 亚洲国产精品女人久久久| 久久免费在线观看| 中国人与牲禽动交精品| 国产脚交av在线一区二区| 日韩精品视频免费专区在线播放| 亚洲一区二区中文字幕| 久久久噜噜噜久久| 国产精品精品一区二区三区午夜版| 亚洲成人黄色网|