01主要功能
本項目基于環(huán)信音視頻云來完成,實現(xiàn)的主要功能有:
- 創(chuàng)建會議、刪除會議、獲取指定會議室詳情、加入會議室、退出會議室等關(guān)于會議的管理 ;
- 獲取會議室參會人名列表、踢人,設(shè)置觀眾為主播,設(shè)置主播為觀眾等關(guān)于會議室的人員管理;
- 共享桌面(web端);
- 三個端的實現(xiàn):Android,iOS,
上面這些功能在項目中都已經(jīng)實現(xiàn)。還有水印 ,變聲等高級功能在環(huán)信音視頻SDK的接口內(nèi)部都已經(jīng)封裝好,本項目沒有實現(xiàn) ,大家可以自行去實現(xiàn)。有關(guān)多人音視頻功能更詳細的介紹大家可以參考:這兒。多人音視頻實現(xiàn)的實現(xiàn)主要有以下一些場景:社交交友,遠程心理咨詢、遠程醫(yī)療、一對一在線教育、遠程視頻輔助等。咳咳 ,接下來就是純干貨了,給大家介紹我是如何一步步開發(fā)出一個完整的多人音視頻app。
02項目截圖
首先給大家展示下項目運行的效果圖,會議界面 主窗口是一個大的 RelativeLayout ,最下面的那一排排小窗口是的實現(xiàn)方法是HorizontalScrollView加上一個開源的組件 com.jaouan.compoundlayout.RadioLayoutGroup 實現(xiàn)的,點擊下面的小窗口后,可以 把小窗口的視頻流顯示在大屏上,具體是調(diào)用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)來更新SurfaceView,具體的細節(jié)大家可以看看代碼里面的實現(xiàn) 最后會公布代碼開源地址。
03準備工作
大家得下載安裝Android Studio,配置好Android 開發(fā)環(huán)境,怎么詳細配置我就在這不再細說了 網(wǎng)上有很多的教程,大家自己可以找找看,然后大家可以看看環(huán)信多人音視頻會議的主要功能和一些基本概念介紹。
04集成工作
首先大家會想問怎么調(diào)用環(huán)信的SDK ,大家可以使用 遠程依賴SDK包,建議大家用最新版本的遠程依賴:
com.hyphenate:hyphenate-sdk:3.6.6 ,依賴包可以放在 build.gradle里面的 dependencies 選項下面,如下圖所示:
2.其次怎么使用環(huán)信的appkey ,可以在環(huán)信 console 后臺注冊一個 賬號申請appkey ,可以參考:這里 ,獲取到 appkey 以后添加到AndroidManifest.xml中 ,如下圖所示:
3.經(jīng)過以上兩個重要的前期配置準備 ,接下來我們就可以開始進行代碼開發(fā)了,首先我們先創(chuàng)建一個項目的DemoApplication類和 DemoHelper類,DemoApplication 類和DemoHelper類都是一個單例類 ,DemoApplication 主要功能就是進行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 進行初始化,代碼如下所示:
DemoHelper還有一個重要的功能就是設(shè)置 EMConferenceListener 進行會議監(jiān)聽,有關(guān) EMConferenceListener 的類的詳細介紹 ,通過這個監(jiān)聽可以再加入會議的時候獲取到已經(jīng)在會議中的流和主播信息,分別是通過其中以下兩個回調(diào)獲取:
DemoHelper還有一個重要的功能就是設(shè)置 EMConferenceListener 進行會議監(jiān)聽,有關(guān) EMConferenceListener 的類的詳細介紹 ,通過這個監(jiān)聽可以再加入會議的時候獲取到已經(jīng)在會議中的流和主播信息,分別是通過其中以下兩個回調(diào)獲取:
@Overridepublic void onMemberJoined(EMConferenceMember member){}
@Overridepublic void onStreamAdded(EMConferenceStream stream){}
4.DemoApplication類完成以后,接下來就是怎么去登陸 環(huán)信IM 賬號和 創(chuàng)建加入會議房間了,首次安裝的時候都沒有賬號,我們使用的辦法是自動注冊一個賬號 在本地進行保存,然后進行登錄 ,注冊 登錄詳細接口請看 這兒, 注冊 登錄的調(diào)用大概如下所示:
4.DemoApplication類完成以后,接下來就是怎么去登陸 環(huán)信IM 賬號和 創(chuàng)建加入會議房間了,首次安裝的時候都沒有賬號,我們使用的辦法是自動注冊一個賬號 在本地進行保存,然后進行登錄 ,注冊 登錄詳細接口請看 這兒, 注冊 登錄的調(diào)用大概如下所示:
try {//注冊一個環(huán)信IDEMClient.getInstance()。createAccount(username, password);//注冊成功進行登錄PreferenceManager.getInstance()。setCurrentUserName(username);PreferenceManager.getInstance()。setCurrentuserPassword(password);login();} catch (final HyphenateException e) {runOnUiThread(new Runnable() {public void run() {int errorCode=e.getErrorCode();if(errorCode==EMError.NETWORK_ERROR){Toast.makeText(getApplicationContext(), getResources()。getString(R.string.network_anomalies), Toast.LENGTH_SHORT)。show();}
}
}
EMClient.getInstance()。conferenceManager()。joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack(){@Overridepublic void onSuccess(EMConference value) {EMLog.i(TAG, "join conference success");Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);startActivity(intent);finish();}
@Overridepublic void onError(final int error, final String errorMsg) {EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);runOnUiThread(new Runnable() {@Overridepublic void run() {setBtnEnable(true);if(error == CALL_TALKER_ISFULL) {takerFullDialogDisplay();}else{Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT)。show();}
}
});}
});
登錄完成以后,我們可以根據(jù)房間名創(chuàng)建并加入房間,主要代碼大概如下:
登錄完成以后,我們可以根據(jù)房間名創(chuàng)建并加入房間,主要代碼大概如下:
EMClient.getInstance()。conferenceManager()。joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack(){@Overridepublic void onSuccess(EMConference value) {EMLog.i(TAG, "join conference success");Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);startActivity(intent);finish();}
@Overridepublic void onError(final int error, final String errorMsg) {EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);runOnUiThread(new Runnable() {@Overridepublic void run() {setBtnEnable(true);if(error == CALL_TALKER_ISFULL) {takerFullDialogDisplay();}else{Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT)。show();}
}
});}
});
EMClient.getInstance().conferenceManager().joinRoom() API可以根據(jù)房間名創(chuàng)建指定會議,當以該房間名命名的會議不存在時候,會直接創(chuàng)建,當會議已經(jīng)創(chuàng)建好 可以根據(jù)正確的房間名和密碼加入房間 ,到這一步為止,我們已經(jīng)成功的創(chuàng)建 并加入會議。
5.加入會議以后我們進入到會議界面,展示從DemoHelper類 EMConferenceListener 中的 onStreamAdded 回調(diào) 和 onMemberJoined 獲取到的流和主播列表 ,在ConferenceActivity 中實現(xiàn) EMConferenceListener ,然后直接把 ConferenceActivity 注冊監(jiān)聽,用以下方法 EMClient.getInstance().conferenceManager().addConferenceListener(this); 這樣就可實現(xiàn) EMConferenceListener 事件的處理,比如主播進出房間 :
public void onMemberJoined(final EMConferenceMember member);public void onMemberExited(final EMConferenceMember member);
增加流移除流:
public void onStreamAdded(final EMConferenceStream stream)public void onStreamRemoved(final EMConferenceStream stream)管理員變更:
public void onAdminAdded(String streamId) ;public void onAdminRemoved(String streamId)角色變更、用戶被踢、誰在說話等各種回調(diào),可以處理各種業(yè)務(wù)邏輯 ,詳細的請參考 項目中的實現(xiàn) ,最后會附上項目的開源地址。
6 進入會議房間以后如果用戶角色為主播可以進行發(fā)布視頻流 ,觀眾只能訂閱視頻流 不能發(fā)布視頻流 ,可以調(diào)用SDK的publish接口發(fā)布流,該接口用到了EMStreamParam參數(shù),你可以自由配置,比如是否上傳視頻,是否上傳音頻,使用前置或后置攝像頭,視頻碼率,顯示視頻頁面等等,具體實現(xiàn)可以參考 中發(fā)布 訂閱視頻流的內(nèi)容, 關(guān)于以上的代碼邏輯如以 如以下:
//發(fā)布視頻流
normalParam = new EMStreamParam();normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);normalParam.setVideoOff(true);normalParam.setAudioOff(true);EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack() {@Overridepublic void onSuccess(String value) {conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);addOrUpdateStreamList("local-stream", value);PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);}
@Overridepublic void onError(int error, String errorMsg) {EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);}
});
//訂閱其他主播的視頻流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack() {@Overridepublic void onSuccess(String value) {}
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack
@Overridepublic void onError(int error, String errorMsg) {}
});}
7.有關(guān)上麥 下麥 的邏輯處理,觀眾可以請求上麥成為主播,主播可以下麥成為觀眾,上麥 下麥 是利用 EMConferenceAttribute進行處理 ,EMConferenceAttribute 是一個事件廣播,廣播事件是一個key-value格式,key-value 可以由開發(fā)者進行自行定義,增添事件以后 ,服務(wù)器會把事件進行廣播。會議中成員會收到 onAttributesUpdated回調(diào)。例如本項目中的會議上麥 下麥 代碼如下所示:
7.有關(guān)上麥 下麥 的邏輯處理,觀眾可以請求上麥成為主播,主播可以下麥成為觀眾,上麥 下麥 是利用 EMConferenceAttribute進行處理 ,EMConferenceAttribute 是一個事件廣播,廣播事件是一個key-value格式,key-value 可以由開發(fā)者進行自行定義,增添事件以后 ,服務(wù)器會把事件進行廣播。會議中成員會收到 onAttributesUpdated回調(diào)。例如本項目中的會議上麥 下麥 代碼如下所示:
//上麥申請EMClient.getInstance()。conferenceManager()。setConferenceAttribute(EMClient.getInstance()。getCurrentUser(),"request_tobe_speaker",new EMValueCallBack() {@Overridepublic void onSuccess(Void value) {EMLog.i(TAG, "request_tobe_speaker scuessed");}
@Overridepublic void onError(int error, String errorMsg) {EMLog.i(TAG, "request_tobe_speaker failed: error=" + error}
});
//下麥申請EMClient.getInstance()。conferenceManager()。setConferenceAttribute(EMClient.getInstance()。getCurrentUser(), "request_tobe_audience", new EMValueCallBack() {@Overridepublic void onSuccess(Void value) {EMLog.i(TAG, "request_tobe_audience scuessed");}
@Overridepublic void onError(int error, String errorMsg) {EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);}
});
上麥 下麥 請求發(fā)出以后 只能由主持人去處理,處理在 EMConferenceListener 的回調(diào) onAttributesUpdated(EMConferenceAttribute[] attributes) 去處理 ,收到回調(diào)以后 解析attributes 然后進行處理請求,處理的過程代碼大概如下:
上麥 下麥 請求發(fā)出以后 只能由主持人去處理,處理在 EMConferenceListener 的回調(diào) onAttributesUpdated(EMConferenceAttribute[] attributes) 去處理 ,收到回調(diào)以后 解析attributes 然后進行處理請求,處理的過程代碼大概如下:
EMClient.getInstance()。conferenceManager()。grantRole(conference.getConferenceId(), new EMConferenceMember(memName, null, null,null), EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack() {@Overridepublic void onSuccess(String value) {EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);dialog.dismiss();}
@Overridepublic void onError(int error, String errorMsg) {EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);}
});
下麥也是和上麥一樣是利用 EMConferenceAttribute進行處理。
9.有關(guān)退出會議 銷毀會議 普通主播 觀眾只能退出會議 ,主持人還可以 銷毀會議 正在進行中的會議可以進行銷毀,退出會議 銷毀會議 具體代碼如下:
EMClient.getInstance()。conferenceManager()。exitConference(new EMValueCallBack() {@Overridepublic void onSuccess(Object value) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "您已成功退出當前會議!", Toast.LENGTH_SHORT)。show();}
});}
@Overridepublic void onError(int error, String errorMsg) {EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);}
});
EMClient.getInstance()。conferenceManager()。destroyConference(new EMValueCallBack() {@Overridepublic void onSuccess(Object value) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "您已成功銷毀當前會議!", Toast.LENGTH_SHORT)。show();}
});EMLog.i(TAG, "finish ConferenceActivity");finish();}
05尾語至此整個多人音視頻會議APP開發(fā)的詳細步驟已經(jīng)完成 ,雖然比較麻煩 但是每個步驟都很清晰 ,有不太清楚的歡迎大家積極討論, 附上本項目的github地址:點擊跳轉(zhuǎn)。 歡迎大家積極參與 ,謝謝支持。歡迎大家積極參與 ,謝謝支持。本人聯(lián)系方式:727402046@qq.com(本文首發(fā)CSDN博客,已得到原作者“不回頭的倔強”的授權(quán))
05尾語至此整個多人音視頻會議APP開發(fā)的詳細步驟已經(jīng)完成 ,雖然比較麻煩 但是每個步驟都很清晰 ,有不太清楚的歡迎大家積極討論, 附上本項目的github地址:點擊跳轉(zhuǎn)。 歡迎大家積極參與 ,謝謝支持。歡迎大家積極參與 ,謝謝支持。本人聯(lián)系方式:727402046@qq.com(本文首發(fā)CSDN博客,已得到原作者“不回頭的倔強”的授權(quán))