9 月 21 日,融雲直播課 社交泛娛樂出海最短變現路徑如何快速實現一款 1V1 影片應用? 歡迎點選上方小程式報名~
1V1 音影片、遠端服務類應用的實現利器——融雲 CallPlus SDK 上線! 關注【融雲全球網際網路通訊雲】瞭解更多
作為新一代音視訊通話場景化 SDK,融雲 CallPlus 完整封裝了撥打、接聽、結束通話等整套呼叫流程,支援一對一及多人音視訊通話,功能齊全、體驗絲滑,且契合海外使用者的互動偏好。
本文將以 Android 端整合為例,分享實戰教程:
使用融雲 CallPlus SDK,一小時整合 1V1 視訊通話能力。
一個 RTC 實時音影片底層零經驗開發者,只需 3 個核心 API、4 步 即可輕鬆實現音視訊通話能力。並且,融雲提供 Quick Demo 原始碼供開發者整合參考。
前置條件
建立融雲開發者賬號
開始之前,需建立融雲開發者賬號並獲取 App Key。在開發者後臺,系統會自動為新賬號建立一個應用。預設使用國內資料中心,並提供開發環境。如果您已經有融雲開發者賬號,可以直接建立新應用。
匯入 SDK
開啟根目錄下的 build.gradle(新版 Android studio 為 settings.gradle),Project 檢視下,宣告融雲的 Maven 程式碼庫。
allprojects {
repositories {
...
//融雲 maven 倉庫地址
maven {url "https://maven.rongcloud.cn/repository/maven-releases/"}
}
}
新增依賴項
在應用的 build.gradle 中,新增如下遠端依賴項。
注意:融雲 CallPlus 業務依賴 IM 通道,所以須同時整合 IMLibCore SDK。
dependencies {
// 請填寫具體的 SDK 版本號,新整合使用者建議使用最新版。此處以5.6.2版本為例。
implementation 'cn.rongcloud.sdk:im_libcore:5.6.2' // 即時通訊基礎能力庫。
implementation 'cn.rongcloud.sdk:callplus_lib:1.0.0'// 音影片呼叫能力庫(內含 rtc_lib)
}
許可權宣告
在 AndroidManifest.xml 中宣告 SDK 需要的所有許可權。
<!-- 允許程式訪問網路連線 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 允許程式獲取網路資訊狀態,如當前的網路連線是否有效 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- 允許程式獲取當前WiFi接入的狀態以及WLAN熱點的資訊 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 允許程式訪問攝像頭進行拍照 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 允許程式錄制聲音透過手機或耳機的麥克 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 允許程式修改聲音設定資訊 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!--允許程式訪問電話狀態,如通話中收到來自SIM卡的來電時,會將SIM卡通話狀態通知給遠端使用者,所以需要該許可權-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
注意:如果開發應用需要支援 Android 6.0(API 級別 23)或更高版本的裝置,還需要在 App 使用者使用對應功能時(如發起呼叫、接聽)請求攝像頭(CAMERA)、麥克風(RECORD_AUDIO)許可權。
詳見 Android 開發者官方文件執行時許可權與請求許可權的工作流。
正式整合
在著手實現之前,我們需要關注以下幾個關鍵點:
☑ 如何發起通話
☑ 如何接收來電
☑ 通話接通後,雙方通話介面如何顯示
要想實現通話功能,必須以雙端都已經成功連線融云為基礎,參考下圖所示 1V1 主叫端和被叫端的流程時序圖。
主叫端時序圖:
被叫端時序圖:
初始化連線融雲
CallPlus for Android 依賴融雲即時通訊客戶端 SDK 提供信令通道,故需先對 IMLibCore 進行初始化,建議放到 Application 中。
String appKey = "Your_AppKey"; // example: bos9p5rlcm2ba 建立融雲開發者賬號,獲取 App Key。
InitOption initOption = new InitOption.Builder().build();
RongCoreClient.init(getApplicationContext(), appKey, initOption);
要撥打和接聽一對一呼叫或開始多人呼叫,必須先透過 RongCoreClient 的 connect 方法連線融雲伺服器。
傳入使用者身份令牌(Token),向融雲伺服器驗證使用者身份;連線成功後,使用 RCCallPlusClient.getInstance().init() 方法初始化和配置 CallPlus SDK。
String token = "使用者Token";// 您在申請開發者賬號後,可以在融雲後臺北極星位置直接生成使用者token
RongCoreClient.connect(token, new IRongCoreCallback.ConnectCallback() {
/**
* 成功回撥
* @param userId 當前使用者 ID
*/
@Override
public void onSuccess(String userId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//todo 儘管在主執行緒初始化 RCCallPlusClient 不是必需的,但考慮到程式碼示例後續對 RCCallPlusClient 的呼叫都在主執行緒進行,所以目前選擇在主執行緒進行初始化。
//todo 請確保在同一個執行緒進行 RCCallPlusClient 的初始化、反初始化和使用,以確保操作的一致性。
RCCallPlusConfig config = RCCallPlusConfig.Builder.create().build();
/**
* 初始化並設定通話全域性配置,重複呼叫該方法時SDK內部會重新初始化
* @param config 設定通話全域性配置
* @return 方法呼叫後同步返回結果,可以在這裡得到初始化是否成功
*/
RCCallPlusResultCode resultCode = RCCallPlusClient.getInstance().init(config);
}
});
}
/**
* 錯誤回撥
* @param errorCode 錯誤碼
*/
@Override
public void onError(IRongCoreEnum.ConnectionErrorCode errorCode) {
}
/**
* 資料庫回撥.
* @param code 資料庫開啟狀態. DATABASE_OPEN_SUCCESS 資料庫開啟成功; DATABASE_OPEN_ERROR 資料庫開啟失敗
*/
@Override
public void onDatabaseOpened(DatabaseOpenStatus code) {
}
});
發起呼叫並設定本地和遠端檢視
使用 startCall 方法來發起一對一通話。
該方法內部會以非同步方式執行,並透過 IRCCallPlusResultListener#onStartCall 回撥來獲取方法的結果。
在發起通話之前需先設定本地和遠端檢視,在對端接聽視訊通話時,本端會自動渲染對端的檢視。
使用 setCallPlusResultListener 方法新增通話 API 非同步結果回撥監聽。該監聽可以接收 startCall、accept、hangup 等方法的結果回撥。
發起呼叫:
private void startCall(String remoteUserId) {
//todo 開啟攝像頭採集,請提前完成攝像頭、麥克風許可權的動態申請
RCCallPlusClient.getInstance().startCamera();
RCCallPlusClient.getInstance().enableMicrophone(true);
//設定本端檢視
setLocalVideoView();
//設定對端檢視
setRemotVideoView(remoteUserId);
List<String> userIds = new ArrayList<>();
userIds.add(remoteUserId);//todo remoteUserId 為被呼叫的遠端使用者userId
RCCallPlusType callType = RCCallPlusType.PRIVATE;//PRIVATE: 1V1通話
RCCallPlusMediaType mediaType = RCCallPlusMediaType.VIDEO;
/**
* 開始發起呼叫
* 該方法內部為非同步執行,結果回撥是註冊的{@link RCCallPlusClient#setCallPlusResultListener(IRCCallPlusResultListener)} 監聽的 {@link IRCCallPlusResultListener#onStartCall(RCCallPlusCode, String, List)}方法<br>
*/
RCCallPlusClient.getInstance().startCall(userIds, callType, mediaType);
}
發起端設定本地檢視:
/**
* 設定本地影片渲染檢視
*/
private void setLocalVideoView() {
//建立本地檢視物件
RCCallPlusLocalVideoView localVideoView = new RCCallPlusLocalVideoView(this.getApplicationContext());
//FIT: 影片幀透過保持寬高比(可能顯示黑色邊框)來縮放以適應檢視的大小
localVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
//設定本地檢視給 SDK
RCCallPlusClient.getInstance().setVideoView(localVideoView);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER_HORIZONTAL;//在父佈局中橫向居中顯示
//將本地檢視新增到XML中顯示
//示例程式碼中 mLocalVideoViewFrameLayout 為 android.widget.FrameLayout 物件
mLocalVideoViewFrameLayout.removeAllViews();
mLocalVideoViewFrameLayout.addView(localVideoView, params);
}
發起端設定對端檢視:
/**
* 發起通話時設定對端影片渲染檢視
*/
private void setRemotVideoView(String remoteUserId) {
//建立遠端檢視物件 remoteUserId為遠端使用者userId
RCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoView(remoteUserId, this.getApplicationContext(), false);
//FIT: 影片幀透過保持寬高比(可能顯示黑色邊框)來縮放以適應檢視的大小
remoteVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
//因為遠端檢視顯示在最頂層,為了防止遠端影片檢視被底部控制元件遮擋,所以新增如下設定:
remoteVideoView.setZOrderOnTop(true);
remoteVideoView.setZOrderMediaOverlay(true);
List<RCCallPlusRemoteVideoView> remoteVideoViewList = new ArrayList<>(); remoteVideoViewList.add(remoteVideoView);
//設定遠端檢視給SDK
RCCallPlusClient.getInstance().setVideoView(remoteVideoViewList);
FrameLayout.LayoutParams remoteVideoViewParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
remoteVideoViewParams.gravity = Gravity.CENTER_HORIZONTAL;
//將遠端檢視新增到XML中顯示
//示例程式碼中 mRemoteVideoViewFrameLayout 為 android.widget.FrameLayout 物件
mRemoteVideoViewFrameLayout.removeAllViews();
mRemoteVideoViewFrameLayout.addView(remoteVideoView, remoteVideoViewParams);
}
接收端接聽通話並設定本地和遠端檢視
可選擇接聽或結束通話來電,若要接聽電話,請使用 accept 方法;若要結束通話來電,請使用 RCCallPlusClient.getInstance().hangup() 方法。
可以透過 RCCallPlusSession#getCallId() 方法獲取執行接聽和結束通話操作所需的 CallId 值。
要接收遠端呼叫通知,必須確保已經註冊了 IRCCallPlusEventListener,並實現了 onReceivedCall(RCCallPlusSession callSession) 方法。被叫使用者透過與融雲服務端的連線或者離線推送通知(離線推送 App 必須已整合第三方廠商推送,詳見推送 2.0 整合概述)接收來電通知。
使用 setCallPlusEventListener 方法新增通話事件監聽,提供來電事件、通話狀態、通話記錄等事件相關回撥。
新增通話事件監聽:
RCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
/**
* 使用者透過該回撥接收到通話呼叫 在這個回撥中,可以接聽通話
*
* @param callSession 通話實體資訊<br>
*/
@Override
public void onReceivedCall(RCCallPlusSession callSession) {
RCCallPlusSession currentCallSession = RCCallPlusClient.getInstance().getCurrentCallSession();
if (currentCallSession != null && !TextUtils.equals(callSession.getCallId(), currentCallSession.getCallId())) {
//可以使用該方法判斷出,有正在進行中的通話,又有第二通通話呼入的情況<br>
//todo 第二通通話可以直接呼叫 RCCallPlusClient.getInstance().accept 方法接聽,SDK內部會將第一通通話結束通話
}
//todo SDK 的回撥均為子執行緒呼叫,showDialog() 方法中存在UI操作,所以切換到主執行緒執行
runOnUiThread(new Runnable() {
@Override
public void run() {
//todo 開啟攝像頭採集,請提前完成攝像頭、麥克風許可權的動態申請
RCCallPlusClient.getInstance().startCamera();
RCCallPlusClient.getInstance().enableMicrophone(true);
setLocalVideoView();//複用發起通話邏輯中的 設定本地影片渲染檢視 方法
showDialog(CallPlusActivity.this, "收到通話,是否接聽?", "接聽", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
acceptCall(callSession);
}
}, "結束通話", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
RCCallPlusClient.getInstance().hangup();
}
});
}
});
}
@Override
public void onCallEnded(RCCallPlusSession session, RCCallPlusReason reason) {
IRCCallPlusEventListener.super.onCallEnded(session, reason);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(CallPlusActivity.this,"通話結束,callId: "+session.getCallId() +" 通話結束原因:"+ reason.getValue(), Toast.LENGTH_SHORT).show();
}
});
}
/**
* 遠端使用者狀態改變監聽<br>
*
* @param callId 通話Id<br>
* @param userId 使用者Id<br>
* @param status 該使用者當前狀態<br>
* @param reason 該使用者當前狀態原因<br>
*/
@Override
public void onRemoteUserStateChanged(String callId, String userId, RCCallPlusUserSessionStatus status, RCCallPlusReason reason) {
IRCCallPlusEventListener.super.onRemoteUserStateChanged(callId, userId, status, reason);
runOnUiThread(new Runnable() {
@Override
public void run() {
StringBuilder stringBuilder = new StringBuilder("通話 ");
stringBuilder.append(callId).append(" 中的遠端使用者 ").append(userId).append(" 當前狀態為 ");
switch (status) {
case CALLING:
stringBuilder.append("呼叫中");
break;
case INVITED:
stringBuilder.append("被邀請中");
break;
case CONNECTING:
stringBuilder.append("已接聽,連線中");
break;
case ON_CALL:
stringBuilder.append("通話中");
break;
case ENDED:
stringBuilder.append("通話已結束");
break;
}
Toast.makeText(CallPlusActivity.this, stringBuilder.toString(), Toast.LENGTH_SHORT).show();
}
});
}
});
具體接聽通話的方法:
private void acceptCall(RCCallPlusSession callSession) {
setRemoteUserVideoView(callSession.getRemoteUserList());
/**
* 開始接聽通話
* 該方法內部為非同步執行,結果回撥是註冊的{@link RCCallPlusClient#setCallPlusResultListener(IRCCallPlusResultListener)} 監聽的 {@link IRCCallPlusResultListener#onAccept(RCCallPlusCode, String)}方法<br>
*/
RCCallPlusClient.getInstance().accept(callSession.getCallId());
}
接收端設定本地和遠端檢視:
/**
* 接聽方設定遠端使用者影片渲染檢視
*/
private void setRemoteUserVideoView(List<RCCallPlusUser> remoteUserList) {
List<RCCallPlusRemoteVideoView> remoteVideoViewList = new ArrayList<>();
for (RCCallPlusUser callPlusUser : remoteUserList) {
RCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoView(callPlusUser.getUserId(), this.getApplicationContext(), false);
//影片幀透過保持寬高比(可能顯示黑色邊框)來縮放以適應檢視的大小
remoteVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
remoteVideoViewList.add(remoteVideoView);
//本示例程式碼中,因為遠端檢視顯示在最頂層,為了防止遠端影片檢視被底部控制元件(檢視)遮擋,所以新增如下設定:
remoteVideoView.setZOrderOnTop(true);
remoteVideoView.setZOrderMediaOverlay(true);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
//todo 將每個遠端檢視(remoteVideoView)新增到XML中顯示,遠端為多人時,需要新增給多個控制元件顯示,本示例程式碼僅展示一個遠端使用者情況
mRemoteVideoViewFrameLayout.removeAllViews();
mRemoteVideoViewFrameLayout.addView(remoteVideoView, params);
}
/**
* 設定遠端使用者影片流渲染檢視給SDK
* 若沒有為遠端使用者設定影片渲染檢視,則不會產生該使用者的影片流的下行流量
*/
RCCallPlusClient.getInstance().setVideoView(remoteVideoViewList);
}
private AlertDialog showDialog(Context context, String content, String positiveBtn, final DialogInterface.OnClickListener positiveListener, final String negativeBtn, final DialogInterface.OnClickListener negativeListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder = builder.setMessage(content);
builder.setCancelable(false);
if (!TextUtils.isEmpty(positiveBtn)) {
builder.setPositiveButton(positiveBtn, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (positiveListener != null) {
positiveListener.onClick(dialog, which);
} else {
dialog.dismiss();
}
}
});
} if (!TextUtils.isEmpty(negativeBtn)) {
builder.setNegativeButton(negativeBtn, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (negativeListener != null) {
negativeListener.onClick(dialog, which);
} else {
dialog.dismiss();
}
}
});
} return builder.show();
}
執行專案發起通話,當被叫端接聽並且主叫端能夠看到雙方影片,說明 1V1 音視訊通話核心能力已經實現了,全程僅需 4 個步驟、3 個核心 API。
其他功能
切換前後攝像頭
成功開啟攝像頭後,可以使用 switchCamera 方法切換前後攝像頭。
switchCamera 方法是非同步呼叫的,支援透過 IRCCallPlusResultListener 的 onSwitchCamera 回撥來獲取呼叫結果。
RCCallPlusClient.getInstance().switchCamera();
RCCallPlusClient.getInstance().setCallPlusResultListener(new IRCCallPlusResultListener() {
/**
* 切換前後攝像頭方法結果回撥<br>
*
* @param code 方法請求結果<br>
* @param isFrontCamera 當前開啟的攝像頭是否是前置攝像頭<br>
*/
@Override
public void onSwitchCamera(RCCallPlusCode code, boolean isFrontCamera) {
IRCCallPlusResultListener.super.onSwitchCamera(code, isFrontCamera);
}
});
美顏
融雲 SDK 已經接入專業三方美顏服務,三步即可實現美顏功能。
在應用的 build.gradle 中新增如下遠端依賴項。
implementation 'cn.rongcloud.sdk:fu_beautifier:5.6.2' implementation 'androidx.core:core-ktx:1.7.0'
- 提供有效的美顏授權檔案(感興趣可聯絡融雲商務詳細諮詢:131 6185 6839)。
- 初始化美顏外掛,初始化時請提供有效的美顏授權檔案。應用執行期間只呼叫一次即可,建議在 Application#onCreate 中初始化。
下面程式碼塊中的 authpackNew 即為相關美顏服務的授權檔案。
RCRTCFUBeautifierEngine.getInstance()
.register(
getApplicationContext(),
null,
authpackNew.A(),
new FUBeautifierResultCallback() {
@Override
public void onSuccess() {
setBeautyEnable();
SetBeautyParameters();
}
@Override
public void onFailed(int code) {
}
});
private void setBeautyEnable() {
// 開啟美顏開關後設定的美顏效果才會生效;關閉開關美顏會失效。
RCRTCFUBeautifierEngine.getInstance().setBeautyEnable(true, new FUBeautifierResultCallback() {
@Override
public void onSuccess() {
}
@Override
public void onFailed(int code) {
}
});
}
// 設定美顏引數
private void SetBeautyParameters() {
RCRTCFUBeautifierEngine.getInstance().setBlurIntensity(6);//// 範圍[0-6]
RCRTCFUBeautifierEngine.getInstance().setColorIntensity(2); // 範圍 [0-2]
RCRTCFUBeautifierEngine.getInstance().setRedIntensity(2);// 範圍 [0-2]
RCRTCFUBeautifierEngine.getInstance().setSharpenIntensity(1);// 範圍 [0-1]
RCRTCFUBeautifierEngine.getInstance().setEyeBrightIntensity(1); // 範圍 [0-1]
RCRTCFUBeautifierEngine.getInstance().setToothIntensity(1);// 範圍 [0-1]
RCRTCFUBeautifierEngine.getInstance().setRemovePouchIntensity(1);// 範圍 [0-1]
RCRTCFUBeautifierEngine.getInstance().setRemoveLawPatternIntensity(1);// 範圍 [0-1]
}
在業務開發整合、上線運營等全過程中,融雲都將提供全流程一站式技術服務支援,開發者可提交工單與融雲工程師交流。歡迎來電諮詢:131 6185 6839
最後,callback 一下融雲 CallPlus SDK 的核心優勢:
完整封裝:提供完整的呼叫功能方案,包括連線、呼叫、接聽、拒接、結束通話、呼叫狀態通知等。
整合便捷:介面設計貼近業務且簡潔明瞭,結合 Quick Demo 原始碼,開發者只需使用 3 個核心介面,一小時即可實現音視訊通話核心功能。
場景全面:支援 iOS、Android、Web 等平臺,可以滿足陌生人社交、線上招聘、遠端醫療、線上諮詢、售後客服等多種單人和多人通話場景使用。
服務穩定: 100% 可靠必達的音影片呼叫信令能力,保證連線安全可靠;音訊弱網抗丟包 80%,影片弱網抗丟包 60%,並有 3A 演算法,保證通話清晰穩定。
周邊完善:提供業務場景所需的豐富高階功能,包括內容稽核、雲端錄製、高階美顏等,讓開發者的業務開展無憂且高效。
價效比高:目前月功能費僅為 1500 元/月,含 200,000 分鐘免費時長。場景靈活度高,不限音影片,影片最高解析度可支援 2K+;真正省心透明,支援 RTC 與 IM 服務單獨採購,且不單獨收取呼叫信令費用。