作者:聲網(wǎng)Agora用戶,資深Android開發(fā)者吳東洋。
本系列文章分享了基于Agora SDK 2.1實現(xiàn)多人視頻通話的實踐經(jīng)驗。
自從2016年,鼓吹“互聯(lián)網(wǎng)寒冬”的論調(diào)甚囂塵上,2017年亦有愈演愈烈之勢。但連麥直播、在線抓娃娃、直播問答、遠程狼人殺等類型的項目卻異軍突起,成了投資人的風口,創(chuàng)業(yè)者的藍海和用戶的必裝App,而這些方向的項目都有一個共同的特點——都依賴視頻通話和全互動直播技術(shù)。
聲網(wǎng)Agora.io的SDK讓App和網(wǎng)站都可以實現(xiàn)高質(zhì)量的音頻通話、視頻通話、全互動直播。我試著通過該SDK實現(xiàn)一個多人視頻通話應用。本文先分享集成的部分。
環(huán)境聲網(wǎng)Agora.io SDK的兼容性良好,對硬件設(shè)備和軟件系統(tǒng)的要求不高,開發(fā)環(huán)境和測試環(huán)境滿足以下條件即可:
Android SDK API Level >= 16
Android Studio 2.0 或以上版本
支持語音和視頻功能的真機
App 要求 Android 4.1 或以上設(shè)備
以下是我試用聲網(wǎng)Agora.io SDK的開發(fā)環(huán)境和測試環(huán)境:
開發(fā)環(huán)境
Windows 10 家庭中文版
Java Version SE 8
Android Studio 3.2 Canary 4
測試環(huán)境
Samsung Nexus (Android 4.4.2 API 19)
Mi Note 3 (Android 7.1.1 API 25)
集成步驟一:首先點此下載完整的SDK和官方demo
步驟二:既然我們要把聲網(wǎng)Agora.io集成到自己的項目里,所以不必運行sample,我們自己新建一個HelloAgora項目,注意一定要支持C++哦。
步驟三:把libs文件夾里的arm64-v8a、、armeabi-v7a以及x86文件夾復制粘貼到app module的libs里。如果有NDK開發(fā)的必要,則把libs->include文件夾里的兩個.h頭文件復制粘貼到合適位置。
步驟四:首先在app module的build.gradle文件的android代碼塊中添加如下代碼:
sourceSets { main { jniLibs.srcDirs = ["../../../libs"] } }
然后在app module的build.gradle文件的android->defaultConfig代碼塊中添加如下代碼:
ndk { abiFilters "armeabi-v7a", "x86" }
接下來在app module的build.gradle文件的dependencies代碼塊中添加如下代碼:
compile "io.agora.rtc:full-sdk:2.0.0"
如果用復制粘貼jar的方式,那么此處添加如下代碼:
compile fileTree(dir: "../../../libs", include: ["*.jar"])
如果有自定義NDK的必要,可以繼續(xù)在app module的build.gradle文件的android代碼塊中添加如下代碼:
externalNativeBuild { ndkBuild { path "src/main/cpp/Android.mk" } }
然后在app module的build.gradle文件的android->defaultConfig代碼塊中添加如下代碼:
externalNativeBuild { ndkBuild { arguments "NDK_APPLICATION_MK:=src/main/cpp/Application.mk" } }
最后sync一下,聲網(wǎng)Agora.io的SDK就集成到項目中來了。
權(quán)限SDK集成完畢后,為了保證SDK能正常運行,我們需要在AndroidManisfest.xml 文件中聲明以下權(quán)限:
這些權(quán)限都是Android開發(fā)過程中的常見權(quán)限,有經(jīng)驗的程序員都會感覺眼熟,WRITE_EXTERNAL_STORAGE等敏感權(quán)限適配Android 6.0以后版本的問題并非本文關(guān)注重點,在此不做贅述。
混淆代碼集成SDK并聲明了權(quán)限后,就該考慮混淆的問題了,我們需要在project的proguard-rules.pro文件里添加以下代碼:
-keep class io.agora.**{*;}
經(jīng)過以上過程后,我們已經(jīng)完成了聲網(wǎng)Agora.io SDK的快速集成,邁出了走向連麥直播、在線抓娃娃、直播問答、遠程狼人殺等風口的第一步。在接下來的文章里,我將繼續(xù)分享APP ID鑒權(quán)、Token鑒權(quán)、一對一視頻聊天、創(chuàng)建群聊room、分屏、窗口切換和前后攝像頭切換等內(nèi)容。
鑒權(quán)APP ID鑒權(quán)
所謂APP ID,就是在 Agora創(chuàng)建每個項目都有的一個唯一標識。App ID 可以明確你的項目及組織身份,并在 joinChannel 方法中作為參數(shù),連接到 Agora 實時網(wǎng)絡(luò)中,實現(xiàn)實時通信或直播功能。不同的App ID在Agora實時網(wǎng)絡(luò)中的通話是完全隔離的;Agora 提供的頻道信息、計費、管理服務也都是基于 App ID。
申請APP ID的操作很簡便,只要在Agora官網(wǎng)https://dashboard.agora.io/pr...“項目”中點擊“添加新項目”,只需輸入項目名就可生成APP ID,全過程如下圖所示:
找到,把“<#YOUR APP ID#>”替換為圖中的馬賽克里的字符串。
<#YOUR APP ID#>
以上就是APP ID鑒權(quán)的全過程。
盡管App ID鑒權(quán)在最大程度上方便了開發(fā)者使用 Agora 的服務。但App ID 鑒權(quán)的安全性不佳,一旦有別有用心的人非法獲取了你的 App ID,他就可以在 Agora 提供的SDK中使用你的App ID。如果你的項目對安全性要求高,或者增加用戶權(quán)限設(shè)置的話,建議采用Token鑒權(quán)。
Token鑒權(quán)
在通信和直播場景中存在著多個角色,而每種角色又對應著一些默認權(quán)限。比如在直播場景中,主播可以發(fā)布流、訂閱流、邀請嘉賓;觀眾可以訂閱流、申請連麥;管理員則可以踢人或禁言。
Token鑒權(quán)的步驟比APP ID鑒權(quán)稍微復雜一些,在上文項目列表中查看 App ID 的地方,啟用該項目的 App Certificate:
首先,點擊激活項目右上方的 編輯 按鈕。
將你的 App Certificate 保存在服務器端,且對任何客戶端均不可見。當項目的 App Certificate 被啟用后,你必須使用 Token。例如: 在啟用 App Certificate 前,你可以使用 App ID 加入頻道。但啟用了 App Certificate 后,你就必須使用 Token 加入頻道。后臺如何用App Certificate生成Token本文不做贅述。
初始化Agora
RtcEngine 類包含應用程序調(diào)用的主要方法,調(diào)用 RtcEngine 的接口最好在同一個線程進行,不建議在不同的線程同時調(diào)用。
目前 Agora Native SDK 只支持一個 RtcEngine 實例,每個應用程序僅創(chuàng)建一個 RtcEngine 對象 。 RtcEngine 類的所有接口函數(shù),如無特殊說明,都是異步調(diào)用,對接口的調(diào)用建議在同一個線程進行。所有返回值為 int 型的 API,如無特殊說明,返回值 0 為調(diào)用成功,返回值小于 0 為調(diào)用失敗。
IRtcEngineEventHandler接口類用于SDK向應用程序發(fā)送回調(diào)事件通知,應用程序通過繼承該接口類的方法獲取 SDK 的事件通知。
接口類的所有方法都有缺?。眨崿F(xiàn),應用程序可以根據(jù)需要只繼承關(guān)心的事件。在回調(diào)方法中,應用程序不應該做耗時或者調(diào)用可能會引起阻塞的 API(如 SendMessage),否則可能影響 SDK 的運行。
private RtcEngine mRtcEngine; /** * Tutorial Step 1 * 初始化Agora,創(chuàng)建 RtcEngine 對象 */ private void initializeAgoraEngine() { try { mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler); } catch (Exception e) { Log.e(LOG_TAG, Log.getStackTraceString(e)); throw new RuntimeException("Agora初始化失敗了,檢查一下是哪兒出錯了 " + Log.getStackTraceString(e)); } } private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() { @Override public void onFirstRemoteVideoDecoded(final int uid, int width, int height, int elapsed) { runOnUiThread(new Runnable() { @Override public void run() { //設(shè)置遠端視頻顯示屬性 setupRemoteVideo(uid); } }); } @Override public void onUserOffline(int uid, int reason) { runOnUiThread(new Runnable() { @Override public void run() { //其他用戶離開當前頻道回調(diào) onRemoteUserLeft(); } }); } @Override public void onUserMuteVideo(final int uid, final boolean muted) { runOnUiThread(new Runnable() { @Override public void run() { //其他用戶已停發(fā)/已重發(fā)視頻流回調(diào) onRemoteUserVideoMuted(uid, muted); } }); } }; private void onRemoteUserLeft() { FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container); container.removeAllViews(); //文案可隨意定制 View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); tipMsg.setVisibility(View.VISIBLE); } private void onRemoteUserVideoMuted(int uid, boolean muted) { FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container); SurfaceView surfaceView = (SurfaceView) container.getChildAt(0); Object tag = surfaceView.getTag(); if (tag != null && (Integer) tag == uid) { surfaceView.setVisibility(muted ? View.GONE : View.VISIBLE); } }
打開視頻模式
enableVideo()方法用于打開視頻模式。可以在加入頻道前或者通話中調(diào)用,在加入頻道前調(diào)用,則自動開啟視頻模式,在通話中調(diào)用則由音頻模式切換為視頻模式。調(diào)用 disableVideo() 方法可關(guān)閉視頻模式。
setVideoProfile()方法設(shè)置視頻編碼屬性(Profile)。每個屬性對應一套視頻參數(shù),如分辨率、幀率、碼率等。 當設(shè)備的攝像頭不支持指定的分辨率時,SDK 會自動選擇一個合適的攝像頭分辨率,但是編碼分辨率仍然用 setVideoProfile() 指定的。
該方法僅設(shè)置編碼器編出的碼流屬性,可能跟最終顯示的屬性不一致,例如編碼碼流分辨率為 640x480,碼流的旋轉(zhuǎn)屬性為 90 度,則顯示出來的分辨率為豎屏模式。
/** * Tutorial Step 2 * 打開視頻模式,并設(shè)置本地視頻屬性 */ private void setupVideoProfile() { //打開視頻模式 mRtcEngine.enableVideo(); //設(shè)置本地視頻屬性 mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_360P, false); }
設(shè)置本地視頻顯示屬性
setupLocalVideo( VideoCanvas local )方法用于設(shè)置本地視頻顯示信息。應用程序通過調(diào)用此接口綁定本地視頻流的顯示視窗(view),并設(shè)置視頻顯示模式。 在應用程序開發(fā)中,通常在初始化后調(diào)用該方法進行本地視頻設(shè)置,然后再加入頻道。退出頻道后,綁定仍然有效,如果需要解除綁定,可以調(diào)用 setupLocalVideo(null) 。
/** * Tutorial Step 3 * 設(shè)置本地視頻顯示屬性 */ private void setupLocalVideo() { FrameLayout container = (FrameLayout) findViewById(R.id.local_video_view_container); SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext()); surfaceView.setZOrderMediaOverlay(true); container.addView(surfaceView); mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_ADAPTIVE, 0)); }
加入一個頻道
joinChannel(String token,String channelName,String optionalInfo,int optionalUid )方法讓用戶加入通話頻道,在同一個頻道內(nèi)的用戶可以互相通話,多個用戶加入同一個頻道,可以群聊。 使用不同 App ID 的應用程序是不能互通的。如果已在通話中,用戶必須調(diào)用 leaveChannel() 退出當前通話,才能進入下一個頻道。
/** * Tutorial Step 4 * 加入一個頻道 */ private void joinChannel() { //如果不指定UID,Agroa將自動生成并分配一個UID mRtcEngine.joinChannel(null, "demoChannel1", "Extra Optional Data", 0); }
設(shè)置遠端視頻顯示屬性
setupRemoteVideo( VideoCanvas remote)方法用于綁定遠程用戶和顯示視圖,即設(shè)定 uid 指定的用戶用哪個視圖顯示。調(diào)用該接口時需要指定遠程視頻的 uid,一般可以在進頻道前提前設(shè)置好。
如果應用程序不能事先知道對方的 uid,可以在 APP 收到 onUserJoined 事件時設(shè)置。如果啟用了視頻錄制功能,視頻錄制服務會做為一個啞客戶端加入頻道,因此其他客戶端也會收到它的 onUserJoined 事件,APP 不應給它綁定視圖(因為它不會發(fā)送視頻流),如果 APP 不能識別啞客戶端,可以在 onFirstRemoteVideoDecoded 事件時再綁定視圖。解除某個用戶的綁定視圖可以把 view 設(shè)置為空。退出頻道后,SDK 會把遠程用戶的綁定關(guān)系清除掉。
/** * Tutorial Step 5 * 設(shè)置遠端視頻顯示屬性 */ private void setupRemoteVideo(int uid) { FrameLayout container = (FrameLayout) findViewById(R.id.remote_video_view_container); if (container.getChildCount() >= 1) { return; } SurfaceView surfaceView = RtcEngine.CreateRendererView(getBaseContext()); container.addView(surfaceView); mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid)); surfaceView.setTag(uid); //文案可隨意定制 View tipMsg = findViewById(R.id.quick_tips_when_use_agora_sdk); tipMsg.setVisibility(View.GONE); }
離開當前頻道
leaveChannel()方法用于離開頻道,即掛斷或退出通話。
當調(diào)用 joinChannel() API 方法后,必須調(diào)用 leaveChannel() 結(jié)束通話,否則無法開始下一次通話。 不管當前是否在通話中,都可以調(diào)用 leaveChannel(),沒有副作用。該方法會把會話相關(guān)的所有資源釋放掉。該方法是異步操作,調(diào)用返回時并沒有真正退出頻道。在真正退出頻道后,SDK 會觸發(fā) onLeaveChannel 回調(diào)。
/** * Tutorial Step 6 * 離開當前頻道 */ private void leaveChannel() { mRtcEngine.leaveChannel(); } public void onEncCallClicked(View view) { finish(); } @Override protected void onDestroy() { super.onDestroy(); leaveChannel(); RtcEngine.destroy(); mRtcEngine = null; }
管理攝像頭
switchCamera()方法用于在前置/后置攝像頭間切換。除此以外Agora還提供了一下管理攝像頭的方法:例如setCameraTorchOn(boolean isOn)設(shè)置是否打開閃光燈、setCameraAutoFocusFaceModeEnabled(boolean enabled)設(shè)置是否開啟人臉對焦功能等等。
/** * Tutorial Step 7 * 切換前置/后置攝像頭 */ public void onSwitchCameraClicked(View view) { mRtcEngine.switchCamera(); }
將自己靜音
muteLocalAudioStream(boolean muted)方法用于靜音/取消靜音。該方法可以允許/禁止往網(wǎng)絡(luò)發(fā)送本地音頻流。但該方法并沒有禁用麥克風,不影響錄音狀態(tài)。
/** * Tutorial Step 8 * 將自己靜音 */ public void onLocalAudioMuteClicked(View view) { ImageView iv = (ImageView) view; if (iv.isSelected()) { iv.setSelected(false); iv.clearColorFilter(); } else { iv.setSelected(true); iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY); } mRtcEngine.muteLocalAudioStream(iv.isSelected()); }
暫停本地視頻流
muteLocalVideoStream(boolean muted)方法用于暫停發(fā)送本地視頻流,但該方法并沒有禁用攝像頭,不影響本地視頻流獲取。
/** * Tutorial Step 9 * 暫停本地視頻流 */ public void onLocalVideoMuteClicked(View view) { ImageView iv = (ImageView) view; if (iv.isSelected()) { iv.setSelected(false); iv.clearColorFilter(); } else { iv.setSelected(true); iv.setColorFilter(getResources().getColor(R.color.colorPrimary), PorterDuff.Mode.MULTIPLY); } mRtcEngine.muteLocalVideoStream(iv.isSelected()); FrameLayout container = (FrameLayout) findViewById(R.id.local_video_view_container); SurfaceView surfaceView = (SurfaceView) container.getChildAt(0); surfaceView.setZOrderMediaOverlay(!iv.isSelected()); surfaceView.setVisibility(iv.isSelected() ? View.GONE : View.VISIBLE); }
運行效果
拿兩部手機安裝編譯好的App,如果能看見兩個自己,說明你成功了。
通過本文的學習,我們已經(jīng)掌握了利用Agora SDK進行一對一聊天的技巧,接下來的文章中,我將繼續(xù)介紹如何實現(xiàn)多人聊天室。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69029.html
摘要:我們先實現(xiàn)一個瀑布流瀑布流的實現(xiàn)方式很多,本文采用結(jié)合的來實現(xiàn)。有了一個可用的瀑布流之后,下面我們就可以實現(xiàn)動態(tài)聊天窗了動態(tài)聊天窗的要點在于的大小由視頻的寬高比決定,因此及其對應的就該注意不要寫死尺寸。 作者:聲網(wǎng)用戶,資深Android工程師吳東洋本系列文章分享了基于Agora SDK 2.1實現(xiàn)多人視頻通話的實踐經(jīng)驗。 在上一篇《Android 多人視頻聊天應用的開發(fā)(一)一對一聊...
閱讀 2079·2023-04-25 22:58
閱讀 1432·2021-09-22 15:20
閱讀 2709·2019-08-30 15:56
閱讀 2005·2019-08-30 15:54
閱讀 2124·2019-08-29 12:31
閱讀 2743·2019-08-26 13:37
閱讀 608·2019-08-26 13:25
閱讀 2110·2019-08-26 11:58