本文源自 RTC 開發者社區,做者爲資深Android工程師吳東洋。android
本系列文章分享了基於Agora SDK 2.1實現多人視頻通話的實踐經驗。程序員
自從2016年,鼓吹「互聯網寒冬」的論調甚囂塵上,2017年亦有愈演愈烈之勢。但連麥直播、在線抓娃娃、直播問答、遠程狼人殺等類型的項目卻異軍突起,成了投資人的風口,創業者的藍海和用戶的必裝App,而這些方向的項目都有一個共同的特色——都依賴視頻通話和全互動直播技術。安全
聲網Agora.io的SDK讓App和網站均可以實現高質量的音頻通話、視頻通話、全互動直播。我試着經過該SDK實現一個多人視頻通話應用。本文先分享集成與一對一視頻通話的部分。bash
聲網Agora.io SDK的兼容性良好,對硬件設備和軟件系統的要求不高,開發環境和測試環境知足如下條件便可:服務器
如下是我試用聲網Agora.io SDK的開發環境和測試環境:網絡
測試環境app
步驟一:首先點此下載完整的SDK和官方demo異步
步驟二:既然咱們要把聲網Agora.io集成到本身的項目裏,因此沒必要運行sample,咱們本身新建一個HelloAgora項目,注意必定要支持C++哦。ide
步驟三:把libs文件夾裏的arm64-v8a、、armeabi-v7a以及x86文件夾複製粘貼到app module的libs裏。若是有NDK開發的必要,則把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的必要,能夠繼續在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一下,聲網Agora.io的SDK就集成到項目中來了。
SDK集成完畢後,爲了保證SDK能正常運行,咱們須要在AndroidManisfest.xml 文件中聲明如下權限:
<!--容許程序鏈接網絡-->
<uses-permission android:name="android.permission.INTERNET" />
<!--容許程序錄制音頻-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--容許程序使用照相設備-->
<uses-permission android:name="android.permission.CAMERA" />
<!--容許程序修改全局音頻設置-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!--容許程序獲取網絡狀態-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--容許對存儲空間進行讀寫-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--容許程序鏈接到已配對的藍牙設備-->
<uses-permission android:name="android.permission.BLUETOOTH" />
複製代碼
這些權限都是Android開發過程當中的常見權限,有經驗的程序員都會感受眼熟,WRITE_EXTERNAL_STORAGE等敏感權限適配Android 6.0之後版本的問題並不是本文關注重點,在此不作贅述。
集成SDK並聲明瞭權限後,就該考慮混淆的問題了,咱們須要在project的proguard-rules.pro文件裏添加如下代碼:
-keep class io.agora.**{*;}
複製代碼
通過以上過程後,咱們已經完成了聲網Agora.io SDK的快速集成,邁出了走向連麥直播、在線抓娃娃、直播問答、遠程狼人殺等風口的第一步。在接下來的文章裏,我將繼續分享APP ID鑑權、Token鑑權、一對一視頻聊天、建立羣聊room、分屏、窗口切換和先後攝像頭切換等內容。
APP ID鑑權
所謂APP ID,就是在 Agora建立每一個項目都有的一個惟一標識。App ID 能夠明確你的項目及組織身份,並在 joinChannel 方法中做爲參數,鏈接到 Agora 實時網絡中,實現實時通訊或直播功能。不一樣的App ID在Agora實時網絡中的通話是徹底隔離的;Agora 提供的頻道信息、計費、管理服務也都是基於 App ID。
申請APP ID的操做很簡便,只要在Agora官網https://dashboard.agora.io/projects右側欄目的「項目」中點擊「添加新項目」,只需輸入項目名就可生成APP ID,全過程以下圖所示:
找到,把「<#YOUR APP ID#>」替換爲圖中的馬賽克里的字符串。
<string name="agora_app_id"><#YOUR APP ID#></string>
複製代碼
以上就是APP ID鑑權的全過程。
儘管App ID鑑權在最大程度上方便了開發者使用 Agora 的服務。但App ID 鑑權的安全性不佳,一旦有別有用心的人非法獲取了你的 App ID,他就能夠在 Agora 提供的SDK中使用你的App ID。若是你的項目對安全性要求高,或者增長用戶權限設置的話,建議採用Token鑑權。
Token鑑權
在通訊和直播場景中存在着多個角色,而每種角色又對應着一些默認權限。好比在直播場景中,主播能夠發佈流、訂閱流、邀請嘉賓;觀衆能夠訂閱流、申請連麥;管理員則能夠踢人或禁言。
Token鑑權的步驟比APP ID鑑權稍微複雜一些,在上文項目列表中查看 App ID 的地方,啓用該項目的 App Certificate:
首先,點擊激活項目右上方的 編輯 按鈕。
將你的 App Certificate 保存在服務器端,且對任何客戶端均不可見。當項目的 App Certificate 被啓用後,你必須使用 Token。例如: 在啓用 App Certificate 前,你可使用 App ID 加入頻道。但啓用了 App Certificate 後,你就必須使用 Token 加入頻道。後臺如何用App Certificate生成Token本文不作贅述。
初始化Agora
RtcEngine 類包含應用程序調用的主要方法,調用 RtcEngine 的接口最好在同一個線程進行,不建議在不一樣的線程同時調用。
目前 Agora Native SDK 只支持一個 RtcEngine 實例,每一個應用程序僅建立一個 RtcEngine 對象 。 RtcEngine 類的全部接口函數,如無特殊說明,都是異步調用,對接口的調用建議在同一個線程進行。全部返回值爲 int 型的 API,如無特殊說明,返回值 0 爲調用成功,返回值小於 0 爲調用失敗。
IRtcEngineEventHandler接口類用於SDK嚮應用程序發送回調事件通知,應用程序經過繼承該接口類的方法獲取 SDK 的事件通知。
接口類的全部方法都有缺省(空)實現,應用程序能夠根據須要只繼承關心的事件。在回調方法中,應用程序不該該作耗時或者調用可能會引發阻塞的 API(如 SendMessage),不然可能影響 SDK 的運行。
private RtcEngine mRtcEngine;
/**
* Tutorial Step 1
* 初始化Agora,建立 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初始化失敗了,檢查一下是哪兒出錯了\n" + 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() {
//設置遠端視頻顯示屬性
setupRemoteVideo(uid);
}
});
}
@Override
public void onUserOffline(int uid, int reason) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//其餘用戶離開當前頻道回調
onRemoteUserLeft();
}
});
}
@Override
public void onUserMuteVideo(final int uid, final boolean muted) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//其餘用戶已停發/已重發視頻流回調
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()方法用於打開視頻模式。能夠在加入頻道前或者通話中調用,在加入頻道前調用,則自動開啓視頻模式,在通話中調用則由音頻模式切換爲視頻模式。調用 disableVideo() 方法可關閉視頻模式。
setVideoProfile()方法設置視頻編碼屬性(Profile)。每一個屬性對應一套視頻參數,如分辨率、幀率、碼率等。 當設備的攝像頭不支持指定的分辨率時,SDK 會自動選擇一個合適的攝像頭分辨率,可是編碼分辨率仍然用 setVideoProfile() 指定的。
該方法僅設置編碼器編出的碼流屬性,可能跟最終顯示的屬性不一致,例如編碼碼流分辨率爲 640x480,碼流的旋轉屬性爲 90 度,則顯示出來的分辨率爲豎屏模式。
/**
* Tutorial Step 2
* 打開視頻模式,並設置本地視頻屬性
*/
private void setupVideoProfile() {
//打開視頻模式
mRtcEngine.enableVideo();
//設置本地視頻屬性
mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_360P, false);
}
複製代碼
設置本地視頻顯示屬性
setupLocalVideo( VideoCanvas local )方法用於設置本地視頻顯示信息。應用程序經過調用此接口綁定本地視頻流的顯示視窗(view),並設置視頻顯示模式。 在應用程序開發中,一般在初始化後調用該方法進行本地視頻設置,而後再加入頻道。退出頻道後,綁定仍然有效,若是須要解除綁定,能夠調用 setupLocalVideo(null) 。
/**
* Tutorial Step 3
* 設置本地視頻顯示屬性
*/
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 )方法讓用戶加入通話頻道,在同一個頻道內的用戶能夠互相通話,多個用戶加入同一個頻道,能夠羣聊。 使用不一樣 App ID 的應用程序是不能互通的。若是已在通話中,用戶必須調用 leaveChannel() 退出當前通話,才能進入下一個頻道。
/**
* Tutorial Step 4
* 加入一個頻道
*/
private void joinChannel() {
//若是不指定UID,Agroa將自動生成並分配一個UID
mRtcEngine.joinChannel(null, "demoChannel1", "Extra Optional Data", 0);
}
複製代碼
設置遠端視頻顯示屬性
setupRemoteVideo( VideoCanvas remote)方法用於綁定遠程用戶和顯示視圖,即設定 uid 指定的用戶用哪一個視圖顯示。調用該接口時須要指定遠程視頻的 uid,通常能夠在進頻道前提早設置好。
若是應用程序不能事先知道對方的 uid,能夠在 APP 收到 onUserJoined 事件時設置。若是啓用了視頻錄製功能,視頻錄製服務會作爲一個啞客戶端加入頻道,所以其餘客戶端也會收到它的 onUserJoined 事件,APP 不該給它綁定視圖(由於它不會發送視頻流),若是 APP 不能識別啞客戶端,能夠在 onFirstRemoteVideoDecoded 事件時再綁定視圖。解除某個用戶的綁定視圖能夠把 view 設置爲空。退出頻道後,SDK 會把遠程用戶的綁定關係清除掉。
/**
* Tutorial Step 5
* 設置遠端視頻顯示屬性
*/
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()方法用於離開頻道,即掛斷或退出通話。
當調用 joinChannel() API 方法後,必須調用 leaveChannel() 結束通話,不然沒法開始下一次通話。 無論當前是否在通話中,均可以調用 leaveChannel(),沒有反作用。該方法會把會話相關的全部資源釋放掉。該方法是異步操做,調用返回時並無真正退出頻道。在真正退出頻道後,SDK 會觸發 onLeaveChannel 回調。
/**
* 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)設置是否打開閃光燈、setCameraAutoFocusFaceModeEnabled(boolean enabled)設置是否開啓人臉對焦功能等等。
/**
* Tutorial Step 7
* 切換前置/後置攝像頭
*/
public void onSwitchCameraClicked(View view) {
mRtcEngine.switchCamera();
}
複製代碼
將本身靜音
muteLocalAudioStream(boolean muted)方法用於靜音/取消靜音。該方法能夠容許/禁止往網絡發送本地音頻流。但該方法並無禁用麥克風,不影響錄音狀態。
/**
* 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)方法用於暫停發送本地視頻流,但該方法並無禁用攝像頭,不影響本地視頻流獲取。
/**
* 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,若是能看見兩個本身,說明你成功了。
經過本文的學習,咱們已經掌握了利用Agora SDK進行一對一聊天的技巧,接下來的文章中,我將繼續介紹如何實現多人聊天室。