中文简体    English


iOS App SDK 快速集成

本章节主要介绍如何快速将SDK集成到工程中,编译通过,快速开发使用。

AppSDK 点击下载

准备工作

  • 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判断IvyPlayerOpenTalkIvyPlayerCloseTalk命令的结果。

抓拍

直接保存当前直播画面内容。

录像

调用以下方法,可录制当前直播视频。

- (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秒

文档更新时间: 2023-07-20 19:23   作者:庄小婵