• 使用融云 CallPlus SDK,一小时实现一款 1V1 视频应用


    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 代码库。

    1. allprojects {
    2. repositories {
    3. ...
    4. //融云 maven 仓库地址
    5. maven {url "https://maven.rongcloud.cn/repository/maven-releases/"}
    6. }
    7. }

    添加依赖项

    在应用的 build.gradle 中,添加如下远程依赖项。

    注意:融云 CallPlus 业务依赖 IM 通道,所以须同时集成 IMLibCore SDK。

    1. dependencies {
    2. // 请填写具体的 SDK 版本号,新集成用户建议使用最新版。此处以5.6.2版本为例。
    3. implementation 'cn.rongcloud.sdk:im_libcore:5.6.2' // 即时通讯基础能力库。
    4. implementation 'cn.rongcloud.sdk:callplus_lib:1.0.0'// 音视频呼叫能力库(内含 rtc_lib)
    5. }

    权限声明

    在 AndroidManifest.xml 中声明 SDK 需要的所有权限。

    1. <uses-permission android:name="android.permission.INTERNET" />
    2. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    3. <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    4. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    5. <uses-permission android:name="android.permission.CAMERA" />
    6. <uses-permission android:name="android.permission.RECORD_AUDIO" />
    7. <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    8. <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 中。

    1. String appKey = "Your_AppKey"; // example: bos9p5rlcm2ba 创建融云开发者账号,获取 App Key
    2. InitOption initOption = new InitOption.Builder().build();
    3. RongCoreClient.init(getApplicationContext(), appKey, initOption);

    要拨打和接听一对一呼叫或开始多人呼叫,必须先通过 RongCoreClient 的 connect 方法连接融云服务器。

    传入用户身份令牌(Token),向融云服务器验证用户身份;连接成功后,使用 RCCallPlusClient.getInstance().init() 方法初始化和配置 CallPlus SDK。

    1. String token = "用户Token";// 您在申请开发者账号后,可以在融云后台北极星位置直接生成用户token
    2. RongCoreClient.connect(token, new IRongCoreCallback.ConnectCallback() {
    3. /**
    4. * 成功回调
    5. * @param userId 当前用户 ID
    6. */
    7. @Override
    8. public void onSuccess(String userId) {
    9. runOnUiThread(new Runnable() {
    10. @Override
    11. public void run() {
    12. //todo 尽管在主线程初始化 RCCallPlusClient 不是必需的,但考虑到代码示例后续对 RCCallPlusClient 的调用都在主线程进行,所以目前选择在主线程进行初始化。
    13. //todo 请确保在同一个线程进行 RCCallPlusClient 的初始化、反初始化和使用,以确保操作的一致性。
    14. RCCallPlusConfig config = RCCallPlusConfig.Builder.create().build();
    15. /**
    16. * 初始化并设置通话全局配置,重复调用该方法时SDK内部会重新初始化
    17. * @param config 设置通话全局配置
    18. * @return 方法调用后同步返回结果,可以在这里得到初始化是否成功
    19. */
    20. RCCallPlusResultCode resultCode = RCCallPlusClient.getInstance().init(config);
    21. }
    22. });
    23. }
    24. /**
    25. * 错误回调
    26. * @param errorCode 错误码
    27. */
    28. @Override
    29. public void onError(IRongCoreEnum.ConnectionErrorCode errorCode) {
    30. }
    31. /**
    32. * 数据库回调.
    33. * @param code 数据库打开状态. DATABASE_OPEN_SUCCESS 数据库打开成功; DATABASE_OPEN_ERROR 数据库打开失败
    34. */
    35. @Override
    36. public void onDatabaseOpened(DatabaseOpenStatus code) {
    37. }
    38. });

    发起呼叫并设置本地和远端视图

    使用 startCall 方法来发起一对一通话。

    该方法内部会以异步方式执行,并通过 IRCCallPlusResultListener#onStartCall 回调来获取方法的结果。

    在发起通话之前需先设置本地和远端视图,在对端接听视频通话时,本端会自动渲染对端的视图。

    使用 setCallPlusResultListener 方法添加通话 API 异步结果回调监听。该监听可以接收 startCall、accept、hangup 等方法的结果回调。

    发起呼叫:

    1. private void startCall(String remoteUserId) {
    2. //todo 打开摄像头采集,请提前完成摄像头、麦克风权限的动态申请
    3. RCCallPlusClient.getInstance().startCamera();
    4. RCCallPlusClient.getInstance().enableMicrophone(true);
    5. //设置本端视图
    6. setLocalVideoView();
    7. //设置对端视图
    8. setRemotVideoView(remoteUserId);
    9. List<String> userIds = new ArrayList<>();
    10. userIds.add(remoteUserId);//todo remoteUserId 为被呼叫的远端用户userId
    11. RCCallPlusType callType = RCCallPlusType.PRIVATE;//PRIVATE: 1V1通话
    12. RCCallPlusMediaType mediaType = RCCallPlusMediaType.VIDEO;
    13. /**
    14. * 开始发起呼叫
    15. * 该方法内部为异步执行,结果回调是注册的{@link RCCallPlusClient#setCallPlusResultListener(IRCCallPlusResultListener)} 监听的 {@link IRCCallPlusResultListener#onStartCall(RCCallPlusCode, String, List)}方法<br>
    16. */
    17. RCCallPlusClient.getInstance().startCall(userIds, callType, mediaType);
    18. }

    发起端设置本地视图:

    1. /**
    2. * 设置本地视频渲染视图
    3. */
    4. private void setLocalVideoView() {
    5. //创建本地视图对象
    6. RCCallPlusLocalVideoView localVideoView = new RCCallPlusLocalVideoView(this.getApplicationContext());
    7. //FIT: 视频帧通过保持宽高比(可能显示黑色边框)来缩放以适应视图的大小
    8. localVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
    9. //设置本地视图给 SDK
    10. RCCallPlusClient.getInstance().setVideoView(localVideoView);
    11. FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
    12. params.gravity = Gravity.CENTER_HORIZONTAL;//在父布局中横向居中显示
    13. //将本地视图添加到XML中显示
    14. //示例代码中 mLocalVideoViewFrameLayout 为 android.widget.FrameLayout 对象
    15. mLocalVideoViewFrameLayout.removeAllViews();
    16. mLocalVideoViewFrameLayout.addView(localVideoView, params);
    17. }

    发起端设置对端视图:

    1. /**
    2. * 发起通话时设置对端视频渲染视图
    3. */
    4. private void setRemotVideoView(String remoteUserId) {
    5. //创建远端视图对象 remoteUserId为远端用户userId
    6. RCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoView(remoteUserId, this.getApplicationContext(), false);
    7. //FIT: 视频帧通过保持宽高比(可能显示黑色边框)来缩放以适应视图的大小
    8. remoteVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
    9. //因为远端视图显示在最顶层,为了防止远端视频视图被底部控件遮挡,所以添加如下设置:
    10. remoteVideoView.setZOrderOnTop(true);
    11. remoteVideoView.setZOrderMediaOverlay(true);
    12. List<RCCallPlusRemoteVideoView> remoteVideoViewList = new ArrayList<>(); remoteVideoViewList.add(remoteVideoView);
    13. //设置远端视图给SDK
    14. RCCallPlusClient.getInstance().setVideoView(remoteVideoViewList);
    15. FrameLayout.LayoutParams remoteVideoViewParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
    16. remoteVideoViewParams.gravity = Gravity.CENTER_HORIZONTAL;
    17. //将远端视图添加到XML中显示
    18. //示例代码中 mRemoteVideoViewFrameLayout 为 android.widget.FrameLayout 对象
    19. mRemoteVideoViewFrameLayout.removeAllViews();
    20. mRemoteVideoViewFrameLayout.addView(remoteVideoView, remoteVideoViewParams);
    21. }

    接收端接听通话并设置本地和远端视图

    可选择接听或挂断来电,若要接听电话,请使用 accept 方法;若要挂断来电,请使用 RCCallPlusClient.getInstance().hangup() 方法。

    可以通过 RCCallPlusSession#getCallId() 方法获取执行接听和挂断操作所需的 CallId 值。

    要接收远端呼叫通知,必须确保已经注册了 IRCCallPlusEventListener,并实现了 onReceivedCall(RCCallPlusSession callSession) 方法。被叫用户通过与融云服务端的连接或者离线推送通知(离线推送 App 必须已集成第三方厂商推送,详见推送 2.0 集成概述)接收来电通知。

    使用 setCallPlusEventListener 方法添加通话事件监听,提供来电事件、通话状态、通话记录等事件相关回调。

    添加通话事件监听:

    1. RCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
    2. /**
    3. * 用户通过该回调接收到通话呼叫 在这个回调中,可以接听通话
    4. *
    5. * @param callSession 通话实体信息
    6. */
    7. @Override
    8. public void onReceivedCall(RCCallPlusSession callSession) {
    9. RCCallPlusSession currentCallSession = RCCallPlusClient.getInstance().getCurrentCallSession();
    10. if (currentCallSession != null && !TextUtils.equals(callSession.getCallId(), currentCallSession.getCallId())) {
    11. //可以使用该方法判断出,有正在进行中的通话,又有第二通通话呼入的情况
    12. //todo 第二通通话可以直接调用 RCCallPlusClient.getInstance().accept 方法接听,SDK内部会将第一通通话挂断
    13. }
    14. //todo SDK 的回调均为子线程调用,showDialog() 方法中存在UI操作,所以切换到主线程执行
    15. runOnUiThread(new Runnable() {
    16. @Override
    17. public void run() {
    18. //todo 打开摄像头采集,请提前完成摄像头、麦克风权限的动态申请
    19. RCCallPlusClient.getInstance().startCamera();
    20. RCCallPlusClient.getInstance().enableMicrophone(true);
    21. setLocalVideoView();//复用发起通话逻辑中的 设置本地视频渲染视图 方法
    22. showDialog(CallPlusActivity.this, "收到通话,是否接听?", "接听", new OnClickListener() {
    23. @Override
    24. public void onClick(DialogInterface dialogInterface, int i) {
    25. acceptCall(callSession);
    26. }
    27. }, "挂断", new OnClickListener() {
    28. @Override
    29. public void onClick(DialogInterface dialogInterface, int i) {
    30. RCCallPlusClient.getInstance().hangup();
    31. }
    32. });
    33. }
    34. });
    35. }
    36. @Override
    37. public void onCallEnded(RCCallPlusSession session, RCCallPlusReason reason) {
    38. IRCCallPlusEventListener.super.onCallEnded(session, reason);
    39. runOnUiThread(new Runnable() {
    40. @Override
    41. public void run() {
    42. Toast.makeText(CallPlusActivity.this,"通话结束,callId: "+session.getCallId() +" 通话结束原因:"+ reason.getValue(), Toast.LENGTH_SHORT).show();
    43. }
    44. });
    45. }
    46. /**
    47. * 远端用户状态改变监听
    48. *
    49. * @param callId 通话Id
    50. * @param userId 用户Id
    51. * @param status 该用户当前状态
    52. * @param reason 该用户当前状态原因
    53. */
    54. @Override
    55. public void onRemoteUserStateChanged(String callId, String userId, RCCallPlusUserSessionStatus status, RCCallPlusReason reason) {
    56. IRCCallPlusEventListener.super.onRemoteUserStateChanged(callId, userId, status, reason);
    57. runOnUiThread(new Runnable() {
    58. @Override
    59. public void run() {
    60. StringBuilder stringBuilder = new StringBuilder("通话 ");
    61. stringBuilder.append(callId).append(" 中的远端用户 ").append(userId).append(" 当前状态为 ");
    62. switch (status) {
    63. case CALLING:
    64. stringBuilder.append("呼叫中");
    65. break;
    66. case INVITED:
    67. stringBuilder.append("被邀请中");
    68. break;
    69. case CONNECTING:
    70. stringBuilder.append("已接听,连接中");
    71. break;
    72. case ON_CALL:
    73. stringBuilder.append("通话中");
    74. break;
    75. case ENDED:
    76. stringBuilder.append("通话已结束");
    77. break;
    78. }
    79. Toast.makeText(CallPlusActivity.this, stringBuilder.toString(), Toast.LENGTH_SHORT).show();
    80. }
    81. });
    82. }
    83. });

    具体接听通话的方法:

    1. private void acceptCall(RCCallPlusSession callSession) {
    2. setRemoteUserVideoView(callSession.getRemoteUserList());
    3. /**
    4. * 开始接听通话
    5. * 该方法内部为异步执行,结果回调是注册的{@link RCCallPlusClient#setCallPlusResultListener(IRCCallPlusResultListener)} 监听的 {@link IRCCallPlusResultListener#onAccept(RCCallPlusCode, String)}方法
    6. */
    7. RCCallPlusClient.getInstance().accept(callSession.getCallId());
    8. }

    接收端设置本地和远端视图:

    1. /**
    2. * 接听方设置远端用户视频渲染视图
    3. */
    4. private void setRemoteUserVideoView(List<RCCallPlusUser> remoteUserList) {
    5. List<RCCallPlusRemoteVideoView> remoteVideoViewList = new ArrayList<>();
    6. for (RCCallPlusUser callPlusUser : remoteUserList) {
    7. RCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoView(callPlusUser.getUserId(), this.getApplicationContext(), false);
    8. //视频帧通过保持宽高比(可能显示黑色边框)来缩放以适应视图的大小
    9. remoteVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
    10. remoteVideoViewList.add(remoteVideoView);
    11. //本示例代码中,因为远端视图显示在最顶层,为了防止远端视频视图被底部控件(视图)遮挡,所以添加如下设置:
    12. remoteVideoView.setZOrderOnTop(true);
    13. remoteVideoView.setZOrderMediaOverlay(true);
    14. FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
    15. params.gravity = Gravity.CENTER_HORIZONTAL;
    16. //todo 将每个远端视图(remoteVideoView)添加到XML中显示,远端为多人时,需要添加给多个控件显示,本示例代码仅展示一个远端用户情况
    17. mRemoteVideoViewFrameLayout.removeAllViews();
    18. mRemoteVideoViewFrameLayout.addView(remoteVideoView, params);
    19. }
    20. /**
    21. * 设置远端用户视频流渲染视图给SDK
    22. * 若没有为远端用户设置视频渲染视图,则不会产生该用户的视频流的下行流量
    23. */
    24. RCCallPlusClient.getInstance().setVideoView(remoteVideoViewList);
    25. }
    26. private AlertDialog showDialog(Context context, String content, String positiveBtn, final DialogInterface.OnClickListener positiveListener, final String negativeBtn, final DialogInterface.OnClickListener negativeListener) {
    27. AlertDialog.Builder builder = new AlertDialog.Builder(context);
    28. builder = builder.setMessage(content);
    29. builder.setCancelable(false);
    30. if (!TextUtils.isEmpty(positiveBtn)) {
    31. builder.setPositiveButton(positiveBtn, new DialogInterface.OnClickListener() {
    32. @Override
    33. public void onClick(DialogInterface dialog, int which) {
    34. if (positiveListener != null) {
    35. positiveListener.onClick(dialog, which);
    36. } else {
    37. dialog.dismiss();
    38. }
    39. }
    40. });
    41. } if (!TextUtils.isEmpty(negativeBtn)) {
    42. builder.setNegativeButton(negativeBtn, new DialogInterface.OnClickListener() {
    43. @Override
    44. public void onClick(DialogInterface dialog, int which) {
    45. if (negativeListener != null) {
    46. negativeListener.onClick(dialog, which);
    47. } else {
    48. dialog.dismiss();
    49. }
    50. }
    51. });
    52. } return builder.show();
    53. }

    运行项目发起通话,当被叫端接听并且主叫端能够看到双方视频,说明 1V1 音视频通话核心能力已经实现了,全程仅需 4 个步骤、3 个核心 API。


    其他功能

    切换前后摄像头

    成功打开摄像头后,可以使用 switchCamera 方法切换前后摄像头。

    switchCamera 方法是异步调用的,支持通过 IRCCallPlusResultListener 的 onSwitchCamera 回调来获取调用结果。

    RCCallPlusClient.getInstance().switchCamera();
    1. RCCallPlusClient.getInstance().setCallPlusResultListener(new IRCCallPlusResultListener() {
    2. /**
    3. * 切换前后摄像头方法结果回调
    4. *
    5. * @param code 方法请求结果
    6. * @param isFrontCamera 当前开启的摄像头是否是前置摄像头
    7. */
    8. @Override
    9. public void onSwitchCamera(RCCallPlusCode code, boolean isFrontCamera) {
    10. IRCCallPlusResultListener.super.onSwitchCamera(code, isFrontCamera);
    11. }
    12. });

    美颜

    融云 SDK 已经接入专业三方美颜服务,三步即可实现美颜功能。

    1. 在应用的 build.gradle 中添加如下远程依赖项。

      1. implementation 'cn.rongcloud.sdk:fu_beautifier:5.6.2'
      2. implementation 'androidx.core:core-ktx:1.7.0'

    2. 提供有效的美颜授权文件(感兴趣可联系融云商务详细咨询:131 6185 6839)。
    3. 初始化美颜插件,初始化时请提供有效的美颜授权文件。应用运行期间只调用一次即可,建议在 Application#onCreate 中初始化。

    下面代码块中的 authpackNew 即为相关美颜服务的授权文件。

    1. RCRTCFUBeautifierEngine.getInstance()
    2. .register(
    3. getApplicationContext(),
    4. null,
    5. authpackNew.A(),
    6. new FUBeautifierResultCallback() {
    7. @Override
    8. public void onSuccess() {
    9. setBeautyEnable();
    10. SetBeautyParameters();
    11. }
    12. @Override
    13. public void onFailed(int code) {
    14. }
    15. });
    1. private void setBeautyEnable() {
    2. // 打开美颜开关后设置的美颜效果才会生效;关闭开关美颜会失效。
    3. RCRTCFUBeautifierEngine.getInstance().setBeautyEnable(true, new FUBeautifierResultCallback() {
    4. @Override
    5. public void onSuccess() {
    6. }
    7. @Override
    8. public void onFailed(int code) {
    9. }
    10. });
    11. }
    12. // 设置美颜参数
    13. private void SetBeautyParameters() {
    14. RCRTCFUBeautifierEngine.getInstance().setBlurIntensity(6); 范围[0-6]
    15. RCRTCFUBeautifierEngine.getInstance().setColorIntensity(2); // 范围 [0-2]
    16. RCRTCFUBeautifierEngine.getInstance().setRedIntensity(2);// 范围 [0-2]
    17. RCRTCFUBeautifierEngine.getInstance().setSharpenIntensity(1);// 范围 [0-1]
    18. RCRTCFUBeautifierEngine.getInstance().setEyeBrightIntensity(1); // 范围 [0-1]
    19. RCRTCFUBeautifierEngine.getInstance().setToothIntensity(1);// 范围 [0-1]
    20. RCRTCFUBeautifierEngine.getInstance().setRemovePouchIntensity(1);// 范围 [0-1]
    21. RCRTCFUBeautifierEngine.getInstance().setRemoveLawPatternIntensity(1);// 范围 [0-1]
    22. }

    在业务开发集成、上线运营等全过程中,融云都将提供全流程一站式技术服务支持,开发者可提交工单与融云工程师交流。欢迎来电咨询:131 6185 6839


    最后,callback 一下融云 CallPlus SDK 的核心优势:

    完整封装:提供完整的呼叫功能方案,包括连接、呼叫、接听、拒接、挂断、呼叫状态通知等。

    集成便捷:接口设计贴近业务且简洁明了,结合 Quick Demo 源码,开发者只需使用 3 个核心接口,一小时即可实现音视频通话核心功能。

    场景全面:支持 iOS、Android、Web 等平台,可以满足陌生人社交、在线招聘、远程医疗、线上咨询、售后客服等多种单人和多人通话场景使用。

    服务稳定: 100% 可靠必达的音视频呼叫信令能力,保证连接安全可靠;音频弱网抗丢包 80%,视频弱网抗丢包 60%,并有 3A 算法,保证通话清晰稳定。

    周边完善:提供业务场景所需的丰富高级功能,包括内容审核、云端录制、高级美颜等,让开发者的业务开展无忧且高效。

    性价比高:目前月功能费仅为 1500 元/月,含 200,000 分钟免费时长。场景灵活度高,不限音视频,视频最高分辨率可支持 2K+;真正省心透明,支持 RTC 与 IM 服务单独采购,且不单独收取呼叫信令费用。

  • 相关阅读:
    【DM8】达梦8 DEM部署
    spring注解开发
    Bert-vits2-v2.2新版本本地训练推理整合包(原神八重神子英文模型miko)
    HTML基本讲解与使用
    Java NIO 关键概念之 Buffer
    集训杂记 7/17
    RDS - 远程桌面服务
    MySQL报错this is incompatible withsal mode=only full group by处理办法
    15.2.1 语法格式
    机器学习(3)
  • 原文地址:https://blog.csdn.net/weixin_44764152/article/details/132831448