# 实现插件特性
本文展示如何实现菊风视频能力平台提供的插件功能。
# 前提条件
开始前,请确保你具备以下条件:
已下载插件包
已集成 Juphoon Plugin。目前只有该版本支持插件集成。
插件 API说明在页面底部,请查看。
# Plugin 进阶应用
# 1. 定制化配置
/**
* @brief 设置定制配置参数
* @param customConfig 定制化配置参数
*/
- (void)setCustomConfig:(T)customConfig;
设置定制化配置参数详见 JCCCommonCustomConfig (opens new window) 。
@interface JCCCommonCustomConfig : NSObject
@property (nonatomic, strong) JCCTalkingConfig *talkingConfig;
@end
# 通话页面配置
// 底部工具栏按钮枚举定义
typedef NS_ENUM(NSUInteger, JCCToolBarButtons) {
JCCEndButton = 1, // 挂断按钮
JCCSpeakerButton, // 扬声器按钮
JCCMicButton, // 麦克风按钮
JCCCameraButton, // 摄像头按钮
JCCFlipButton, // 翻转按钮
JCCScreenShareButton, // 屏幕共享按钮
JCCLocalRecordButton, // 本地录制按钮
JCCChatButton, // 消息按钮
JCCCollaborationButton, // 协作按钮
JCCRemoteRecordButton // 远程录制按钮
};
@interface JCCTalkingConfig : NSObject
/// 颜色格式为8位16进制ARGB,例如0xFFFFFFFF
/// 顶部栏背景色
@property (nonatomic, assign) NSUInteger topBarBackground;
/// 标题内容
@property (nonatomic, copy) NSString *title;
/// 工具栏背景色
@property (nonatomic, assign) NSUInteger toolBarBackground;
/// JCToolbarButtons配置数组
@property (nonatomic, strong) NSArray *toolBarButtons;
/// 自定义图层,需实现IBaseView协议,用来接收生命周期等事件,尺寸为全屏幕大小
@property (nonatomic, strong) UIView<JCCIBaseView> *customView;
/// 是否显示最小化按钮,默认为NO
@property (nonatomic, assign) BOOL showMinimized;
/// 是否显示统计按钮,默认为NO
@property (nonatomic, assign) BOOL showStatistic;
/// 是否使用第三方排队机,如果为NO,则使用插件中排队机,默认为NO
@property (nonatomic, assign) BOOL useThirdACD;
/// 是否显示电子签名栏取消按钮,默认为NO
@property (nonatomic, assign) BOOL showCancelSign;
/// 电子签名画笔宽度,默认为8
@property (nonatomic, assign) NSUInteger defaultDoodleStrokeWidth;
/// 签字签名是否全屏显示
@property (nonatomic, assign) BOOL signFullScreen;
/// 通话保持中时,需要播放的音频文件路径
@property (nonatomic, copy) NSString *heldAudioPath;
/// 转接时,需要播放的音频文件路径
@property (nonatomic, copy) NSString *transferAudioPath;
/// 是否显示共享的画面,默认为YES
@property (nonatomic, assign) BOOL showShareCanvas;
/// 协作共享远端路径颜色
@property (nonatomic, assign) NSUInteger collaborationRemotePathColor;
/// 协作共享自己端路径颜色
@property (nonatomic, assign) NSUInteger collaborationSelfPathColor;
/// 通话中关闭摄像头视频背景颜色,如果不使用背景颜色则设置为-1,默认设置为-1
@property (nonatomic, assign) NSInteger videoHoldBackgroundColor;
/// 通话中关闭摄像头小视频图标
@property (nonatomic, strong) UIImage *videoHoldSmallIcon;
/// 通话中关闭摄像头大视频图标
@property (nonatomic, strong) UIImage *videoHoldLargeIcon;
/// 全显示功能布局
@property (nonatomic, assign) BOOL fullDisplayFunctionLayout;
@end
// IBaseView定义
@protocol JCCIBaseView <NSObject>
- (void)viewDidLoad;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;
@end
示例代码:
JCCCommonCustomConfig *customConfig = [[JCCCommonCustomConfig alloc] init];
customConfig.talkingConfig.toolBarButtons = @[@(JCCEndButton), @(JCCSpeakerButton)];
...
[JCCRoomManager.shared setCustomConfig:customConfig];
# 2. 在通话页面中添加自定义视图
自定义图层需要实现 IBaseView 协议。
@interface TestCustomView : UIView<JCCIBaseView>
@end
@implementation TestCustomView
- (void)viewDidAppear:(BOOL)animated {
NSLog(@"viewDidAppear");
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(@"viewDidDisappear");
}
- (void)viewDidLoad {
NSLog(@"viewDidLoad");
}
- (void)viewWillAppear:(BOOL)animated {
NSLog(@"viewWillAppear");
}
- (void)viewWillDisappear:(BOOL)animated {
NSLog(@"viewWillDisappear");
}
示例代码:
///自定义图层尺寸设置为全屏大小
TestCustomView *customView = [[TestCustomView alloc] initWithFrame:bounds];
JCCManager.defaultManager.customConfig.talkingConfig.customView = customView;
# 3. 在通话过程中弹窗
可以通过 ViewController、Window 以及插件提供的自定义视图实现弹窗。
# ViewController 弹出方式
获取最顶层 ViewController ,通过顶层 ViewController present 弹窗 Controller 。
- (UIViewController *)topViewController {
UIViewController *resultVC;
resultVC = [self _topViewController:[[UIApplication sharedApplication].keyWindow rootViewController]];
while (resultVC.presentedViewController) {
resultVC = [self _topViewController:resultVC.presentedViewController];
}
return resultVC;
}
- (UIViewController *)_topViewController:(UIViewController *)vc {
if ([vc isKindOfClass:[UINavigationController class]]) {
return [self _topViewController:[(UINavigationController *)vc topViewController]];
} else if ([vc isKindOfClass:[UITabBarController class]]) {
return [self _topViewController:[(UITabBarController *)vc selectedViewController]];
} else {
return vc;
}
return nil;
}
TestViewController *controller = [TestViewController alloc] init];
[topViewController presentViewController:controller animated:YES completion:nil];
# Window 方式
创建一个 Window ,通过 Window 弹出弹窗。需要保证 Window 的生命周期。可以设置 Window 为单例或者让某个类持有 Window ,在使用结束再销毁 Window 。
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [[UIViewController alloc] init];
window.windowLevel = UIWindowLevelNormal + 1;
window.hidden = NO;
TestViewController *controller = [TestViewController alloc] init];
[window.rootViewController presentViewController:controller animated:YES completion:nil];
如果实现了 iOS 13 的 **UIWindowSceneDelegate ,**需要将 Window 添加到 windowScene 中。
if (@available(iOS 13.0, *)) {
for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
window.windowScene = windowScene;
break;
}
}
}
# 自定义视图方式
在自定义视图中添加弹窗视图,需要显示的时候弹出,其他时候隐藏。
JCCCommonCustomConfig *customConfig = [[JCCCommonCustomConfig alloc] init];
// TestCustomView 实现了IBaseView协议
TestCustomView *customView = [[TestCustomView alloc] initWithFrame:self.view.bounds];
customConfig.talkingConfig.customView = customView;
[JCCRoomManager.shared setCustomConfig:customConfig];
# 4. 开启国密
# 参数配置
开启国密需要在初始化的时候设置 JCCLoginParam (opens new window) 的 accountEntry 和 certificate 参数。
参数需要根据实际情况填写。
JCCLoginParam *config = [[JCCLoginParam alloc] init];
// 设置账户分录,测试环境accountEntry值
config.accountEntry = @"AccountEntry:sarc -h arc@AccountEntry -p 198 -S 3;";
// 设置证书路径,测试证书
NSString *s1 = [[NSBundle mainBundle] pathForResource:@"CA" ofType:@"der"];
NSData *data1 = [[NSFileManager defaultManager] contentsAtPath:s1];
NSString *ca = [data1 base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
config.certificate = ca;
config.appKey = @"appkey";
config.server = @"server"
config.userName = @"userName"
[JCCGuestManager.shared login:config];
# 加载国密库
加载 jussl.framework 库。
# 5. 开启云加速
- 从管理平台申请加速云接入,并获得加速云接入地址,接口 key 和接入 key 密钥
- 登录参数中需要将上述获得的参数填入
# 接入地址
# 直连地址
一般是接入行方系统的外部地址。
# 加速云地址
公网 router ,用于提供公网上的 App 到直连地址连通质量。
// 测试参数
ListenerAccessKey ID: XuG4vjNLLpUEuo6qbVYvHMmr
ListenerAccessKey Secret: kB2RnoXi3iaxVkoUerL7ojd88BUNa0
加速云接入地址:Router:udp -h entry.jac.juphoon.com -p 4021 -ri -1;
直连地址:
udp:122.227.209.194:10122 (外网)
udp:192.168.3.200:10030(内网)
// 双接入地址
Router:udp -h entry.jac.juphoon.com -p 4021 -ri -1;udp -h 122.227.209.194 -p 10122 -ri -2;
# 地址格式
# 单地址格式1
协议:域名/ IP :端口
示例:
udp:122.227.209.194:10122
# 单地址格式2
Router:协议 -h 域名/IP -p 端口
示例:
Router:udp -h entry.jac.juphoon.com -p 4021
# 多地址
Router:单地址格式2;单地址格式2
示例:
Router:udp -h entry.jac.juphoon.com -p 4021 -ri -1;udp -h 122.227.209.194 -p 10122 -ri -2;
# 主备地址
Router:单地址格式2;单地址格式2 -bu
示例:
Router:udp -h entry.jac.juphoon.com -p 4021 -ri -1;udp -h 122.227.209.194 -p 10122 -ri -2 -bu;
示例代码:
JCCLoginParam *config = [[JCCLoginParam alloc] init];
...
config.server = @"加速云接入地址";
config.accelerateKey = @"加速云Key";
config.accelerateKeySecret = @"加速云Key密钥";
...
BOOL ret = [JCCGuestManager.shared login:config];
# 6. 屏幕共享
屏幕共享可以让您和频道中的其他成员一起分享设备里的精彩内容,您可以在频道中利用屏幕共享的功能进行文档演示、在线教育演示、视频会议以及游戏过程分享等。 通过 ReplayKit 2 的系统库,实现屏幕共享,该库仅支持 iOS 12.0 以上的系统进行共享系统的屏幕,且支持屏幕外共享
# 添加 Broadcast Upload Extension 扩展
1、项目中选择 target ,添加 extension ,用于屏幕录制。
如上所示,在添加 extension 时选中 Broadcast Upload Extension 扩展进行添加。
2、app 和 extension 加入同一个 app group 后,实现 extension 和宿主 app 间的数据共享,加入步骤如下图所示。
3、然后打开工程目录下对应的 SampleHandler.h 和 SampleHandler.m 文件,导入库 #import <ReplayKit/ReplayKit.h>,继承系统的 RPBroadcastSampleHandler 类,重写broadcastStartedWithSetupInfo、processSampleBuffer、broadcastFinished 方法,并且调用调用插件提供的方法,如下图
# 配置屏幕共享参数
在登录的时候传入 shareGroupId 和 shareExtension 。
- (BOOL)login:(JCCLoginParam *)config {
config.shareGroupId = @"groupId";
config.shareExtension = @"shareExtension";
BOOL ret = [JCCRoomManager.shared login:config];
return ret;
}
完成以上配置后,即可使用插件屏幕共享功能。
# 7. 点击事件拦截
需要拦截通话中按钮点击事件,需要实现 JCCRoomCallback 的 onViewEvent: 回调方法
/**
* UI事件通知
* @param viewEvent 事件类型
* @return
* - true 表示该事件由app外部处理,插件内部无需处理
* - false 插件内部默认处理该事件
*/
- (BOOL)onViewEvent:(JCCViewEvent)viewEvent;
示例代码
- (BOOL)onViewEvent:(JCCViewEvent)event {
if (event == JCCViewEventTalkEndClick) { // 拦截通话挂断事件
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[JCCRoomManager.shared leave];
});
return YES;
}
return NO;
}
# 8. Token 校验
token 认证服务,主要用于登录时 token 验证,由第三方服务获取 token,将 token 下发给集成的终端,由 SDK 发起登录时带上 token ,进行认证。详见 token 流程介绍 (opens new window)。
允许用户登录时,带入 token 。如果未使用,可以不带。
/**
* @brief 初始化并登录,登录结果通过 {@link JCCCommonCallback#onLogin:reason: onLogin} 回调通知
* @param param 初始化和登录的基本配置信息
* @return 接口调用结果
* - YES 表示调用成功
* - NO 表示调用异常
*/
- (BOOL)login:(JCCLoginParam *)param;
示例代码:
JCCLoginParam *loginParam = [JCCLoginParam new];
param.token = @"token字符串";
[JCCRoomManager.shared login:param];
# 9. SDK引擎使用
跳过插件层,调用更加底层的SDK接口,实现插件外的功能。
示例代码:
// 在登录成功以后,可以获取到engine引擎
- (void)onLogin:(BOOL)result reason:(JCCReasonCode)reason {
JRTCRoomEngine *engine = JCCRoomManager.shared.engine; // 获取引擎
[engine addCallback:self]; // 添加回调
JRTCRecordRemoteParam *recordParam = [JRTCRecordRemoteParam new];
recordParam.fileName = @"xxx.mp4"; // 设置远程录制参数
……
// 开启远程录制
[engine enableRemoteRecord:YES recordParam:recordParam];
……
[engine enableRemoteRecord:NO recordParam:nil]; // 关闭远程录制
}
addCallback以后,实现JRTCKitRoomCallback协议,接收引擎的回调。
具体可参考JRTCRoomEngine.h文件。