2. IOS 客户端开发指南

2.1. 概述

  
  本节用于指导使用 PPVIEW 客户端 SDK 开发 IOS 平台手机客户端。

  在客户端的所有流程中,与平台交互的接口,可以直接调用;与设备端交互的接口,需要先创建一个P2P连接,并将这个P2P连接句柄作为参数传递给交互接口。
  
  请注意,这个作为交互用的P2P连接可以重复使用,但是不能并行使用。即创建后,可以在一个交互流程完成后,用于另一个交互流程,但不能同时用于两个交互流程。

  我们在内部做了P2P连接优化,当创建了多个用于交互的P2P连接时,我们会在内部进行复用,不影响这些多次创建的P2P连接的并行使用。

2.2. 开发准备

2.2.1. 创建IOS项目

创建IOS项目。

2.2.2. 导入库

将开发包中 goe_lib 文件夹下的头文件和库文件复制到项目中

并确保“Build Phases/Link Binary With Libraries”中已包含了 SDK 中的库文件。

2.2.3. 导入必需的系统库

2.2.4. 在main.m中忽略管道破裂信号

int main(int argc, char * argv[])
{
    //忽略管道破裂信号
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigaction( SIGPIPE, &sa, 0);
    
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

如果不忽略,会因为 Socket 关闭而导致程序崩溃退出。

2.3. 流程指南

2.3.1. 初始化流程

  • 创建与平台交互的对象实例
#import "ppview_cli.h"

ppview_cli *cli = [ppview_cli getInstance];

一个客户端应用进程创建一个平台交互对象实例即可,此接口采用单例模式,多次调用仅创建一个实例。

  • 初始化参数
[cli setAppInfo:@"http://ppview.vveye.com:3000/webapi/client/"
     devurl:@"http://ppview.vveye.com:3000/webapi/device/" 
     eventurl:@"http://ppview.vveye.com:3000/webapi/page" 
     appid:@"C6460838-D5D0-0001-CB18-B1491FFB1DCE" 
     vendorpass:@"H10Yz6fJS5oPX1Hj" 
     with_p2p_svr:@"nat.vveye.net" 
     with_p2p_port:8000 
     with_secret:@""];

设置平台服务器地址、P2P 服务器地址及应用ID等必要的参数。

2.3.2. 用户账号注册流程

2.3.2.1. 通过手机号注册用户

手机APP->手机APP: 1. 输入手机号
手机APP->云平台: 2. 请求短信验证码
云平台-->手机APP: 3. 请求短信验证码回复
云平台->用户: 4. 短信验证码
用户->手机APP: 5. 手工输入短信验证码
手机APP->云平台: 6. 注册新用户请求
云平台-->手机APP: 7. 注册请求回复
  • 实现并设置代理函数

接口:vercode_interface

- (void)cli_lib_GetSmsVcode_CALLBACK:(int)HttpRes
{
    //TODO: 处理获取短信验证码请求的回复
}

...

cli.cli_vercode_delegate = self;

接口: register_interface

- (void)cli_lib_Register_CALLBACK:(int)HttpRes
{
    //TODO: 处理注册请求的回复
}

...

cli.cli_register_delegate = self;
  • 请求短信验证码
[cli cli_lib_GetSmsVcode:m_mobile bCheck:1];
  • 注册
[cli cli_lib_Register:m_usr regpass:m_pass vcode:m_pass mail:m_email mobile:m_mobile myses:nil with_nick:nil];

2.3.2.2. 通过邮箱注册用户

手机APP->手机APP: 1. 输入邮箱号
手机APP->云平台: 2. 请求邮箱验证码
云平台-->手机APP: 3. 请求邮箱验证码回复
云平台->用户: 4. 邮箱验证码
用户->用户: 5. 打开邮箱,读取验证码
用户->手机APP: 6. 手工输入邮箱验证码
手机APP->云平台: 7. 注册新用户请求
云平台-->手机APP: 8. 注册请求回复
  • 实现并设置代理函数

接口:vercode_interface

- (void)cli_lib_GetMailVcode_CALLBACK:(int)HttpRes
{
    //TODO: 处理获取邮箱验证码请求的回复
}

...

cli.cli_vercode_delegate = self;

接口: register_interface

- (void)cli_lib_Register_CALLBACK:(int)HttpRes
{
    //TODO: 处理注册请求的回复
}

...

cli.cli_register_delegate = self;
  • 请求邮箱验证码
[cli cli_lib_GetMailVcode:m_email language:m_lang_tag bCheck:1];
  • 注册
[cli cli_lib_Register:m_usr regpass:m_pass vcode:m_pass mail:m_email mobile:m_mobile myses:nil with_nick:nil];

2.3.3. 密码重置流程

2.3.3.1. 通过手机号重置密码

手机APP->手机APP: 1. 输入手机号
手机APP->云平台: 2. 请求短信验证码
云平台->用户: 3. 短信验证码
用户->手机APP: 4. 用户手工输入验证码
手机APP->手机APP: 5. 输入新密码
手机APP->云平台: 6. 重置密码请求
云平台-->手机APP: 7. 重置密码回复
  • 实现并设置代理函数

接口:vercode_interface

- (void)cli_lib_GetSmsVcode_CALLBACK:(int)HttpRes
{
    //TODO: 处理获取短信验证码请求的回复
}

...

cli.cli_vercode_delegate = self;

接口:set_interface

- (void)cli_lib_ResetPassword_sms_CALLBACK:(int)HttpRes with_vercode:(NSString *)vercode
{
    //TODO: 处理重置密码请求的回复
}

...

cli.cli_register_delegate = self;
  • 请求短信验证码
[cli cli_lib_GetSmsVcode:m_mobile bCheck:0];
  • 验证短信验证码
[cli cli_lib_CheckSmsVcode:m_vercode mobile:m_mobile];

2.3.3.2. 通过邮箱号重置密码

手机APP->手机APP: 1. 输入邮箱号
手机APP->云平台: 2. 通过邮箱重置密码
云平台-->手机APP: 3. 回复结果
云平台->用户邮箱: 4. 发送重置邮件
用户->用户邮箱: 5. 打开邮件,点击链接
用户邮箱->云平台: 6. 跳转至平台密码重置页
云平台->云平台: 7. 重置密码

注:手机端请求平台向用户邮箱发送密码重置邮件后,流程结束,用户须登录自己的邮箱,点开邮箱中的链接进行重置。

  • 实现并设置代理函数

接口:set_interface

-(void)cli_lib_ResetPassword_mail_CALLBACK:(int)HttpRes with_mail:(NSString*)mail
{
    //TODO: 处理通过邮箱重置密码请求的回复
}

...

cli.cli_set_delegate = self;
  • 通过邮箱重置密码
int res = [cli_lib_user_resetpass_email_v2:email language:language];

2.3.3.3. 修改密码

在密码已知的情况下,可直接修改密码,不需要使用密码重置流程。
用手机号和邮箱号新注册的用户,其默认密码为手机验证码或邮箱验证码。可以在注册的最后一步,通过修改密码接口,让用户设置一个自己容易记住的密码。

  • 修改密码
int res = [cli_lib_ModifyPassword_v2:usr oldpass:oldpass new_pass:newpass];

2.3.4. 用户登录

手机APP->手机APP: 1. 输入账号及密码
手机APP->云平台: 2. 登录
云平台-->手机APP: 3. 回复结果

用户登录只需要被执行一次。

  • 实现并设置代理函数

接口:login_interface

- (void)cli_lib_login_callback:(int)HttpRes
{
    //TODO: 处理登录请求的回复
}

...

cli.cli_login_delegate = self;
  • 登录
[cli cli_lib_login:m_login_usr pass:m_login_pass];

2.3.5. 修改用户昵称流程

手机APP->手机APP: 1. 输入新昵称
手机APP->云平台: 2. 请求修改
云平台-->手机APP: 3. 回复结果

以上步骤请在登录后进行。

  • 实现并设置代理函数

接口:set_interface

-(void)cli_lib_ModifyNick_CALLBACK:(int)HttpRes newnick:(NSString*)new_nick
{
    //TODO: 处理修改结果
}

...

cli.cli_set_delegate = self;
  • 请求修改昵称
int res = [cli cli_lib_ModifyNick:m_new_nick];

2.3.6. 获取设备列表流程

由于单个设备可能会有多个通道(摄像头),因此这个接口通常不需要调用,呈现给用户的列表通常是摄像头列表。如果需要在界面上显示设备列表,再分级显示设备下的通道的话,需要获取设备列表和摄像头列表,并自己进行组合。

手机APP->手机APP: 1. 登录(略,参见2.3.4.)
手机APP->云平台: 2. 请求获取设备列表
云平台-->手机APP: 3. 回复结果

以上步骤请在登录后进行。

  • 实现并设置代理函数

接口:devlist_interface

- (void)cli_lib_GetDevList_CALLBACK:(int)HttpRes data:(NSData *)data
{
    //TODO: 处理获取设备列表请求的回复
}

...

cli.cli_devlist_delegate = self;
  • 取设备列表
[cli cli_lib_GetDevList];

2.3.7. 获取摄像头列表流程

手机APP->手机APP: 1. 登录(略,参见2.3.4.)
手机APP->云平台: 2. 请求获取摄像头列表
云平台-->手机APP: 3. 回复结果
手机APP->云平台: 4. 获取摄像头缩略图
云平台-->手机APP: 5. 回复
手机APP->手机APP: 6. 打开P2P预连接

以上步骤请在登录后进行。

  • 实现并设置代理函数

接口:cameralist_interface

- (void)cli_lib_GetCamlist_CALLBACK:(int)HttpRes data:(NSData *)data
{
    //TODO: 处理获取摄像头列表请求的回复
}

...

cli.cli_camlist_delegate = self;
  • 取摄像头列表
[cli cli_lib_getcamlist_ex];
  • 获取摄像头缩略图
//返回jpeg数据
NSData *jpeg = [cli cli_lib_GetCamThumbnail:camid];
  • 开启P2P预连接
int res = [cli cli_lib_set_preconnects: devs_array];

注:当摄像头列表发生变化时(通常发生在同一个账号多个手机登录,并且添加或删除摄像头时),会收到摄像头列表变化推送,此时需要更新一下摄像头列表。

2.3.8. 局域网内搜索设备

手机APP->局域网: 1. 发送搜索请求
局域网->设备: 2. 搜索请求
设备-->手机APP: 3. 回复自己的信息
手机APP->手机APP: 4. 收集多个设备信息,通过代理返回给上层
  • 实现并设置代理函数

接口:add_dev_interface

- (void)cli_lib_vv_search_callback:(NSData *)searchdata
{
    //TODO: 处理搜索到的设备列表
}

...

cli.cli_devadd_delegate = self;
  • 开始搜索
[cli cli_lib_start_search];

注:由于广播包在局域网内十分容易丢包,因此建议重复搜索三次,将三次的结果合并在一起。否则可能会存在部分设备没有被搜到。

2.3.9. 一键WIFI配置

手机APP->手机APP: 1. 获取当前手机使用的SSID
手机APP->手机APP: 2. 用户输入WIFI密码及设备序列号
手机APP->手机APP: 3. 开启一键WIFI配置
手机APP->无线网络: 4. 发送无线广播,开启定时器
无线网络-->设备: 5. 广播消息被设备截获
设备->设备: 6. 配置无线网络参数
手机APP->手机APP: 7. 停止配置
手机APP->设备: 8. 定时尝试建立点对点连接
设备-->手机APP: 9. 连接结果
  • 实现并设置代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK:(int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;
  • 开启配置
[cli cli_lib_wifi_start_config:str_ssid pass:str_ssidpass];
  • 停止配置
[cli cli_lib_wifi_stop_config];
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:str_devid devuser:@"admin" devpass:str_devpass tag:nil];

注:流程结束前,请不要将h_connector用于其它接口。

建议:总的等待时间为120秒,等待第一个10秒后,开始尝试P2P连接,以后每隔5秒开启、关闭WIFI配置,一直循环直到收到P2P连接成功代理函数或总等待时间超时。

  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

2.3.10. 添加和删除设备

可以将设备添加到用户账号下,我们称之为绑定,绑定后,设备将出现在用户的设备列表中。

有三种流程来添加用户,开发APP时可以按需实现其中的一种或多种。它们分别是:

  • 已接入互联网的设备,通过设备序列号和密码进行添加
  • 未接入互联网的设备,通过一键WIFI配置无线网络并添加
  • 未接入互联网的设备,通过二维码识别方式配置无线网络并添加

2.3.10.1. 通过设备序列号及密码添加

在APP开发中,设备序列号可以通过搜索局域网中的设备来获取,也可以扫描机身二维码来获取(APP自行开发,SDK不提供),或是直接由用户手工输入。

获取到设备序列号后,使用以下流程:

手机APP->手机APP: 1. 输入设备序列号及密码
手机APP->设备: 2. 尝试建立P2P连接
设备-->手机APP: 3. 连接结果(可重复1~2步共尝试3次)
手机APP->云平台: 4. 添加(绑定)设备
云平台-->手机APP: 5. 添加设备请求的回复
手机APP->设备: 6. 中断P2P连接

以上步骤请在登录后进行。

  • 实现并设置代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK:(int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;

接口:add_dev_interface

- (void)cli_lib_binduser_callback:(int)result my_sess:(NSString *)bindsess
{
    //TODO: 处理添加(绑定)设备结果
}

...

cli.cli_devadd_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];

注:流程结束前,请不要将h_connector用于其它接口。

  • 添加设备
[cli cli_lib_bind_lang:h_connector devname:m_devname devusr:m_devusr devpass:m_devpass bind_sess:m_bind_sess lang:lang];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

2.3.10.2. 一键WIFI配置添加设备

手机APP->手机APP: 1. 一键WIFI配置(参见2.3.9.步骤1~9)
手机APP->云平台: 2. 添加(绑定)设备
云平台-->手机APP: 3. 添加设备请求的回复
手机APP->设备: 4. 中断P2P连接

以上步骤请在登录后进行。

  • 实现并设置代理函数

接口:add_dev_interface

- (void)cli_lib_binduser_callback:(int)result my_sess:(NSString *)bindsess
{
    //TODO: 处理添加(绑定)设备结果
}

...

cli.cli_devadd_delegate = self;
  • 添加设备
[cli cli_lib_bind_lang:h_connector devname:m_devname devusr:m_devusr devpass:m_devpass bind_sess:m_bind_sess lang:lang];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

此处断开的P2P连接是在一键WIFI流程中创建的。

2.3.10.3. 二维码识别添加设备

手机APP->手机APP: 1. 获取当前手机使用的SSID
手机APP->手机APP: 2. 输入WIFI密码
手机APP->手机APP: 3. 生成二维码字符串
手机APP->手机APP: 4. 显示二维码
手机APP->设备: 5. 将二维码对准设备摄像头
设备->设备: 6. 识别并配置WIFI
设备->云平台: 7. 添加设备到用户
云平台->手机APP: 8. 推送添加成功消息

以上步骤请在登录后进行。

如果设备正确识别了二维码并添加到用户账号下,平台会推送一个消息给客户端,通过cli_lib_qrcode_bind_callback回调给客户端。如果没有正确识别,平台是无法反馈的。

  • 实现并设置代理函数

接口:vvpush_msg_interface_ex

- (void)cli_lib_qrcode_bind_callback:(int)res devid:(NSString *)devid dev_ower:(NSString *)dev_ower
{
    //TODO: 处理添加(绑定)设备结果
}

...

cli.cli_c2s_push_msg_ex_delegate = self;
  • 生成二维码字符串
NSString *qrcode = [cli cli_lib_getwifi_qrcode:str_ssid ssidpass:str_ssidpass lang:@"zh"];

2.3.10.4. 删除设备

删除自己添加的设备,和别人分享给自己的设备,需要使用不同的接口。

  • 实现并设置代理函数

接口:dev_delete_interface

- (void)cli_lib_deleteDev_CALLBACK:(int)HttpRes devid:(NSString *)dev_id
{
    //TODO: 处理删除设备结果
}

...

cli.cli_devdelete_delegate = self;

接口:cameralist_interface

- (void)cli_lib_DeleteShareCam_CALLBACK:(int)HttpRes camid:(NSString *)cam_id devid:(NSString *)devid my_sn:(int)sn
{
    //TODO: 处理删除别人分享给自己的摄像头结果
}

...

cli.cli_camlist_delegate = self;
  • 删除自己添加的设备:
[cli cli_lib_DeleteDevice:devid];
  • 删除别人分享给自己的摄像头:
[cli cli_lib_delete_shared_camera:devid camid:camid sn:cursn]; 

注:某些设备可能含有多个通道(即多个摄像头,如DVR),在调用cli_lib_DeleteDevice时将删除所有的通道。而分享功能是可以细化到摄像头的,因此调用cli_lib_delete_shared_camera时,删除的仅仅是指定的摄像头。

2.3.11. 播放实时音视频流程(包括云台控制、音频开关、抓图、本地录像、码流切换)

st=>start: 开始
init=>operation: 初始化视窗
create=>operation: 创建播放实例并设置播放窗口
setcb=>operation: 实现并设置代理函数
play=>operation: 开始播放
op=>operation: 云镜控制、抓图、本地录像、音频开关
stop=>operation: 停止播放
e=>end: 结束

st->init->create->setcb->play->op->stop->e

以上步骤请在登录后进行。

  • 初始化视窗
m_playview = [[VideoWnd alloc]initWithFrame:CGRectMake(0, 0, 320, 240)];//VideoWnd是一个自定义的OpenGL View,请参考SDK中自带的DEMO

......

  • 创建并初始化播放器实例
m_player = [[vv_real_player alloc]init:0 fisheyetype:0 left:0 right:0 top:0 bottom:0];

注:鱼眼摄像头的相关参数可以从摄像头列表中的fisheye_params中获得。

建议:如果是鱼眼摄像头,请只使用主码流(即分辨率最高的码流),不允许切换码流。因为如果是子码流的鱼眼图像,经变形放大后,会很不清晰。如果一定要切换,请在stop_play后,调用set_fisheye_param函数重设边距参数,再开始start_play

  • 实现并设置代理函数

接口:vv_real_play_interface

-(void)on_play_audio_cap_callback:(int)exist play_id:(int)index
{
    //TODO: 处理当前播放的码流是否存在音频
}

-(void)on_play_status_callback:(int)status play_id:(int)index tag:(NSString*)tag
{
    //TODO: 处理播放状态变化
}

-(void)on_snap_jpg_callback:(int)res jpgdata:(NSData*)data
{
    //TODO:处理抓图
}
-(void)on_resolution_changed_callback:(int)width height:(int)height index:(int)index
{
    //TODO: 处理播放过程中,码流的分辨率发生变化
}

...

m_player.delegate = self;

注:播放过程中,如果分辨率发生变化,SDK内部会自动处理变化并继续播放,但由于分辨率分生变化时,画面的长宽比也会发生变化,因此开发者需要在变化时,调整surface_view的大小以符合长宽比。

  • 设置播放窗口
[m_player set_surfaceview:m_playview];
  • 开始播放
[m_player start_play:chl_id stream_id:stream_id devid:m_devid user:m_devusr pass:m_devpass;

注:这里的devid指设备序列号

  • 云镜控制
[m_player ptz_ctrl:cmd with_speed:50 with_times:0];
  • 抓图
[m_player snap_picture];
  • 开启和关闭本地录像
[m_player start_record:filename_video thumbil_filename:filename_pic];
[m_player stop_record];
  • 开关音频
[m_player set_audio_status:status];

注:当多路播放时,切换当前播放的视频时,需要先调用set_audio_status接口关掉前一路的音频,再调用set_audio_status接口开启后一路的音频,否则,多路的音频会同时播放。

  • 获取音频开关状态
int status = [m_player get_audio_status];
  • 停止播放r
[m_player stop_play];

2.3.12. 音频对讲流程

音频对讲流程与音视频实时播放流程相互独立,因此,可以在视频播放的过程中开启音频对讲,从而实现音视频对讲功能,也可以独立开启对讲流程进行纯音频对讲。

s=>start: 开始
e=>end: 结束
create=>operation: 创建对讲实例
setcb=>operation: 实现并设置代理函数
conn=>operation: 创建P2P连接
conncb=>operation: P2P连接结果
cond=>condition: P2P连接成功?
open=>operation: 开启对讲
sw=>operation: 对讲方向切换(可选)
close=>operation: 关闭对讲
close_conn=>operation: 释放连接
cond2=>condition: 开启对讲成功?

s->create->setcb->conn->conncb->cond
cond(yes)->open->cond2
cond(no)->close_conn
cond2(yes)->sw->close
cond2(no)->close
close->close_conn
close_conn->e

以上步骤请在登录后进行。

  • 创建对讲实例
m_talk = [CTalk getInstance];
  • 实现并设置代理函数

接口:CTalk_interface

- (void)on_voicetalk_status_callback: (int)status
{
    //TODO: 处理对讲流程中状态变化
}
...

m_talk.delgate = self;

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK: (int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];

注:流程结束前,请不要将h_connector用于其它接口。

  • 开启对讲
int res = [m_talk vv_voicetalk_start:h_connector chlid:chl_id user:str_user pass:str_pass talk_type:talk_type];
  • 切换音频方向(只针对半双工模式)
[m_talk voice_switch:1];//声音方向:APP->设备
[m_talk voice_switch:0];//声音方向:设备->APP
  • 关闭对讲
[m_talk vv_voicetalk_stop];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

2.3.13. 录像回放流程(包括录像查询、远程回放、本地录像回放)

2.3.13.1. 录像查询

s=>start: 开始
e=>end: 结束
create=>operation: 创建客户端实例
setcb=>operation: 实现并设置查询结果代理函数
create_conn=>operation: 创建P2P连接
conncb=>operation: P2P连接结果代理
cond=>condition: P2P连接成功?
query_date=>operation: 查询某一月内哪些天有录像
query_min=>operation: 查询某一天内的录像列表
handle=>operation: 处理查询结果
close_conn=>operation: 释放连接
cond2=>condition: 是否继续查询

s->create->setcb->create_conn->conncb(right)->cond
cond(yes)->query_date->query_min->handle->cond2
cond(no)->close_conn->e

cond2(yes)->query_date
cond2(no)->close_conn->e

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置客户端实例代理代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK: (int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;

接口:c2d_cam_query_record_interface

-(void)cli_lib_cam_record_date_callback:(int)res days:(NSString*)days with_date:(NSString*)str_date
{
    //TODO: 处理查询得到的某一月内哪些天有录像
}

-(void)cli_lib_cam_record_min_callback:(int)res timelists:(NSMutableArray*)lists
{
    //TODO: 处理查询得到的某一天内哪些分钟有录像
}

...

cli.cli_c2d_query_record_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];

注:流程结束前,请不要将h_connector用于其它接口。

  • 查询某一月内哪些天有录像
int res = [cli cli_lib_record_get_date_list:hconnector with_chlid:chlid with_rec_type:rec_type with_date:@"201609"];
  • 查询某一日内哪些分钟有录像
int res = [cli cli_lib_record_get_min_list:h_connector with_chlid:n_chl with_rec_type:rec_type with_date:@"20160901"];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

2.3.13.2. 设备端录像远程回放

s=>start: 开始
e=>end: 结束
create=>operation: 创建回放实例
setcb=>operation: 实现并设置回放代理函数
init=>operation: 设置播放窗口
start=>operation: 开始回放
ctrl=>operation: 回放控制
stop=>operation: 停止回放
cond=>condition: 开始回放成功?
cond2=>condition: 是否继续回放?

s->create->setcb->init->start(right)->cond
cond(yes)->ctrl->stop
cond(no)->stop
stop->cond2
cond2(yes, right)->start
cond2(no)->e

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 创建回放实例
m_playbacker = [[vv_playback_player alloc]init:0 fisheyetype:fisheye_type left:left right:right top:top bottom:bottom];
  • 实现并设置回放实例的代理函数

接口:vv_playback_player_interface

- (void)on_play_audio_cap_callback:(int)exist play_id:(int)index
{
    //TODO: 处理回放中是否存在音频
}

- (void)on_play_status_callback:(int)status progress:(int)progress play_id:(int)index
{
    //TODO: 处理回放过程中播放状态变化 
}

- (void)on_resolution_changed_callback:(int)width height:(int)height index:(int)index
{
    //TODO: 处理回放过程中视频分辨率变化
}

- (void)on_snap_jpg_callback:(int)res jpgdata:(NSData *)data
{
    //TODO: 处理回放过程中抓图结果
}

...

m_playbacker.delegate = self;
  • 设置回放窗口
[m_playbacker set_surfaceview:view_gl];
  • 开始回放
[m_playbacker start_play:n_chl devid:str_devid user:str_devuser pass:str_devpass start_time:m_play_date_str];
  • 音频开关
[m_playbacker set_audio_status: on_or_off];
  • 获取音频开关状态
int status = [m_playbacker get_audio_status];
  • 停止回放
[m_playbacker stop_play];

2.3.13.3. 手机端录像(本地录像)回放

在实时音视频播放时,可以将播放的码流保存在手机上,我们称之为手机端录像或本地录像。保存在手机上的录像采用私有格式,需要通过SDK提供的接口进行回放。

回放手机端录像,不需要登录平台。

s=>start: 开始
e=>end: 结束
init=>operation: 创建并初始化播放器
set_view=>operation: 设置回放窗口
start_play=>operation: 开始回放
get_time=>operation: 取录像回放的当前时刻,并同步播放界面进度条
get_params=>operation: 各种取参数接口(可选)
set_pause=>operation: 暂停/恢复(可选)
set_seek=>operation: 重定位回放位置(可选)
stop_play=>operation: 停止回放

s->init->set_view->start_play->get_time->get_params->set_pause->set_seek->stop_play->e
  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 创建本地回放实例
m_player = [[vv_local_player alloc]init:0 fisheyetype:fisheye_type left:left right:right top:top bottom:bottom];
  • 实现并设置本地回放实例的代理函数

接口:vv_local_player_interface

- (void)on_play_audio_cap_callback:(int)exist play_id:(int)index
{
    //TODO: 处理本地回放中是否存在音频
}

- (void)on_play_status_callback:(int)status play_id:(int)index total_sec:(long)total_sec
{
    //TODO: 处理本地回放过程中播放状态变化 
}

- (void)on_resolution_changed_callback:(int)width height:(int)height index:(int)index
{
    //TODO: 处理本地回放过程中视频分辨率变化
}
  • 设置本地回放窗口
[m_player set_surfaceview:view_gl];

view必须是opengl类型。

  • 开始回放
[m_player start_play:filename];
  • 暂停/恢复
[m_player true];//暂停
[m_player false];//恢复
  • 获取本地回放实例索引号
int index = [m_player get_index];

本地回放实例索引号是在init时传入的,APP中存在多个本地回放实例时,用索引号来标识。

  • 音频开关
[m_player set_audio_status: on_or_off];
  • 获取音频开关状态
int status = [m_player get_audio_status];
  • 取本地回放的视频的宽高值
int width = [m_player get_video_width];
int height = [m_player get_video_height];
  • 取当前回放位置的秒数
int sec = [m_player get_cur_sec];

获取当前已播放的秒数(距离录像开始位置),录像文件总的秒数,在start_play后通过代理on_play_status_callback中得到。

通过录像文件总秒数和当前秒数,开发者可以在界面上动态显示本地录像回放进度。

  • 重定位回放位置
int res = [m_player set_seek:sec];

重新设置播放位置,其参数sec表示离录像开始时的秒数,结合总秒数,开发者可以实现拖动进度条定位播放位置。

  • 取当前回放位置的UTC时间
int utc = [m_player get_cur_frame_utctime];

显示和控制进度条,使用秒数就行,这个utc时间,是用来在画面上添加OSD时间字幕用的。原始画面上如果已经有OSD时间,就不需要再使用。

由于鱼眼摄像头的画面在实际播放时会进行校正,因此不适合在原始图像上添加OSD时间字幕,此时在回放本地鱼眼摄像头录像时,需要自己在画面上添加时间字幕。

注:此处得到的时间是UTC时间,如果要显示本地时间,请自行转换。

  • 停止本地回放
[m_player stop_play];

2.3.14. 布撤防流程

手机APP->手机APP: 1. 选择要布撤防的设备
手机APP->云平台: 2. 发送布撤防请求
云平台-->手机APP: 3. 回复

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置客户端实例代理函数

接口:cameralist_interface

- (void)cli_lib_SetCamAlert_CALLBACK:(int)HttpRes camid:(NSString *)cam_id status:(int)status
{
    //TODO: 处理布撤防请求回复
}

...

cli.cli_camlist_delegate = self;
  • 单摄像头布撤防
[cli cli_lib_ModifyCamAlertStatus:str_camid devid:str_devid status:on_or_off];
  • 多摄像头布撤防
[cli cli_lib_ModifyCamsAlertStatus:cams status:on_or_off];

2.3.15. 查询所有摄像头的事件列表流程

s=>start: 开始
e=>end: 结束
create=>operation: 创建客户端实例
setcb=>operation: 实现并设置事件相关代理函数
cond=>condition: 是否首次查询或重新查询
cond2=>condition: 是否继续读取下一页
query=>operation: 查询第一页,获取当前界面显示的事件缩略图
get_unread_count=>operation: 获取未读消息数,更新界面(可选步骤)
query_next=>operation: 查询下一页,获取当前界面显示的事件缩略图
get_unread_count2=>operation: 获取未读消息数,更新界面
read=>operation: 读取一条事件
get_url=>operation: 获取事件url,并显示事件图片
set_readed=>operation: 将所有事件置为已读(可选)
s->create->setcb->cond
cond(yes)->query->get_unread_count->read->get_url->set_readed->e
cond(no)->query_next->get_unread_count2->cond2
cond2(yes, right)->query_next
cond2(no)->read

本流程用于查询用户已添加的所有设备的事件列表。

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置客户端实例代理函数

接口:c2s_msg_interface

- (void)cli_lib_events_callback:(int)res event_id:(NSString *)event_id events:(NSData *)events
{
    //TODO: 处理分页查询事件列表回复
}

- (void)cli_lib_unreadeventcount_callback:(int)res count:(int)count
{
    //TODO: 处理获取未读条数回复
}

- (void)cli_lib_set_allevents_read_callback:(int)res
{
    //TODO: 处理设置所有事件已读回复
}

...

cli.cli_c2s_msg_delegate = self;
  • 获取事件列表
[cli cli_lib_get_event_list:@"0" items_count:10];//第一个参数为"0"表示第一页
  • 获取未读事件数量
[cli cli_lib_get_unreadevent_count];

注:这个函数不一定要在流程图中标识的步骤中进行,可以在APP启动或从后台切换到前台时取一次。在APP处理激活状态时,未读事件数量会通过推送获取到。

  • 获取事件缩略图
//返回jpeg数据
NSData *data = [cli cli_lib_GetEventThumbnail:camid eventid:eventid];
  • 获取事件图片网址
NSString *url = [cli cli_lib_GetEventUrl:event_id];
//TODO: 用webviewer打开url显示报警图片
  • 将所有事件设置为已读状态
[cli cli_lib_set_all_events_readed];

2.3.16. 查询单个摄像头的事件列表流程

s=>start: 开始
e=>end: 结束
create=>operation: 创建客户端实例
setcb=>operation: 实现并设置事件相关代理函数
cond=>condition: 是否首次查询或重新查询
cond2=>condition: 是否继续读取下一页
query=>operation: 查询第一页,获取当前界面显示的事件缩略图
query_next=>operation: 查询下一页,获取当前界面显示的事件缩略图
read=>operation: 读取一条事件
get_url=>operation: 获取事件url,并显示事件图片
set_readed=>operation: 将所有事件置为已读(可选)
s->create->setcb->cond
cond(yes)->query->read->get_url->set_readed->e
cond(no)->query_next->cond2
cond2(yes, right)->query_next
cond2(no)->read

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置客户端实例代理函数

接口:c2s_msg_interface

- (void)cli_lib_cam_events_callback:(int)res cam_id:(NSString *)cam_id event_id:(NSString *)event_id events:(NSData *)events
{
    //TODO: 处理分页查询单摄像头事件列表回复
}
...

cli.cli_c2s_cam_msg_delegate = self;
  • 获取事件列表
[cli cli_lib_get_cam_event_list:str_camid, event_id:@"0" items_count:10];//第二个参数为"0"表示第一页

注:这里的id是摄像头id,不是设备id,因为有些设备如DVR含有多个摄像头。

  • 获取事件缩略图
//返回jpeg数据
NSData *data = [cli cli_lib_GetEventThumbnail:camid eventid:eventid];
  • 获取事件图片网址
NSString *url = [cli cli_lib_GetEventUrl:event_id];
//TODO: 用webviewer打开url显示报警图片

2.3.17. 分组管理流程

我们可以为摄像头创建分组。分组可以是树状多级结构,分组中可以包含摄像头,也可以包含子分组。摄像头和子分组可以在不同的分组中自由移动。

当删除分组后,原分组下的摄像头和子分组下的摄像头都将被移动到最顶级,即没有被分组包含的状态,原该分组下的子分组将被全部删除。

s=>start: 开始
e=>end: 结束
getsn=>operation: 获取最新流水号
func=>operation: 创建、修改、删除、移动分组,设置摄像头分组
getlist=>operation: 获取摄像头列表
getlist2=>operation: 获取摄像头列表
check=>operation: 判断错误类型
t=>operation: test

cond=>condition: 是否与已获得的流水号一致
cond2=>condition: 是否成功
cond3=>condition: 流水号不一致?

s->getsn->cond
cond(yes, right)->func
cond(no)->getlist(right)->func

func->cond2
cond2(yes, right)->e
cond2(no)->check(bottom)->cond3(bottom)

cond3(yes, right)->getlist->func
cond3(no)->e

注:由于我们允许同一个账号在不同的设备上可以登录多次,因此在进行与分组相关的操作前,最好先获取一下当前最新的摄像头列表流水号,与已获得的流水号进行比较,如果不一致,应当刷新一下摄像头列表,以保证摄像头列表是最新的。如果当前的列表不是最新的,服务器会拒绝这次操作,以防止多人同时操作时引起的列表混乱。在代理函数中得到的流水号是分组操作后最新的流水号,可用于下一次操作。

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置客户端实例代理函数

接口:cameralist_interface

- (void)cli_lib_GetCamlistSn_CALLBACK:(int)HttpRes sn:(short)list_sn;
{
    //TODO: 处理获得的最新摄像头列表流水号
}

- (void)cli_lib_AddNewGroup_CALLBACK:(int)HttpRes groupid:(NSString *)group_id parentid:(NSString *)parent_id new_name:(NSString *)newname my_sn:(int)sn
{
    //TODO: 处理创建分组的回复
}

- (void)cli_lib_ModifyGroupName_CALLBACK:(int)HttpRes groupid:(NSString *)group_id new_name:(NSString *)newname parentid:(NSString *)parent_id my_sn:(int)sn
{
    //TODO: 处理修改分组名称的回复
}

- (void)cli_lib_Deletegroup_CALLBACK:(int)HttpRes groupid:(NSString *)group_id my_sn:(int)sn
{
    //TODO: 处理删除分组的回复
}

- (void)cli_lib_MoveGroup_CALLBACK:(int)HttpRes groupid:(NSString *)group_id old_parid:(NSString *)oldparid new_parid:(NSString *)newparid my_sn:(int)sn
{
    //TODO: 处理移动分组的回复
}

- (void)cli_lib_MoveCamera_CALLBACK:(int)HttpRes camid:(NSString *)cam_id old_parid:(NSString *)parent_old new_parid:(NSString *)parent_new my_sn:(int)sn
{
    //TODO: 处理移动摄像头到分组的回复
}

...

cli.cli_camlist_delegate = self;
  • 获取最新的摄像头列表流水号
[cli cli_lib_GetCamlistSn];
  • 获取摄像头列表
[cli cli_lib_getcamlist_ex];
  • 添加分组
[cli cli_lib_AddNewGroup:groupid new_name:groupname sn:sn];
  • 修改分组名称
[cli cli_lib_ModifyGroupName:groupid new_name:newname with_parid:parent_group_id sn:sn];                                                                                        
  • 移动分组
[cli cli_lib_MoveGroup:groupid oldparid:old_parent_group_id destgroupid:dest_parent_group_id sn:sn];       
  • 移动分组到新建分组
[cli cli_lib_MoveGroup_to_newgroup:groupid oldparid:old_parid new_groupname:new_groupname newgroup_parid:newgroup_parid sn:sn];
  • 移动摄像头到分组
[cli cli_lib_MoveCamera:camid cam_type:cam_type old_groupid:oldgroupid destgroupid:destgroupid sn:sn];

请注意:这里的camid是摄像头编号,非设备编号

  • 移动摄像头到新分组
[cli cli_lib_MoveCamera_to_newgroup:camid cam_type:cam_type old_groupid:oldgroupid new_groupname:new_groupname newgroup_parid:newgroup_parid sn:sn];
  • 删除分组
[cli cli_lib_DeleteGroup:groupid sn:sn]; 

删除分组后,该分组所属的子分组和摄像头会自动转移到最顶下。

2.3.18. 设备参数设置流程

设备参数设置是通过P2P通道直接连接设备后进行设置的,因此所有直接与设备交互的设备参数设置流程,都需要先创建一个P2P连接(使用cli_lib_createconnect接口)。

多个设备参数设置流程,不能同时使用同一个P2P连接,但是如果这些设置流程是依次进行的,即前一个设置流程结束后再进行下一个,此时可以复用P2P连接。

因为设备有很多参数项可以设置,其流程都是一致的,所不同的仅仅是函数接口,因此这里只示例一种设置。

同一个函数可能会允许设置多个参数,如果用户只需要改变其中的一个值,那么调用这个函数时,除了这个改变的值以外,其它不改变的值仍需要将原值传递进去。

以设置网络配置为例:

s=>start: 开始
e=>end: 结束
conn=>operation: 创建P2P连接
conncb=>operation: P2P连接结果代理
cond=>condition: P2P连接成功?
set=>operation: 设置网络参数
close_conn=>operation: 释放连接
cond2=>condition: 是否下一次设置?

s->conn->conncb->cond(right)

cond(yes,right)->set(right)->cond2
cond(no)->close_conn->e

cond2(yes, right)->set
cond2(no)->close_conn->e

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK: (int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;

接口:dev_netif_interface

- (void)cli_lib_netif_cli_get_CALLBACK:(int)res with_data:(NSData *)net_data
{
    //TODO: 处理获取设备网络配置结果
}

- (void)cli_lib_netif_cli_set_CALLBACK:(int)res
{
    //TODO: 处理设置设备网络配置结果
}

...

cli.cli_devconnect_single_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];

注:流程结束前,请不要将h_connector用于其它接口。

  • 获取网络配置
[cli cli_lib_cli_get_netif_settings:hconnector];
  • 设置网络配置
[cli cli_lib_cli_set_netif:hconnector set_json:str_setjson];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

以下将好友及黑名单统称为“关系”

用户A->用户A: 1. 获取自己的关系列表
用户A->用户B: 2. 添加好友请求
用户B->用户A: 3. 好友请求回复
用户A->用户A: 4. 将某用户加入到黑名单
用户A->用户A: 5. 将好友转入黑名单
用户A->用户A: 6. 删除好友或黑名单中的账号

以上步骤请在登录后进行。

以上步骤中,2、4、5、6可以并行进行,不需要严格按照图中的顺序。添加好友时,需要对方的确认;添加黑名单则不用。将某用户添加到黑名单后,将不再能收到该用户的好友请求。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置代理函数

接口:c2s_usr_relations_interface

- (void)cli_lib_GetUsrRelations_CALLBACK:(int)HttpRes data:(NSData *)data
{
    //TODO: 处理获取到的关系列表
}

- (void)cli_lib_AddUsrRelations_CALLBACK:(int)HttpRes relation_usr:(NSString *)relation_usr relation_type:(int)relation_type
{
    //TODO: 处理添加关系的回复
}

- (void)cli_lib_ModifyUsrRelations_CALLBACK:(int)HttpRes relation_usr:(NSString *)relation_usr relation_type:(int)relation_type memo:(NSString *)memo
{
    //TODO: 处理修改关系的回复
}

- (void)cli_lib_DeleteUsrRelations_CALLBACK:(int)HttpRes relation_usr:(NSString *)relation_usr
{
    //TODO: 处理删除关系的回复
}

...

 cli.cli_c2s_usrrelations_delegate = self;
  • 获取关系(好友和黑名单)列表
[cli cli_lib_get_usr_relations];
  • 添加关系(好友或黑名单)
[cli cli_lib_add_usr_relations:m_new_usr relation_type:1];//添加好友
[cli cli_lib_add_usr_relations:m_new_usr relation_type:2];//添加黑名单
  • 修改关系

用于同意好友申请、把好友改为黑名单或修改备注。不能用于添加好友。如果拒绝好友申请,需要使用删除关系cli_lib_delete_usr_relations函数

[cli cli_lib_modify_usr_relations:relation_usr relation_type:relation_type memo:memo];
  • 删除关系

将好友或黑名单从列表中删除掉。

[cli cli_lib_delete_usr_relations:relation_usr];

2.3.20. 摄像头分享管理流程

这一节描述摄像头在好友间分享的流程。主要描述如何将自己的摄像头分享给好友或回收分享,应答别人的分享请求。

别人分享给自己的摄像头,可在通过获取摄像头列表获得。

  • 1. 请求别人将摄像头分享给自己
用户A->平台: 1.1 发起分享摄像头请求
平台->平台: 1.2 记录请求
平台->用户B: 1.3 推送请求
用户B-->平台: 1.4 响应分享请求
平台->用户A: 1.5 推送列表变化
用户A->用户A: 1.6 刷新列表
用户A->用户A: 1.7 删除分享给自己的摄像头

以上步骤请在登录后进行。

  • 2. 处理分享请求(别人向自己发送的分享请求)
手机APP->平台: 2.1 请求获取分享请求事件数量(可定时获取,实时的请求会有推送)
平台-->手机APP: 2.2 分享请求事件数量
手机APP->手机APP: 2.3 提示有分享请求
手机APP->平台: 2.4 请求获取分享请求列表
平台-->手机APP: 2.5 分享请求列表
手机APP->平台: 2.6 逐条响应分享请求
平台-->手机APP: 2.7 处理结果

以上步骤请在登录后进行。

  • 3. 管理自己分享给别人的摄像头
用户A->平台: 3.1 获取分享给某用户的摄像头列表
平台-->用户A: 3.2 回复分享给某用户的摄像头列表
用户A->用户A: 3.3 修改列表
用户A->平台: 3.4 设置新的分享给某用户的摄像头列表
平台->平台: 3.5 比对列表并更新
平台->用户B: 3.6 推送列表变动消息
平台-->用户A: 3.7 回复

以上步骤请在登录后进行。
修改列表是指在列表中添加或删除分享的摄像头

  • 4. 管理某摄像头的分享情况
用户A->平台: 4.1 获取某摄像头的分享用户列表
平台-->用户A: 4.2 某摄像头的分享用户列表回复
用户A->用户A: 4.3 在列表中添加或删除分享的用户
用户A->平台: 4.4 上传新的摄像头分享用户列表
平台->其它用户: 4.5 列表变化推送消息
平台-->用户A: 4.6 上传新的列表的回复

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置代理函数

接口:c2s_usr_relations_interface

- (void)cli_lib_GetShareCams_CALLBACK:(int)HttpRes relation_user:(NSString *)relation_usr data:(NSData *)data
{
    //TODO: 处理获取到的“分享给某用户的摄像头列表”
}

- (void)cli_lib_SetShareCams_CALLBACK:(int)HttpRes relation_usr:(NSString *)relation_usr
{
    //TODO: 处理“设置分享给某用户的摄像头列表请求”结果
}

- (void)cli_lib_ReqShareDev_CALLBACK:(int)HttpRes devid:(NSString *)devid
{
    //TODO: 处理“向其它用户申请分享设备请求”的回复
}

- (void)cli_lib_GetDevBelong_CALLBACK:(int)HttpRes devid:(NSString *)devid belongto:(NSString *)belong_usr
{
    //TODO: 处理“获取设备归属用户请求”的回复
}

- (void)cli_lib_GetCam_shareusrs_CALLBACK:(int)HttpRes camid:(NSString *)camid data:(NSData *)data
{
    //TODO: 处理“某摄像头分享的用户列表”,即这个摄像头分享给了哪些用户
}

- (void)cli_lib_SetShareFriends_CALLBACK:(int)HttpRes camid:(NSString *)camid
{
    //TODO: 处理“将某摄像头分享给某些用户请求”的回复,即告诉平台,这个摄像头分享给了哪些用户后,平台的回复
}

...

cli.cli_c2s_sharecams_delegate = self;
  • 请求其它用户分享摄像头(1.1)
[cli cli_lib_req_share_devid:devid with_msg:msg];

由于发起请求时,发起者并不知道该设备有多少个通道,因此只能针对设备发起请求,被请求者可能选择该设备下的一个或多个摄像头分享给该用户。可通过接口获取设备的归属用户,以便向该用户发起共享请求。

  • 获取设备的归属用户
int res = [cli_lib_get_dev_belong:devid];
  • 响应其它用户的分享请求(1.4、2.6) 可以有三种响应:同意,拒绝,或是为了防止骚扰而将其拉入黑名单
[cli cli_lib_response_sharereq_cam:reqid cmd:1];//同意
[cli cli_lib_response_sharereq_cam:reqid cmd:0];//拒绝

拉入黑名单请参考好友及黑名单管理流程中的”关系管理“。

  • 删除分享给自己的摄像头(1.7)
int res = [cli cli_lib_delete_shared_camera:devid camid:camid sn:cursn];  
  • 获取其它用户发来的分享请求条数(2.1)
int count = [cli cli_lib_get_sharecam_req_count];
  • 获取其它用户发来的分享请求列表(2.4)
int res = [cli cli_lib_get_sharereqs_cams];
  • 获取分享给某用户的摄像头列表(3.1)
int res = [cli cli_lib_get_shared_cams:relation_usr];
  • 设置新的分享给某用户的摄像头列表(3.4)
int res = [cli cli_lib_set_shared_cams:relation_usr cam_json:cam_json];
  • 获取某摄像头的分享用户列表(4.1)
int res = [cli cli_lib_get_cam_shareusr_list:devid camid:camid];
  • 上传新的摄像头分享用户列表(4.4)
int res = [cli cli_lib_set_shared_friends:camid devid:devid friends_json:friends_json];

2.3.21. 透明传输通道交互流程

透明通道是为开发者预留的,可以在设备端和客户端之间P2P传输任意格式数据的接口。因此需要设备端与客户端协同才可正常工作。SDK中的DEMO仅示例了这些接口的用法,在真实环境中测试时,需要设备端也实现了相应的接口,并与客户端使用相同的自定义协议。

在客户端与设备端通过透明通道进行交互时,通过调用cli_lib_trchl_send函数主动发送数据,接收数据则通过cli_lib_trchl_receive_callback代理。

s=>start: 开始
e=>end: 结束

conn=>operation: 创建P2P连接
conncb=>operation: P2P连接结果代理
cond=>condition: P2P连接成功?
open=>operation: 开启透明通道
trans=>operation: 通过透明通道接收和发送数据
close=>operation: 关闭透明通道
close_conn=>operation: 释放连接

s->conn->conncb->cond
cond(yes)->open->trans->close->close_conn->e
cond(no)->close_conn

以上步骤请在登录后进行。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK: (int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;

接口:c2d_trchl_interface

- (void)cli_lib_trchl_receive_callback:(int)trchlhandle data:(NSData *)data datalen:(int)datalen
{
    //TODO: 处理从透明通道获取到的数据(即从设备端->客户端)
}

...

cli.cli_c2d_trchl_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];
  • 打开透明传输通道
h_trchl_handle = [cli cli_lib_trchl_open:h_connector];
  • 通过透明通道发送数据
int len = [cli_lib cli_lib_trchl_send:h_trchl_handle buffer:data size:data_size];

接收数据在代理中进行。

  • 关闭透明通道
[cli cli_lib_trchl_close:h_trchl_handle];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];

2.3.22. 推送信息处理流程

ppview平台具备自己的推送服务器,用于向设备、手机推送消息。

iOS的推送消息分为前台消息和后台消息。前台消息是指APP在前台时,所能收到的消息,这些消息需要在程序中通过代理函数来处理,参见实现推送消息处理代理函数;后台消息是APP处于后台是,在系统中显示的推送消息,APP不需要处理。
iOS的前台消息,由平台服务器直接推送给手机APP。
iOS后台推送消息,由平台服务器推送给苹果推送服务器后,再由苹果推送服务器推送至手机。因此开发者需要在平台中上传推送用的签名证书文件(包括开发版和发布版),或将文件发送开技术支持,由技术支持来处理。

2.3.22.1 初始化并注册APNS消息类型

AppDelegate.m中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   [ppview_cli cli_lib_vv_registerForPush_types:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert) launchingOption:launchOptions];
    return YES;
}

2.3.22.2 向服务器注册手机的设备Token

AppDelegate.m中:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [ppview_cli cli_lib_vv_registerDeviceToken:deviceToken];
}

2.3.22.3 设置推送标记

ViewController.m中:

cli_lib.cli_c2s_msg_delegate = self;

// client_type 0:开发版 1:发布版
[cli_lib cli_lib_vv_setTags_ex:@"user_name" language:@"language tag" push_svr:@"push server address" push_svr_port:8080 push_key:@"pushkey" push_pass:@"pushpass" client_type:0 version:@"1.0"];

注:以上代码中函数调用中的各个参数仅做示例用,在实际开发时,需要替换成正确的值。其中第一个参数必须为用户名,language为语言标记,push_svr、push_svr_port、push_key、push_pass分别为推送服务器地址、端口、推送key和推送pass,在注册为开发者账号时自动分配,或向技术支持索取。

特别注意:client_type用于指定开发版或发布版,在发布版中务必要将值设置为1,否则可能会引起无法接收到推送消息,或是出现混乱的情况。

2.3.22.4 实现推送消息处理代理函数


// 以下为推送消息代理函数
- (void)cli_lib_push_register_callback:(int)res
{
    //TODO: 处理注册请求返回的结果
}

- (void)cli_lib_push_unregister_callback:(int)res
{
    //TODO: 处理注销请求返回的结果
}

- (void)cli_lib_devstatus_change_callback:(NSString *)devid status:(int)status
{
    //TODO: 处理设备状态变化推送
}

- (void)cli_lib_devlist_change_callback
{
    //TODO: 处理设备列表变化推送
}

- (void)cli_lib_eventpic_change_callback:(NSString *)eventid
{
    //TODO: 处理事件图片变化推送(因为事件消息是即时送达的,事件图片需要稍后才能完成上传,因此图片完成上传后会有推送消息,方便界面上更新图片)
}

- (void)cli_lib_camlist_change_callback:(short)sn
{
    //TODO: 处理摄像头列表变化推送
}

- (void)cli_lib_alarm_callback:(NSDictionary *)event
{
    //TODO: 处理事件消息推送
}

- (void)cli_lib_alert_change_callback:(NSString *)camid status:(int)status
{
    //TODO: 处理布撤防状态变化推送
}

- (void)cli_lib_disable_alert_event_callback:(NSString *)camid
{
    //TODO: 处理清除报警状态推送(有些APP会有当摄像头报警时,在摄像头上显示报警状态的需求,当用户取消这个状态时,会触发这个推送,并被推送到所有已用该账号登录的APP上)
}

- (void)cli_lib_disable_lowpower_event_callback:(NSString *)camid
{
    //TODO: 处理清除低电压状态推送(有些外部传感器会触发低电状态,此时所有使用该账号登录的账号都会接收到低电事件。此时,如果有其中一个客户端清除了低电状态,所有其它客户端均会收到清除低电状态的推送,以便在界面上更新状态)
}

- (void)cli_lib_camprivate_change_callback:(NSString *)camid status:(int)status
{
    //TODO: 处理隐私模式开关变化推送(在隐私模式下,无法远程查看音视频)
}

- (void)cli_lib_req_cam_sharenum_change_callback:(int)num
{
    //TODO: 处理分享请求数变化推送(别人向你请求分享的消息)
}

- (void)cli_lib_user_relations_changed_callback:(NSDictionary *)dic
{
    //TODO: 用户关系(好友、黑名单)变化推送
}

2.3.22.5 从推送服务器上注销

在APP中,如果要注销当前用户,或者还需要切换到其它用户时,必须先注销推送,否则在注销状态下,iphone后台仍会收到该用户的推送消息。

[cli cli_lib_vv_push_stop];

2.3.23. APP前后台切换或网络通断时需要调用的函数

IOS客户端切换到后台后,所有网络连接都会被挂起,或者发生网络中断时,会导致已创建的P2P连接中断,重新切换到前台或网络恢复后,虽然P2P连接也会自动恢复,但所需时间较长,为了有更好的体验,需要在前后台切换或网络连接断开时调用切换通知函数,以便SDK库能够及时做出响应。

  • 恢复网络(从后台切换到前台,或网络从中断状态恢复时):
int res = [cli cli_lib_cli_active_status:1];
  • 中断网络(从前台切换到后台,或网络发生中断时):
int res = [cli cli_lib_cli_active_status:0];
[cli cli_lib_clear_player_cache];

2.3.24. 传感器相关

传感器对码流程:

手机APP->设备: 创建P2P连接
设备-->手机APP: P2P连接反馈
手机APP->设备: 请求开启对码模式
设备->设备: 开启对码模式
传感器->设备: 触发信号
设备->设备: 记录新对码到的传感器或等待超时
设备->设备: 结束对码模式
设备-->手机APP: 对码结果

传感器相关操作是通过P2P通道直接连接设备后进行设置的,因此需要先创建一个P2P连接(使用cli_lib_createconnect接口)。
多个操作流程,不能同时使用同一个P2P连接,但是如果这些操作是依次进行的,即前一个设置操作结束后再进行下一个,此时可以复用P2P连接。
某些传感器(也可以称为智能设备)内部,包含有多个子通道,如含有三个插口的智能插座,或者含有多个开关的智能开关组。

  • 得到客户端实例
cli = [ppview_cli getInstance];
  • 实现并设置代理函数

接口:dev_connect_interface

- (void)cli_lib_devconnect_CALLBACK: (int)msg_id connector:(int)connector result:(int)res
{
    //TODO: 处理与设备建立P2P连接结果
}

...

cli.cli_devconnect_single_delegate = self;

接口:c2s_sensors_interface

- (void)cli_lib_get_sensors_callback:(int)res data:(NSData*)data
{
    //TODO: 处理获取到的传感器列表信息
}
- (void)cli_lib_sensor_codding_callback:(int)res data:(NSData*)data
{
    //TODO: 处理对码结果
}
- (void)cli_lib_sensor_delete_callback:(int)res sensor_id:(NSString*)sensor_id;
{
    //TODO: 处理删除传感器结果 
}
- (void)cli_lib_sensor_set_callback:(int)res tag:(int)tag sensorid:(NSString*)sensorid name:(NSString*)name preset:(int)preset isalarm:(int)isalarm;
{
    //TODO: 处理设置传感器信息结果
}
- (void)cli_lib_sensor_subchl_set_callback:(int)res tag:(NSString*)tag sensorid:(NSString*)sensorid chlid:(int)chlid name:(NSString*)name alarm_linkage:(int)alarm_linkage;
{
    //TODO: 处理设置传感器子通道信息结果
}
- (void)cli_lib_sensor_subchl_remote_ctrl_callback:(int)res sensor_id:(NSString*)sensor_id chlid:(int)chlid status:(int)status;
{
    //TODO: 处理远程控制子通道结果
}

...

cli.cli_c2s_sensors_delegate = self;
  • 创建P2P连接
h_connector = [cli cli_lib_createconnect:m_devid devuser:m_devusr devpass:m_devpass];

注:流程结束前,请不要将h_connector用于其它接口。

  • 开始设备与传感器对码

通过对码,将传感器绑定到设备上。调用此接口后,需要触发一下传感器来完成配对,在完成前不可做其他操作。

[cli cli_lib_coding_sensors:hconnector with_chlid:chlid];
  • 获得传感器列表
[cli cli_lib_get_sensors:hconnector with_chlid:chlid sensor_type:sensor_type];
  • 设置传感器信息
[cli cli_lib_set_sensors:hconnector with_chlid:chlid with_id:sensorid name:name preset:preset isalarm:isalarm with_tag:tag];
  • 移除传感器
[cli cli_lib_delete_sensor:hconnector with_chlid:chlid sensorid:sensorid];
  • 设置传感器子通道信息
[cli cli_lib_set_sensor_subchl:hconnector with_chlid:chlid with_id:sensorid subchl_id:subchl_id name:name alarm_linkage:alarm_linkage with_tag:tag];
  • 远程控制传感器开关
[cli cli_lib_sensor_remote_ctl:hconnector sensor_id:sensor_id chlid:chlid subchlid:subchlid status:status];
  • 断开P2P连接
[cli cli_lib_releaseconnect:h_connector];