快速开始
1. 前提条件
支持 iOS 8.0 或以上版本的 iOS 真机设备。
有效的菊风云开发者账号 免费注册
2. 准备工作
开始之前,请先做好如下准备工作。
2.1 SDK 下载
点击 iOS SDK 进行下载。如果已经下载了 SDK,请直接进行 SDK 配置。
注解
从 2.1 版本开始提供动态库和静态库。开发者可以根据需要下载对应的库。
2.2 AppKey 获取
AppKey 是应用在 菊风云平台 中的唯一标识。需要在 SDK 初始化的时候使用,AppKey 获取请参考 创建应用 。
2.3 SDK 配置
您可以在工程中使用静态库或者动态库,此处介绍使用静态库的配置方法。如果想使用动态库,请参考动态库的配置说明文档 iOS 导入动态库 。
打开下载的 JC SDK 静态库,目录如下:
注解
从 2.1 版本开始,下载的 SDK 中包含 JCCloudWrapper.framework,该库是为IM(消息)服务的,如果需要集成 IM 消息,则需要在工程中添加 JCCloudWrapper.framework;如果不需要集成 IM 消息,则无需集成该库。
只有完成 SDK 的配置之后,您才可以集成 JC SDK 提供的功能,请按以下操作完成静态库的配置:
导入静态库
有两种方式导入静态库
方法一:使用 CocoaPods 自动导入
注解
该方法仅适用于 2.0 及以上版本的静态库集成。
SDK 支持使用 CocoaPods 导入 SDK。 需要安装 CocoaPods 环境,请参照 CocoaPods 官网 安装。
CocoaPods 环境安装好后,请执行以下操作:
- 打开 Terminal,cd 至项目根目录
- 执行 pod init,项目文件夹下会生成一个 Podfile 文本文件
- 执行 open -e Podfile
- 添加导入配置 pod 'JuphoonCloudSDK_iOS', '2.1'
修改后内容如下所示,注意将 Your App 替换为你的 Target 名称。
platform :ios, '9.0'
#use_frameworks!
target 'Your App' do
pod 'JuphoonCloudSDK_iOS', '2.1'
end
2.0 为当前发布的最新版本,关于指定具体版本请参考 pod 使用规范 。
- 执行 pod install,成功安装后,Terminal 中会显示 Pod installation complete!,此时项目文件夹下会生成一个 xcworkspace 文件。
注解
如果需要更新本地库版本,在执行 pod install 之前执行 pod update 命令。
- 双击打开 .xcworkspace 文件即可。
方法二:手动导入
拷贝文件
将 sdk 文件夹拷贝到您工程所在的目录下。
工程设置
1. 导入 SDK
打开 Xcode,进入 TARGETS > Project Name > Build Phases > Link Binary with Libraries 菜单,点击 '+' 符号,导入 sdk 文件夹下的 JCSDKOC.framework、lib 文件夹下的两个 .a 文件,如下图:
2. 导入 SDK 依赖的库
继续点击 '+' 符号,导入下图红框中的库:
3. 设置路径
点击 'Build Settings',找到 Framework Search Paths 、Header Search Paths(头文件路径) 和 Library Search Paths(库文件路径)。并设置 Framework Search Paths、Header Search Paths 和 Library Search Paths,如下图:
注解
在完成第1步导入 JCSDKOC.framework 和两个.a文件后,Xcode 会自动生成该路径,如果 Xcode 没有自动生成路径,用户要根据 JCSDKOC.framework 、include 和 lib 库文件所在目录,手动设置路径。
4. 设置 Enable Bitcode 为 NO
点击 'Build Settings',找到 Enable Bitcode 设置为 NO,如下图:
5. 设置 Other Linker Flags 的参数为 -ObjC
点击 'Build Settings',找到 Other Linker Flags 并添加参数 -ObjC,如下图:
6.设置预处理宏定义
点击 'Build Settings',找到 Preprocessor Macros,在右侧输入 ZPLATFORM=ZPLATFORM_IOS,如下图:
如果设置了 APNs 推送,则还需要在 Preprocessor Macros 下的 Debug 中输入 DEBUG,如下图:
注解
DEBUG 宏定义的目的是为了区分推送环境是 release 还是 debug,环境不对会导致推送失败。
7. 设置 Documentation Comments 为 NO
点击 'Build Settings',找到 Documentation Comments 并设置为 NO,如下图:
8. 设置后台运行模式
点击 'Capabilities',找到 Background Modes,勾选红框内的 Audio, AirPlay, and Picture in Picture ,如下图:
权限设置
9. 设置麦克风和摄像头权限
点击 'Info',然后添加麦克风和摄像头的权限,如下图:
Key | Type | Value |
---|---|---|
Privacy - Microphone Usage Description | String | 使用麦克风的目的,如语音通话。 |
Privacy - Camera Usage Description | String | 使用摄像头的目的,如视频通话。 |
10. 编译运行
以上步骤进行完后,编译工程,如果提示 succeeded,恭喜您已经成功配置 SDK,可以进行 SDK 初始化了。
2.4 SDK 初始化
在使用 SDK 之前,需要进行 SDK 的初始化。
首先在需要使用 JC SDK 的地方 #import <JCSDKOC/JCSDKOC.h>
然后在实现初始化的文件中实现 JCClientCallback 回调,用于接收 JCClient 相关通知。
JCClientCreateParam 对象有以下属性
/// sdk信息存储目录
@property (nonatomic, copy) NSString* __nonnull sdkInfoDir;
/// sdk日志目录
@property (nonatomic, copy) NSString* __nonnull sdkLogDir;
/// sdk日志等级 JCLogLevel
@property (nonatomic) JCLogLevel sdkLogLevel;
调用下面的接口初始化 SDK
/**
* @brief 创建 JCClient 实例
* @param appKey 用户从 Juphoon Cloud 平台上申请的 AppKey 字符串
* @param callback 回调接口,用于接收 JCClient 相关通知
* @param createParam 创建参数,nil 则按默认值创建
* @return JCClient 对象
*/
+(JCClient* __nullable)create:(NSString* __nonnull)appKey callback:(id<JCClientCallback> __nonnull)callback creatParam:(JCClientCreateParam* __nullable)createParam;
示例代码
// 初始化,因为这些模块实例将被频繁使用,建议声明在单例中
JCClientCreateParam *param = [[JCClientCreateParam alloc] init];
param.sdkLogLevel = JCLogLevelInfo;
param.sdkInfoDir = @"SDK 信息存放路径";
param.sdkLogDir = @"日志存放路径";
JCClient *client = [JCClient create:@"your appkey" callback:self creatParam:param];
SDK 初始化之后,即可进行登录的集成。
3. 登录
登录涉及 JCClient 类及其回调 JCClientCallback 类,其主要作用是负责登录、登出管理及帐号信息存储。
登录之前,可以通过配置关键字进行登录的相关配置,如是否使用代理服务器登录以及服务器地址的设置,具体如下。
3.1 登录环境设置
服务器地址设置,包括国际环境服务器地址和国内环境服务器地址
JCClientLoginParam* loginParam = [[JCClientLoginParam alloc] init];
//默认国内环境 http:cn.router.justalkcloud.com:8080
loginParam.serverAddress = @"服务器地址";
//如果使用代理服务器登录
loginParam.httpsProxy = @"代理服务器地址";
开发者可以使用自定义服务器地址。
还可以通过 displayName 属性设置昵称,例如
client.displayName = @"小张";
3.2 发起登录
/**
* @brief 登陆 Juphoon Cloud 平台,只有登陆成功后才能进行平台上的各种业务
* 服务器分为鉴权模式和非鉴权模式
*
* - 鉴权模式: 服务器会检查用户名和密码
*
* - 免鉴权模式: 只要用户保证用户标识唯一即可, 服务器不校验
*
* 登陆结果通过 JCClientCallback 通知
*
* @param userId 用户名
* @param password 密码,免鉴权模式密码可以随意输入,但不能为空
* @param loginParam 登录参数,nil则按照默认值登录
* @return 返回 true 表示正常执行调用流程,false 表示调用异常,异常错误通过 JCClientCallback 通知
* @warning 目前只支持免鉴权模式,免鉴权模式下当账号不存在时会自动去创建该账号
* @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
*/
-(bool)login:(NSString* __nonnull)userId password:(NSString* __nonnull)password loginParam:(JCClientLoginParam* __nullable)loginParam;
示例代码
JCClientLoginParam* loginParam = [[JCClientLoginParam alloc] init];
//默认国内环境 http:cn.router.justalkcloud.com:8080
loginParam.serverAddress = @"服务器地址";
[client login:@"账号" password:@"123" loginParam:loginParam];
登录成功之后,首先会触发登录状态改变(onClientStateChange)回调
/**
* @brief 登录状态变化通知
* @param state 当前状态值
* @param oldState 之前状态值
*/
-(void)onClientStateChange:(JCClientState)state oldState:(JCClientState)oldState;
JCClientState 有
// 未初始化
JCClientStateNotInit,
// 未登录
JCClientStateIdle,
// 登录中
JCClientStateLogining,
// 登录成功
JCClientStateLogined,
// 登出中
JCClientStateLogouting,
示例代码
-(void)onClientStateChange:(JCClientState)state oldState:(JCClientState)oldState
{
if (state == JCClientStateIdle) { // 未登录
...
} else if (state == JCClientStateLogining) { // 登录中
...
} else if (state == JCClientStateLogined) { // 登录成功
...
} else if (state == JCClientStateLogouting) { // 登出中
...
}
}
之后通过 onLogin 回调上报登录结果
/**
* @brief 登陆结果回调
* @param result true 表示登陆成功,false 表示登陆失败
* @param reason 当 result 为 false 时该值有效
* @see JCClientReason
*/
-(void)onLogin:(bool)result reason:(JCClientReason)reason;
其中,JCClientReason 有
/// 正常
JCClientReasonNone,
/// sdk 未初始化
JCClientReasonSDKNotInit,
/// 无效的参数
JCClientReasonInvalidParam,
/// 函数调用失败
JCClientReasonCallFunctionError,
/// 当前状态无法再次登录
JCClientReasonStateCannotLogin,
/// 超时
JCClientReasonTimeOut,
/// 网络异常
JCClientReasonNetWork,
/// appkey 错误
JCClientReasonAppKey,
/// 账号密码错误
JCClientReasonAuth,
/// 无该用户
JCClientReasonNoUser,
/// 被强制登出
JCClientReasonServerLogout,
/// 其他错误
JCClientReasonOther,
登录成功之后,SDK 会自动保持与服务器的连接状态,直到用户主动调用登出接口,或者因为帐号在其他设备登录导致该设备登出。
3.3 登出
登出调用下面的方法,登出后不能进行平台上的各种业务操作
/**
* 登出 菊风云平台,登出后不能进行平台上的各种业务
* @return 返回 true 表示正常执行调用流程,false 表示调用异常,异常错误通过 JCClientCallback 通知
*/
-(bool)logout;
登出同样会触发登录状态改变(onClientStateChange)回调
之后将通过 onlogout 回调上报登出结果
/**
* @brief 登出回调
* @param reason 登出原因
* @see JCClientReason
*/
-(void)onLogout:(JCClientReason)reason;
集成登录后,即可进行相关业务的集成。
4. 业务集成
一对一视频通话涉及以下类
名称 | 描述 |
---|---|
JCCall | 一对一通话类,包含一对一语音和视频通话功能 |
JCCallItem | 通话对象类,此类主要记录通话的一些状态,UI 可以根据其中的状态进行显示逻辑 |
JCCallCallback | 通话模块回调代理 |
JCMediaDevice | 设备模块,主要用于视频、音频设备的管理 |
JCMediaDeviceVideoCanvas | 视频对象,主要用于 UI 层视频显示、渲染的控制 |
JCMediaDeviceCallback | 设备模块回调代理 |
更多接口的详细信息请参考 API 说明文档 。
接口调用逻辑和相关状态
说明:蓝色与黄色字表示接口,红色字表示通话状态。
开始集成通话功能前,请先实现 JCMediaDeviceCallback, JCCallCallback 回调,用于接收 JCMediaDevice 和 JCCall 的相关通知。
之后进行模块的初始化
创建 JCMediaDevice 实例
/**
* @brief 创建 JCMediaDevice 对象
* @param client JCClient 对象
* @param callback JCMediaDeviceCallback 回调接口,用于接收 JCMediaDevice 相关通知
* @return 返回 JCMediaDevice 对象
*/
+(JCMediaDevice* __nullable)create:(JCClient* __nonnull)client callback:(id<JCMediaDeviceCallback> __nonnull)callback;
创建 JCCall 实例
/**
* @brief 创建 JCCall 实例
* @param client JCClient 实例
* @param mediaDevice JCMediaDevice 实例
* @param callback JCCallCallback 回调接口,用于接收 JCCall 相关回调事件
* @return 返回 JCCall 实例
*/
+(JCCall* __nullable)create:(JCClient* __nonnull)client mediaDevice:(JCMediaDevice* __nonnull)mediaDevice callback:(id<JCCallCallback> __nonnull)callback;
示例代码
// 初始化各模块,因为这些模块实例将被频繁使用,建议声明在单例中
JCMediaDevice *mediaDevice = [JCMediaDevice create:client callback:self];
JCCall *call = [JCCall create:client mediaDevice:mediaDevice callback:self];
开始集成
媒体参数配置
一对一视频通话支持智能硬件设备集成,需要在发起通话前设置媒体参数
// 根据模式生成配置参数
JCCallMediaConfig *mediaConfig = [JCCallMediaConfig generateByMode:JCCallMediaConfigModeIntelligentHardware];
// 设置媒体参数
JCManager.shared.call.mediaConfig = mediaConfig;
菊风提供三种配置模式供开发者选择,并开放相关属性供开发者进行灵活的自定义配置,具体方法请查看 媒体参数设置 。
4.1 拨打通话
主叫调用下面的接口发起视频通话,此时 video 传入值为 true
/**
* @brief 一对一呼叫
* @param userId 用户标识
* @param video 是否为视频呼叫
* @param extraParam 透传参数,被叫方可获取透传参数
* @return 返回 true 表示正常执行调用流程,false 表示调用异常
*/
-(bool)call:(NSString* __nonnull)userId video:(bool)video extraParam:(NSString * __nullable)extraParam;
通话发起后,主叫和被叫均会收到新增通话的回调,通话状态变为 JCCallStatePending
/**
* @brief 新增通话回调
* @param item JCCallItem 对象
*/
-(void)onCallItemAdd:(JCCallItem* __nonnull)item;
示例代码
-(void)onCallItemAdd:(JCCallItem* __nonnull)item {
// 收到新增通话回调
}
4.1.1 创建本地视频画面
通话发起后,即可调用 JCMediaDevice 类中的 startCameraVideo 方法打开本地视频预览,调用此方法会打开摄像头
/**
* @brief 获得预览视频对象,通过此对象能获得视图用于UI显示
* @param type 渲染模式,@ref JCMediaDeviceRender
* @return JCMediaDeviceVideoCanvas 对象
*/
-(JCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(int)type;
其中,type(渲染模式)可以参考 JCMediaDeviceRender 的枚举值,具体如下
/// 视频图像按比例填充整个渲染区域(裁剪掉超出渲染区域的部分区域)
JCMediaDeviceRenderFullScreen = 0,
/// 视频图像的内容完全呈现到渲染区域(可能会出现黑边,类似放电影的荧幕)
JCMediaDeviceRenderFullContent,
/// 自动
JCMediaDeviceRenderFullAuto,
该方法采集分辨率默认值为 640*360,帧率为 30。默认打开的是前置摄像头。
如果想自定义摄像头采集参数,如采集的高度、宽度和帧速率,请参考 视频采集和渲染。
示例代码
// 发起视频呼叫
[call call:@"peer number" video:true extraParam:@"自定义透传字符串"];
// 本地视频渲染
JCMediaDeviceVideoCanvas *localCanvas = [mediaDevice startCameraVideo:JCMediaDeviceRenderFullContent];
localCanvas.videoView.frame = CGRectMake(20, 20, 90, 160);
[self.view addSubview:localCanvas.videoView];
4.2 应答通话
被叫收到 onCallItemAdd 回调事件,并通过 JCCallItem 中的 video 属性以及 direction 属性值 JCCallDirectionIn 判断是视频呼入还是语音呼入,此时可以调用以下接口选择视频应答或者语音应答
/**
* @brief 接听
* @param item JCCallItem 对象
* @param video 针对视频呼入可以选择以视频接听还是音频接听
* @return 返回 true 表示正常执行调用流程,false 表示调用异常
*/
-(bool)answer:(JCCallItem* __nonnull)item video:(bool)video;
如果被叫应答通话成功,双方都会收到 onCallItemUpdate 的回调。
示例代码
-(void)onCallItemAdd:(JCCallItem*)item {
// 如果是视频呼入且在振铃中
if(item && item.state == JCCallStatePending) {
if (item.direction == JCCallDirectionIn && item.video) {
// 应答通话
[call answer:item video:true];
}
}
}
通话应答后,通话状态变为 JCCallStateConnecting。
4.3 通话建立
被叫接听通话后,双方将建立连接,此时,主叫和被叫都将会收到通话更新的回调(onCallItemUpdate)。连接成功之后,可以进行远端视频的渲染。
4.3.1 创建远端视频画面
远端视频画面的获取通过调用 JCMediaDevice 类中的 startVideo 方法实现
/**
* @brief 获得预览视频对象,通过此对象能获得视图用于UI显示
* @param videoSource 渲染标识串,比如 JCMediaChannelParticipant JCCallItem 中的 renderId,当videoSource 为 videoFileId 时,内部会调用 startVideoFile
* @param type 渲染模式,@ref JCMediaDeviceRender
* @return JCMediaDeviceVideoCanvas 对象
*/
-(JCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)videoSource renderType:(int)type;
其中,type(渲染模式)可以参考 JCMediaDeviceRender 的枚举值,具体如下
/// 视频图像按比例填充整个渲染区域(裁剪掉超出渲染区域的部分区域)
JCMediaDeviceRenderFullScreen = 0,
/// 视频图像的内容完全呈现到渲染区域(可能会出现黑边,类似放电影的荧幕)
JCMediaDeviceRenderFullContent,
/// 自动
JCMediaDeviceRenderFullAuto,
现在您可以进行一对一视频通话了。
示例代码
-(void)onCallItemUpdate:(JCCallItem*)item {
// 如果对端在上传视频流(uploadVideoStreamOther)
if (item.state == JCCallStateTalking && remoteCanvas == nil && item.uploadVideoStreamOther) {
// 获取远端视频画面,renderId来源JCCallItem对象
JCMediaDeviceVideoCanvas *remoteCanvas = [mediaDevice startVideo:item.renderId renderType:JCMediaDeviceRenderFullContent];
remoteCanvas.videoView.frame = self.view.frame;
[self.view addSubview:remoteCanvas.videoView];
}
}
4.4 挂断通话
主叫或者被叫均可以调用下面的方法挂断通话
/**
* @brief 获得当前活跃通话
*
* @return 当前活跃通话,没有则返回nil
*/
-(JCCallItem* __nullable)getActiveCallItem;
当前活跃通话对象获取后,调用下面的方法挂断通话
/**
* @brief 挂断
* @param item JCCallItem 对象
* @param reason 挂断原因
* @param description 挂断描述
* @return 返回 true 表示正常执行调用流程,false 表示调用异常
* @see JCCallReason
*/
-(bool)term:(JCCallItem* __nonnull)item reason:(JCCallReason)reason description:(NSString* __nullable)description;
示例代码
// 挂断通话
JCCallItem *item = [call getActiveCallItem];
[call term:item reason:JCCallReasonNone description:@"主叫挂断"];
4.4.1 销毁本地和远端视频画面
通话挂断后,还需要调用 stopVideo 接口移除视频画面
/**
* @brief 停止视频
* @param canvas JCMediaDeviceVideoCanvas 对象,由 startVideo 获得
*/
-(void)stopVideo:(JCMediaDeviceVideoCanvas* __nonnull)canvas;
通话挂断后,UI 会收到移除通话的回调,通话状态变为 JCCallStateOk
/**
* @brief 移除通话
* @param item JCCallItem 对象
* @param reason 通话结束原因
* @param description 通话结束原因的描述,只有被动挂断的时候,才会收到这个值,其他情况下则返回空字符串
* @see JCCallReason
*/
-(void)onCallItemRemove:(JCCallItem* __nonnull)item reason:(JCCallReason)reason description:(NSString * __nullable)description;
其中,reason 有以下几种
名称 | 描述 |
---|---|
JCCallReasonNone | 无异常 |
JCCallReasonNotLogin | 未登录 |
JCCallReasonCallFunctionError | 函数调用错误 |
JCCallReasonTimeOut | 超时 |
JCCallReasonNetWork | 网络错误 |
JCCallReasonCallOverLimit | 超出通话上限 |
JCCallReasonTermBySelf | 自己挂断 |
JCCallReasonAnswerFail | 应答失败 |
JCCallReasonBusy | 忙 |
JCCallReasonDecline | 拒接 |
JCCallReasonUserOffline | 用户不在线 |
JCCallReasonNotFound | 无此用户 |
JCCallReasonRejectVideoWhenHasCall | 已有通话拒绝视频来电 |
JCCallReasonRejectCallWhenHasVideoCall | 已有视频通话拒绝来电 |
JCCallReasonOther | 其他错误 |
示例代码
-(void)onCallItemRemove:(JCCallItem* __nonnull)item reason:(JCCallReason)reason description:(NSString * __nullable)description { //移除通话回调
// 界面处理
if (_localCanvas) { // 本端视频销毁
[mediaDevice stopVideo:_localCanvas];
[_localCanvas.videoView removeFromSuperview];
_localCanvas = nil;
}
if (_remoteCanvas) { // 远端视频销毁
[mediaDevice stopVideo:_remoteCanvas];
[_remoteCanvas.videoView removeFromSuperview];
_remoteCanvas = nil;
}
}
通话挂断的其他情况
如果拨打通话时,对方未在线,或者主叫呼叫后立即挂断,则对方再次上线时会收到未接来电的回调
/**
* @brief上报服务器拉取的未接来电
* @param item JCCallItem 对象
*/
-(void)onMissedCallItem:(JCCallItem * __nonnull)item;
此时通话状态变为 JCCallStateMissed。