使用融雲 CallPlus SDK,一小時實現一款 1V1 影片應用

發表於2023-09-19


9 月 21 日,融雲直播課 社交泛娛樂出海最短變現路徑如何快速實現一款 1V1 影片應用? 歡迎點選上方小程式報名~

1V1 音影片、遠端服務類應用的實現利器——融雲 CallPlus SDK 上線! 關注【融雲全球網際網路通訊雲】瞭解更多

作為新一代音視訊通話場景化 SDK,融雲 CallPlus 完整封裝了撥打、接聽、結束通話等整套呼叫流程,支援一對一及多人音視訊通話,功能齊全、體驗絲滑,且契合海外使用者的互動偏好。

本文將以 Android 端整合為例,分享實戰教程:

使用融雲 CallPlus SDK,一小時整合 1V1 視訊通話能力。

一個 RTC 實時音影片底層零經驗開發者,只需 3 個核心 API、4 步 即可輕鬆實現音視訊通話能力。並且,融雲提供 Quick Demo 原始碼供開發者整合參考。


前置條件

建立融雲開發者賬號

建立融雲開發者賬號,獲取 App Key

開始之前,需建立融雲開發者賬號並獲取 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 已經接入專業三方美顏服務,三步即可實現美顏功能。

  1. 在應用的 build.gradle 中新增如下遠端依賴項。

    implementation 'cn.rongcloud.sdk:fu_beautifier:5.6.2'
    implementation 'androidx.core:core-ktx:1.7.0'
  2. 提供有效的美顏授權檔案(感興趣可聯絡融雲商務詳細諮詢:131 6185 6839)。
  3. 初始化美顏外掛,初始化時請提供有效的美顏授權檔案。應用執行期間只呼叫一次即可,建議在 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 服務單獨採購,且不單獨收取呼叫信令費用。

相關文章