中文简体 English
iOS App SDK 快速集成
本章节主要介绍如何快速将SDK集成到工程中,编译通过,快速开发使用。
准备工作
Xcode
iOS App SDK文件
iOS App SDK文件包含的文件
IvyConstants.h
IvyIoDef.h
IvyCamera.h
IvyObject.h
IvyPlayer.h
IvySDPlayer.h
IvySDDownloader.h
IvyVoicePlayer.h
IvySdkManager.h
libIvySdk.a
集成iOS App SDK
创建项目
工程配置
将SDK添加到工程
添加依赖库
Link Binary With Libraries,新增libc++.1.tbd库
设置Bitcode
设置Bitcode为NO
初始化SDK
引入头文件初始化SDK
适配iOS 版本
从 iOS 14 开始,在设备配网、局域网本地控制时会触发「本地网络」权限弹窗,在用户点击允许后,app才能够向本地局域网发送数据。如果用户点击了拒绝,将无法使用相关功能。目前苹果没有提供任何 API 对此权限进行判断,建议开发者在相关功能无法正常使用时提示、引导用户检查「系统设置 - app设置」,确认是否开启了「本地网络」权限。
初始化
初始化SDK
通过IvySdkManager
中的initializer
方法初始化App Sdk。
[[IvySdkManager shared] initializer];
通过IvySdkManager
中的setP2PRegion
方法设置P2P服务器地区。
[[IvySdkManager shared] setP2PRegion:0];
注:使用不区分国内外UID,需设置P2P服务器地址,可根据用户所在国家进行设置,默认使用非中国区P2P服务器。
配网
二维码配网
通过IvySdkManager
中的qrCodeString:password:deviceUID:bindingToken
方法生成用于配网的二维码字符串。
NSString *qrCodeString = [[IvySdkManager shared] qrCodeString:SSID password:password deviceUID:deviceUID bindingToken:bindingToken];
用该字符串生成二维码图片。将该二维码对准处于配网状态的IPC设备,设备成功识别到二维码信息后会自动配网。
注:可通过局域网搜索功能,定时检测判断设备是否成功配网。
声波配网
通过IvyVoicePlayer
类,进行声波配网。
_voicePlayer = [[IvyVoicePlayer alloc] initWithSSID:SSID password:password deviceUID:deviceUID];
_voicePlayer.delegate = self;
[_voicePlayer play]; // 开始播放声波
用voicePlayer对象播放声波。处于配网状态的IPC设备成功后会自动配网。
可通过代理IvyVoicePlayerDelegate
获取声波配网的播放结果。离开配网页面,需要停止调用stop
方法停止播放。
注:可通过局域网搜索功能,定时检测判断设备是否成功配网。
局域网搜索
通过IvySdkManager
中的searchDevices:
方法搜索该局域网内的设备。
[[IvySdkManager shared] searchDevices:^(NSArray<IvyDevLan *> * _Nonnull devices) {
}];
可通过IvyDevLan
中的uid
属性,与配网设备的deviceUID
对比。
创建IvyCamera对象
NSString *deviceUID = kDeviceUID;
NSString *username = kUsername;
NSString *password = kPassword;
IvyCamera *obj = [[IvyCamera alloc] initWithDeviceUID:deviceUID username:username password:password];
deviceUID:设备的UID,通常是一个24位的字符串,如: ZSZ77RNGE3821F2WZZZZBY2Z
username: 用户名称,出厂用户名默认是admin
password:用户密码,出厂用户密码需要调用下面的方法获取
[[IvySdkManager shared] resetPasswordByDeviceUID:deviceUID];
注:一个设备只创建一个IvyCamera
对象,IvyCamera
对象建议作为全局对象保存。如果是非全局对象,当对象释放时连接也会自动释放。
登录设备
IvyCamera
对象创建后,即可调用loginCamera
方法登录设备
[obj loginCamera:^(IVYIO_HANDLE_STATE handleState, IVYIO_RESULT cmdResult) {
NSLog(@"... loginCamera handleState:%@ cmdResult:%@", @(handleState), @(cmdResult));
}];
注:loginCamera:
方法只需通过handleState结果,判断设备当前状态。
处理登录结果
1、handleState为IVYIO_HANDLE_STATE_ONLINE
,表明设备已登录成功,可以进行后续操作。
2、handleState为IVYIO_HANDLE_STATE_ON_RESET
,表明设备处于出厂状态,需强制修改设备用户名、密码才可,进行后续操作。
[obj modifyUserNameAndPassword:username newPassword:password onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {
if (cmdResult == IVYIO_RESULT_OK) {
NSLog(@"... success");
} else {
NSLog(@"... failed");
}
}];
3、handleState为IVYIO_HANDLE_STATE_USR_OR_PWD_ERR
,表明创建IvyCamera对象时使用的用户名或者密码错误。使用正确的用户名、密码,调用以下方法更新设备信息,[obj updateUsername:username password:password];
。成功后再次登录设备,并处理登录结果。
4、handleState为IVYIO_HANDLE_STATE_MAX_USERS
,表明设备超过最大用户数。
5、handleState为IVYIO_HANDLE_STATE_LOCKED
,表明因为登录设备时用户名、密码短时间内多次错误,设备自动锁住,需等待数分钟后,重新尝试登录设备。
获取设备信息
登录设备成功后,可获取该设备的相关信息。
获取设备信息
IvyDevInfo
对象包含设备的固件版本等信息。
[self.ivyCamera getDevInfo:^(IvyDevInfo * _Nonnull devInfo, IVYIO_RESULT cmdResult) {
if (cmdResult == IVYIO_RESULT_OK) {
} else {
}
}];
获取设备能力集
通过IvyDevAbility
对象,可判断设备支持的功能。
[self.ivyCamera getDevAbility:^(IvyDevAbility * _Nonnull devAbility, IVYIO_RESULT cmdResult) {
if (cmdResult == IVYIO_RESULT_OK) {
} else {
}
}];
直播
创建IvyPlayer对象后,需要实现对象的IvyPlayerDelegate代理方法,接受直播获取到的实时数据。根据播放时解码数据的类型decodeType,实现对应的回调方法。
开始播放
根据代理回掉,展示直播画面。
[self.ivyPlayer playLive:self.ivyCamera decodeType:IvyVideoDecodeUIImage];
decodeType:视频数据格式类型。
decodeType选择IvyVideoDecodeUIImage,需实现以下代理方法
- (void)ivyPlayer:(IvyPlayer *)ivyPlayer didReciveFrame:(UIImage *)image isFirstFrame:(BOOL)isFirstFrame;
否则需实现以下代理方法
- (void)ivyPlayer:(IvyPlayer *)ivyPlayer didReciveIVYFrame:(IVYIO_FRAME *)frame isFirstFrame:(BOOL)isFirstFrame;
注:decodeType一般情况下选择IvyVideoDecodeUIImage。
停止播放
[self.ivyPlayer stop];
注:一般在- (void)viewWillDisappear:(BOOL)animated中调用。
监听
通过validAudio属性设置。
@property (nonatomic, assign) BOOL validAudio;
可通过回掉ivyPlayer:playerCommand:result
判断IvyPlayerOpenAudio
命令的结果。
注: 建议在playLive:decodeType:后设置监听状态。
对讲
打开对讲
- (void)startTalk;
关闭对讲
- (void)endTalk;
可通过回掉ivyPlayer:playerCommand:result
判断IvyPlayerOpenTalk
、IvyPlayerCloseTalk
命令的结果。
抓拍
直接保存当前直播画面内容。
录像
调用以下方法,可录制当前直播视频。
- (void)startRecord:(NSString *)filepath onCompletion:(IvyCameraResultBlock)resultBlock;
注:监听关闭的情况下,录像是没有声音的。
夜视模式切换
- (void)setDayNightConfig:(BOOL)onoff mode:(NSInteger)mode onCompletion:(IvyCameraResultBlock)resultBlock;
mode:红外灯模式 (0:自动 1:手动 2:计划)
onoff: 红外灯开关 (仅在手动模式下生效)
手动模式下,打开夜视红外灯
[self.ivyCamera setDayNightConfig:YES mode:2 onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {
if (cmdResult == IVYIO_RESULT_OK) {
} else {
}
}];
清晰度切换
获取设备所支持的清晰度
[self.ivyCamera getSupportedStreamTypes:^(NSArray * _Nonnull types, IVYIO_RESULT cmdResult) {
}];
types: 参考
IvyDefinitionType
定义
获取当前清晰度
[self.ivyCamera getStreamType:^(IvyDefinitionType streamType, IVYIO_RESULT cmdResult) {
}];
设置清晰度
IvyDefinitionType type = IvyDefinitionHD;
[self.ivyCamera setStreamType:type onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {
if (cmdResult == IVYIO_RESULT_OK) {
} else {
}
}];
PTZ
向上转动
IVY_PTZ_CMD ptzCmd = IVY_PTZ_MOVE_UP;
[self.ivyCamera setPTZCmd:ptzCmd onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {}];
变焦
IVY_PTZ_CMD ptzCmd = IVY_PTZ_ZOOM_IN;
[self.ivyCamera setPTZCmd:ptzCmd onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {}];
对焦
IVY_PTZ_CMD ptzCmd = IVY_PTZ_FOCUS_NEAR;
[self.ivyCamera setPTZCmd:ptzCmd onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {}];
注: 操作PTZ后,需调用IVY_PTZ_STOP
停止。
直播代码示例
//
// ViewController.m
// Demo
//
// Created by JackChan on 10/5/2021.
// Copyright © 2021 JackChan. All rights reserved.
//
#import "ViewController.h"
#import "IvyCamera.h"
#import "IvyPlayer.h"
#import "IvySdkManager.h"
#define kDeviceUID @"DN5K12U4AB3BD3821111ABZZZ"
#define kUsername @"a"
#define kPassword @"abc123"
@interface ViewController () <IvyPlayerDelegate>
@property (nonatomic, strong) IvyCamera *ivyCamera;
@property (nonatomic, strong) IvyPlayer *ivyPlayer;
@property (nonatomic, strong) UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSString *deviceUID = kDeviceUID;
NSString *username = @"admin";
NSString *password = [[IvySdkManager shared] resetPasswordByDeviceUID:deviceUID];
_ivyCamera = [[IvyCamera alloc] initWithDeviceUID:deviceUID username:username password:password];
[self.view addSubview:self.imageView];
self.imageView.frame = CGRectMake(0, CGRectGetHeight(self.view.frame) * 0.3, CGRectGetWidth(self.view.frame), CGRectGetWidth(self.view.frame) * 9 / 16.f);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loginCamera];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.ivyPlayer stop];
}
#pragma mark - Private Methods
- (void)loginCamera {
__weak typeof(self) weakSelf = self;
[self.ivyCamera loginCamera:^(IVYIO_HANDLE_STATE handleState, IVYIO_RESULT cmdResult) {
NSLog(@"... loginCamera End handleState:%@ cmdResult:%@", @(handleState), @(cmdResult));
switch (handleState) {
case IVYIO_HANDLE_STATE_ONLINE: {
[weakSelf playLive];
}
break;
case IVYIO_HANDLE_STATE_ON_RESET: {
[weakSelf modifyAccount];
}
break;
default:
break;
}
}];
}
- (void)playLive {
[self.ivyPlayer playLive:self.ivyCamera decodeType:IvyVideoDecodeUIImage];
}
- (void)modifyAccount {
__weak typeof(self) weakSelf = self;
[self.ivyCamera modifyUserNameAndPassword:kUsername newPassword:kPassword onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {
if (IVYIO_RESULT_OK == cmdResult) {
[weakSelf loginCamera];
}
}];
}
#pragma mark - IvyPlayerDelegate
- (void)ivyPlayer:(IvyPlayer *)ivyPlayer didReciveFrame:(UIImage *)image isFirstFrame:(BOOL)isFirstFrame {
self.imageView.image = image;
if (isFirstFrame) {
}
}
- (void)ivyPlayer:(IvyPlayer *)ivyPlayer playerCommand:(IvyPlayerCommand)command result:(IVYIO_RESULT)result {
NSLog(@"... command:%@ result:%@", @(command), @(result));
}
#pragma mark - Getter && Setter
- (IvyPlayer *)ivyPlayer {
if (!_ivyPlayer) {
_ivyPlayer = [IvyPlayer new];
_ivyPlayer.delegate = self;
}
return _ivyPlayer;
}
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [UIImageView new];
_imageView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1];
}
return _imageView;
}
@end
直播示例效果
SD卡
搜索SD卡录像列表
搜索SD卡录像列表方法
- (void)getSDCardRecordList:(NSUInteger)startTime endTime:(NSUInteger)endTime recordType:(NSInteger)recordType onCompletion:(IvyCameraResultBlock)resultBlock;
startTime: 开始时间
endTime: 结束时间
recordType: 录像类型
搜索当日所以SD卡录像例子:
NSInteger year, month, day;
NSInteger recordType = 2;
{
NSDate *date = [NSDate date];
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
calendar.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDateComponents *components = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date];
year = components.year;
month = components.month;
day = components.day;
}
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = year;
components.month = month;
components.day = day;
components.minute = 0;
components.second = 0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [calendar dateFromComponents:components];
NSUInteger st = (unsigned int)[date timeIntervalSince1970];
NSUInteger et = st + 24 * 3600 - 1;
[self.ivyCamera getSDCardRecordList:st endTime:et recordType:recordType onCompletion:^(id _Nullable obj, IVYIO_RESULT cmdResult) {
if (IVYIO_RESULT_OK == cmdResult) {
NSArray *records = obj;
} else {
}
}];
注:也可通过分页的方式加载SD卡录像列表,参见以下方法。
- (void)getSDCardRecordList:(NSUInteger)startTime endTime:(NSUInteger)endTime recordType:(NSInteger)recordType startNo:(NSInteger)startNo onCompletion:(void(^)(IvyRecordList *recordList, IVYIO_RESULT cmdResult))resultBlock;
播放SD卡录像
创建SD卡播放器
@property (nonatomic, strong) IvySDPlayer *player;
- (IvySDPlayer *)player {
if (!_player) {
_player = [IvySDPlayer new];
_player.delegate = self;
_player.validAudio = YES;
}
return _player;
}
实现SD卡播放器代理IvySDPlayerDelegate
#pragma mark - IvySDPlayerDelegate
- (void)ivySDPlayer:(IvySDPlayer *)player didReciveFrame:(UIImage *)image isFirstFrame:(BOOL)isFirstFrame {
self.imageView.image = image;
if (isFirstFrame) {
}
}
- (void)ivySDPlayer:(IvySDPlayer *)player playerCommand:(IvyPlayerCommand)command result:(IVYIO_RESULT)result {
NSLog(@"... command:%@ result:%@", @(command), @(result));
_command = command; // 保存状态
switch (command) {
case IvyPlayerOpenVideo: {
}
break;
case IvyPlayerFinished:
case IvyPlayerStopped: {
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = nil;
});
if (_nextObject) {
[self playBack:_nextObject];
_nextObject = nil;
}
}
break;
default:
break;
}
}
传入录像列表中的对象播放
id<IvyRecordObject> obj = self.records.firstObject;
[self.player playBack:self.ivyCamera recordObject:obj decodeType:IvyVideoDecodeUIImage];
获取SD卡信息
可获取是否存在SD卡,SD卡使用容量等信息。
[self.ivyCamera getSDCardInfo:^(IvySDInfo * _Nonnull ivySDInfo, IVYIO_RESULT cmdResult) {
if (1 == ivySDInfo.isExist) {
} else {
}
}];
格式化SD卡
[self.ivyCamera setSDCardFormat:^(id _Nullable obj, IVYIO_RESULT cmdResult) {}];
注: sdFormatError非0且freeSpace、totalSpace为0,提示需要格式化。该接口超时时长为120秒