StreamSDK接入
StreamSDK主要处理和图片视频相关的功能,主要包含直播、回放、云端视频录制、语音对讲、视频通话、抓图、上报报警信息等功能。
StreamSDK-API 列表
StreamSDK 相关的 API 功能说明如下:
API 接口 | 功能说明 |
---|---|
xs_stream_Init | SDK 初始化 |
xs_stream_unInit | SDK 反初始化 |
xs_stream_start | 启动流相关服务 |
xs_stream_stop | 停止流相关服务 |
xs_stream_createStream | 创建媒体流 |
xs_stream_inputVideo | 输入视频流 |
xs_stream_inputAudio | 输入音频流 |
xs_stream_destroyStream | 销毁媒体流 |
xs_stream_inputIotMsg | 处理外部消息(IoTSDK 回调的消息) |
xs_stream_postIntelligentAlarm | 上报 AI 报警事件 |
xs_stream_sleep | 休眠接口,低功耗设备使用 |
xs_stream_postTriggerPicture | 异步抓图回复 |
功能详述
初始化
初始化函数必须在其他函数之前调用,初始化的时候需要设置初始化参数以及回调函数指针列表
/**
* @brief 初始化函数
* @param [IN] initConfig: 初始化参数配置
* @param [IN] cbFun: 回调函数指针列表
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_Init(xs_initConfig_st* initConfig, xs_stream_callbackFun_st* cbFun);
初始化过程中,需要设置回调函数列表,设备需要处理的回调函数说明:
回调函数 | 说明 |
---|---|
xs_streamLogCallback | SDK日志回调 |
xs_streamMsgCallback | sdk内部消息的回调函数 [设备侧收到此回调之后,直接将参数转给IoTSDK处理即可(xs_iot_handleExternalMsg),不需要关心参数内容] |
xs_triggerPictureCallback | 抓图回调,同步接口,主要用于客户端图片展示、以及其他用途,详情参考 [图片服务]章节 |
xs_triggerPictureAsyncCallback | 抓图回调,异步接口 |
xs_asyncResultCallback | SDK异步处理结果通知,主要用来通知报警图片上传结果 |
xs_talkCallback_st | 音频对讲、视频对讲相关的回调函数 |
xs_playbackCallback_st | 回放本地录像,查询本地录像列表,报警列表相关的回调,详情参考 [本地回放] 章节 |
xs_liveStreamCallback_st | 实时流相关的回调,详情参考 [实时推流]章节 |
xs_statusCallback_st | 状态回调,主要包括设备休眠状态,直播状态等回调 |
音视频服务
SDK 主要提供了音视频的直播、对讲、云存储、本地回放等功能,不负责音视频的本地存储。
实时流
1.创建流资源
使用音视频服务之前必须先调用如下接口先创建音视频资源(有几条流就创建几条),然后启动流媒体服务:
/**
* @brief 创建音视频流对象
* @param [IN] devAuth: 设备认证信息
* @param [IN] lensId: 镜头id,从0开始(用于双目或多目设备)
* @param [IN] streamId: 码流id,从0开始(主码流为0,子码流为1)
* @param [IN] videoParam: 视频参数
* @param [IN] audioParam: 音频参数
* @notice 开始推流之前需要调用此API
* @return 句柄,用于后续视频流操作,返回值=0 为成功,否则为失败的错误码
*/
xs_int32 xs_stream_createStream(xs_deviceAuth_st* devAuth, xs_uint32 lensId, xs_uint32 streamId, const xs_videoEncodeParam_st* videoParam, const xs_audioEncodeParam_st* audioParam);
/**
* @brief 启动服务
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_start();
2.等待SDK回调推流
音视频资源创建成功后,设备等待 SDK 回调控制是否推流:
/**
* @brief 开始推送实时流,收到此回调后,应用层调用 xs_stream_inputVideo 和 xs_stream_inputAudio 接口将实时数据塞入SDK
* @param [IN] streamIndex: 请求流信息
* @param [IN] serviceId:服务ID
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_startPushLiveStream)(const xs_streamIndex_st* streamIndex, xs_int32 serviceId, const PushParams_st* param);
/**
* @brief 停止推送实时流
*
* @param [IN] serviceId:
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_stopPushLiveStream)(xs_int32 serviceId);
3.开始推流
收到xs_startPushLiveStream回调后,调用推流接口塞入实时流,注意 serviceId 要填写正确(此塞流接口和回放数据塞流接口是复用的)
/**
* @brief 输入编码后的的视频帧(直播或者回放),收到播放回调:xs_startPushLiveStream/xs_startPlaybackByTime/xs_startPlaybackByFileName 之后,一直调用此接口塞入视频
* @param [IN] serviceId: 播放回调函数参数中的service id
* @param [IN] videoFrame:视频帧
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_inputVideo(xs_int32 serviceId, const xs_videoInfo_st* videoFrame);
/**
* @brief 输入编码后的的音频帧(直播或者回放),收到播放回调:xs_startPushLiveStream/xs_startPlaybackByTime/xs_startPlaybackByFileName 之后,一直调用此接口塞入音频
* @param [IN] serviceId: 播放回调函数参数中的service id
* @param [IN] audioFrame:音频帧
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_inputAudio(xs_int32 serviceId, const xs_audioInfo_st* audioFrame);
4.停止推流
收到 xs_stopPushLiveStream 回调后,设备停止推送实时流
5.销毁流资源
当设备处于关闭状态后(标准物模型中的powerstate可以控制设备关闭状态),需要停止推流并销毁流资源:
/**
* @brief 销毁音视频流对象
* @param [IN] hdl: xs_stream_createVideoStream 返回的句柄
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_destroyStream(xs_int32 hdl);
6.示例代码
参考 demo 中音视频推流的流程,demo 中的 PushAVDataProc 函数:
- 从本地私有格式录制的媒体文件中逐帧读取音视频数据,将其通过 xs_stream 相关接口传入 SDK
- demo 中的此部分代码和本地存储的回放是共用的
音视频推流注意点:
- 推流端需要保证传入的音视频时间戳(pts)是同步的,并且是按帧率均匀增长的,误差尽量控制在 150ms 以内
- 视频格式支持 h264/h265, 音频目前支持 g711a
demo推流示例代码
/*独立线程,循环读取本地视频文件,调用sdk接口推流*/
void *PushAVDataProc(void* args)
{
char threadName[20]={0};
StreamResource_st* ctx = (StreamResource_st*)args;
MediaInfo_st frameheader;
xs_uint64 vpts = 0, apts = 0;
xs_uint32 vseq = 0, aseq = 0;
char filepath[256] = {0};
xs_int32 fd_stream = -1;
xs_uint32 readsize = 0;
unsigned char *framedata = NULL;
sprintf(threadName, "demoPushProc-%d", ctx->serviceId);
pthread_setname_np(pthread_self(), threadName);
/* 读取私有格式录制的测试文件,模拟音视频输入。 测试文件中 视频为h265格式,帧率15fps, 音频为g711a格式, 帧率25fps*/
sprintf(filepath, "./resource/media.raw");
LOG_PRINT("[stream:%d-%d-%d][service:%d]start streaming demo proc, path %s\n", ctx->streamIdx.devId,ctx->streamIdx.lensId, ctx->streamIdx.streamId, ctx->serviceId, filepath);
fd_stream = open(filepath, O_RDONLY);
if(fd_stream < 0) {
LOG_PRINT("open file failed,path:%s\n",filepath);
goto CLEAN_UP;
}
framedata = (unsigned char *)malloc(512*1024);
if (!framedata) {
goto CLEAN_UP;
}
while(ctx->streamLoop) {
readsize = read(fd_stream, &frameheader, sizeof(MediaInfo_st));
//printf("readsize:%d\n", readsize);
if(readsize < sizeof(MediaInfo_st)) {
lseek(fd_stream, 0, SEEK_SET);
LOG_PRINT("readsize:%d, reopen file again\n", readsize);
continue;
}
readsize = read(fd_stream, framedata, frameheader.data_size);
//LOG_PRINT("read data, encode type:%d\n",frameheader.media_type);
if (frameheader.media_type == MEDIA_TYPE_VIDEO) {
vpts += 66; //测试文件中,视频帧率15fps
xs_videoInfo_st vinfo={0};;
vinfo.encode = XS_VIDEO_TYPE_H265;
vinfo.frame_type = (frameheader.frame_type == VIDEO_FRAME_TYPE_IFRAME)?XS_VIDEO_FRAME_I:XS_VIDEO_FRAME_P;
vinfo.payload = framedata;
vinfo.payload_size = frameheader.data_size;
vinfo.pts = vpts;
vinfo.utcms = ctx->isPlayback? (ctx->startTime + vpts):currentTimeMillisecond(); //回放流将时间戳utc时间戳特殊处理了一下
vinfo.seq = ++vseq;
if (VIDEO_FRAME_TYPE_IFRAME == frameheader.frame_type) {
LOG_PRINT("[stream:%d-%d-%d][service:%d]push key frame| size:%d | pts:%lld | seq:%d\n",
ctx->streamIdx.devId,ctx->streamIdx.lensId, ctx->streamIdx.streamId,
ctx->serviceId, frameheader.data_size, vpts, vseq);
}
/*输入视频数据*/
if (xs_stream_inputVideo(ctx->serviceId, &vinfo) != XSIOT_EC_SUCCESS) {
LOG_PRINT("input video frame failed\n");
}
usleep(66000);
} else if (frameheader.media_type == MEDIA_TYPE_AUDIO) {
apts += 40; //音频帧率25fps
xs_audioInfo_st ainfo={0};;
ainfo.encode = XS_AUDIO_TYPE_G711A;
ainfo.payload = framedata;
ainfo.payload_size = frameheader.data_size;
ainfo.pts = apts;
ainfo.seq = ++aseq;
ainfo.utcms = ctx->isPlayback? (ctx->startTime + apts):currentTimeMillisecond();//回放流将时间戳utc时间戳特殊处理了一下
/*输入音频数据*/
if (xs_stream_inputAudio(ctx->serviceId, &ainfo) != XSIOT_EC_SUCCESS) {
LOG_PRINT("input audio frame failed\n");
}
}
}
CLEAN_UP:
if (framedata){
free(framedata);
}
if (fd_stream!= -1){
close(fd_stream);
}
LOG_PRINT("[stream:%d-%d-%d] quit streaming demo proc\n",ctx->streamIdx.devId,ctx->streamIdx.lensId, ctx->streamIdx.streamId);
return NULL;
}
直播/云存
设备只需要正确响应 SDK 拉取实时流的回调(xs_startPushLiveStream 和 xs_stopPushLiveStream)即可,直播请求的响应和云存由SDK内部处理。
设备云存储支持事件存储和全天存储的模式,录制时长由平台下发的套餐信息决定, SDK 内部处理。
本地回放
本地回放功能是指APP远程回看摄像头本地存储保存的历史视频。
SDK 不负责本地存储,音视频的本地存储功能由设备厂商自行实现。
SDK提供查询和 回放的相关接口回调,设备端需要实现这些回调,以实现本地录像的查询、回放、Seek 等操作。
/*SD卡回放相关的回调*/
typedef struct {
xs_startPlaybackByTime startPlaybackByTime;
xs_startPlaybackByFileName startPlaybackByName;
xs_seekPlaybackByTime seekPlayback;
xs_stopPlayback stopPlayback;
xs_playbackCtrl playbackCtrl;
xs_queryRecordTimeSection queryRecordTimeSection;
xs_queryRecordFileList queryRecordFileList;
xs_queryMonthRecord queryMonthRecord;
xs_queryAlarmEventList queryEventList;
xs_getPlaybackProperty getPlaybackProperty;
} xs_playbackCallback_st;
SDK中关于本地回放相关的功能主要包括:
功能 | 说明 | 接口名称 |
---|---|---|
按时间回放 | 从指定时间开始回放本地存储数据 | xs_startPlaybackByTime |
按文件名回放 | 按照文件名开始回放本地存储数据 | xs_startPlaybackByFileName |
停止回放 | 停止回放 | xs_stopPlayback |
回放seek | 回放开始之后,切换回放的时间点 按照开始时间回放本地存储数据后,seek的时间点为绝对时间 按照文件名开始回放本地存储数据后,seek的时间点为相对时间(相对文件开始位置的时间偏移量) | xs_seekPlaybackByTime |
回放速率控制 | 控制设备按照指定倍速进行回放 | xs_playbackCtrl |
查询录像时间列表 | 根据起止时间点查询指定时间段内的录像片段时间段信息 | xs_queryRecordTimeSection |
查询报警列表 | 根据起止时间点查询指定时间段内的报警片段时间段信息 | xs_queryAlarmEventList |
查询录像文件列表 | 根据起止时间点查询指定时间段内的录像文件名称列表信息 | xs_queryRecordFileList |
查询录像天数 | 根据指定月份查询当月有录像的天数信息 | xs_queryMonthRecord |
获取回放能力 | 获取回放能力 相关信息(目前只有并发路数) | xs_getPlaybackProperty |
回放过程的 StreamSDK 和摄像头应用程序(CameraAPP)交互流程图如下:
按文件回放
客户端可以拉取到设备录像文件列表,选择按照文件名进行回放,回调如下:
/**
* @brief 按文件名开始回放的回调,设备收到此回调后,通过xs_stream_inputVideo 和 xs_stream_inputAudio 接口塞入音视频数据
* @param [IN] streamIndex: 请求流信息
* @param [IN] serviceId:服务ID
* @param [IN] fileName: 开始回放文件名
* @param [IN] seekTime: seek时间戳,utc毫秒,按文件回放的模式下,为相对于文件开始时间偏移的时间量
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_startPlaybackByFileName)(const xs_streamIndex_st* streamIndex, xs_int32 serviceId, const xs_char* fileName, xs_uint64 seekTime);
按时间点回放
客户端可以选择按照时间点进行回放,回调如下:
/**
* @brief 按开始时间回放的回调,设备收到此回调后,通过xs_stream_inputVideo 和 xs_stream_inputAudio 接口塞入音视频数据
* @param [IN] streamIndex: 请求流信息
* @param [IN] serviceId:服务ID
* @param [IN] startTime: 开始回放时间戳,utc毫秒
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_startPlaybackByTime)(const xs_streamIndex_st* streamIndex, xs_int32 serviceId, xs_uint64 startTime);
倍速回放
回放开始后,倍速回放通过xs_playbackCtrl回调触发,设备侧收到此回调后,需要按照指定倍速(或者只推关键帧)塞入数据,注意塞入数据的时候时间戳 pts 要确保准确(使用数据原始的pts时间戳即可),sdk内部会根据倍速处理pts,这样播放器才能达到倍速回放的效果。
/**
* @brief 回放控制的回调
*
* @param [IN] serviceId:服务ID,标识一次回放的session
* @param [IN] speed: 倍速
* @param [IN] keyOnly: 是否仅推送关键帧 0: 推送全数据帧, 1: 仅推送I帧
*
* @return 0:成功,其他值:失败
*
* @note 回调函数返回成功后,设备需要按照指定倍速塞入视频流(非1倍速的情况下,sdk会丢弃音频流,否则会导致app无法正常播放)。设备塞入的数据流pts按 照编码时的pts正常塞入,sdk内部会按照倍速对pts进行调整和校准
*/
typedef xs_int32 (*xs_playbackCtrl)(xs_int32 serviceId, xs_float speed, xs_int32 keyOnly);
并发路数
回放并发路数通过回调 xs_getPlaybackProperty 获取,不实现此回调,默认路数为1
/**
* @brief 获取回放能力相关信息
*
* @param [IN] devId: 设备ID
* @param [OUT] params: 回放能力相关信息
* @return void
*/
typedef xs_void (*xs_getPlaybackProperty)(xs_int32 devId, xs_playbackParams_st* property);
本地回放注意点:
- 按文件回放过程的 StreamSDK 和摄像头应用程序(CameraAPP)交互流程与按开始时间回放过程类似
- 两者的区别主要有3个 2.1. 用queryRecordFileList 代替queryRecordTimeSection查询得到录像文件列表信息 2.2. 用startPlaybackByName代替startPlaybackByTime开始按文件名回放 2.3. seek操作在按时间回放的模式下,seekTime为utc绝对时间戳,在按文件回放的模式下,seekTime为相对于文件开始时间偏移的时间量
- queryRecordTimeSection 回调的参数 xs_recordTimeList_st* result,其变量 xs_recordTime_st* section(录像片段列表)需由设备端根据查询到的录像片段条数来分配对应的缓存空间,最后 由 SDK 释放
- queryRecordFileList 回调的参数 xs_recordFileList_st* result,其变量 xs_recordFile_st* section(录像片段列表)需由设备端根据查询到的录像文件条数来分配对应的缓存空间,最后由 SDK 释放
- 设备端查询录像片段时,应该从回调参数 xs_queryRecord_st *queryParam 的变量 xs_uint64 endTime 所对应的查询结束时间往前查询,也即优先查询并返回最新的录像片段(文件)
- SDK 通过回调 startPlaybackByTime 开始回放后,设备端需创建一个独立线程用于本地录像的推送,从回调参数 startTime 对应的时间点开始发送录像数据。注意音视频录像按帧推送,当前推送速率为 1 倍速(正常速率),且第一帧为关键帧,同时保持音视频同步。
- SDK 通过回调 startPlaybackByName 开始回放后,设备端需创建一个独立线程用于本地录像的推送,从回调参数 fileName 对应的录像文件起始位置开始发送录像数据。注意音视频录像按帧推送,当前推送速率为 1 倍速(正常速率),且第一帧为关键帧,同时保持音视频同步。
- SDK 通过回调 seekPlayback 触发 seek 操作后,设备端需首先切换到对应时间点的录像数据,然后需从关键帧开始继续推送音视频录像帧
- 回放复用了 xs_stream_inputVideo、xs_stream_inputAudio 推流,注意 serviceId 不要填错
播放状态
SDK提供了无人观看,有人观看的回调通知如下:
/**
* @brief 播放状态回调(包含直播和SD卡回放)
*
* @param [IN] devId: 设备ID标识
* @param [IN] isPlay: 是否有人播放
* @return void
*/
typedef xs_void (*xs_playStatusCb)(xs_int32 devId, xs_bool isPlay);
音视频对讲
StreamSDK支持双向音频对讲以及双向视频对讲,当 App 发起语音对讲或视频对讲后,设备端会收到下列回调
/*对讲相关回调*/
typedef struct {
xs_talkStartCallback talkStartCb; /*对讲开始*/
xs_talkAudioDataCallback talkAudioCb; /*对讲语音数据回调*/
xs_talkVideoDataCallback talkVideoCb; /*对讲视频数据回调(带屏设备)*/
xs_talkStopCallback talkStopCb; /*对讲停止*/
} xs_talkCallback_st;
/**
* @brief 对讲开始的回调
*
* @param [IN] devId: 设备ID
* @param [IN] serviceId:服务ID
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_talkStartCallback)(xs_int32 devId, xs_int32 serviceId);
/**
* @brief 对讲音频数据的回调
*
* @param [IN] devId: 设备ID
* @param [IN] serviceId:服务ID
* @param [IN] audioInfo:音频数据
*
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_talkAudioDataCallback)(xs_int32 devId, xs_int32 serviceId, xs_audioInfo_st* audioInfo);
/**
* @brief 对讲视频数据的回调(带屏设备)
*
* @param [IN] devId: 设备ID
* @param [IN] serviceId:服务ID(xs_talkStartCallback回调中的serviceId)
* @param [IN] audioInfo:视频数据
*
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_talkVideoDataCallback)(xs_int32 devId, xs_int32 serviceId, const xs_videoInfo_st* videoInfo);
/**
* @brief 对讲停止的回调
*
* @param [IN] devId: 设备ID
* @param [IN] serviceId:服务ID
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_talkStopCallback)(xs_int32 devId, xs_int32 serviceId);
**注意:目前对讲的音频格式默认为 g711a 格式,采样频率 8khz,16bit,视频格式为H264编码格式 **
上传图片
SDK 提供了回调接口,用于抓取设备当前图片,并上传到云端,用于客户端图片展示或者其他用途。
建议默认抓取标清图片即可,主要作为客户端展示实时缩略图使用
SDK目前支持两种模式推送图片。
同步模式
回调函数定义如下:
/**
* @brief 抓图回调,主要用于客户端图片展示、以及其他用途
*
* @param [IN] devId: 设备ID
* @param [OUT] picInfo: 返回的图片信息
*
* @return void
*/
typedef xs_int32 (*xs_triggerPictureCallback)(xs_int32 devId, xs_pictureInfo_st* picInfo);
/* 抓图回复结构体 */
typedef struct {
xs_char *pic; //抓图事件的媒体数据,buffer由设备层进行分配
xs_uint32 picLen; //图像长度
xs_uint32 width; //图像宽度
xs_uint32 height; //图像高度
xs_freeFun freePic; //释放图片内存的函数指针,NULL表示不需要释放,回调函数结束后,SDK会调用此函数释放内存
} xs_pictureInfo_st;
/*回调函数释放内存*/
typedef void (*xs_freeFun)(xs_char* buf);
注意:回调参数结构体中指定了释放图片内存的函数(freePic),如果需要释放内存,设备层需要实现此函数,否则将其置为 NULL 即可。
示例代码:
/*回调函数释放内存*/
static void freePicture(xs_char* buf)
{
if (buf){
LOG_PRINT("release buf:%p\n", buf);
free(buf);
}
}
/*抓图回调*/
static xs_int32 capturePicCb(xs_int32 devId, xs_pictureInfo_st* picInfo)
{
LOG_PRINT("start to capture picture\n");
/*此处实现抓取图片的功能,demo中从本地读取的文件图片*/
if (!GetSnapshot(picInfo)){
LOG_PRINT("get snapshot failed\n");
return XSIOT_EC_CAPTURE_PIC_FAILED;
}
picInfo->freePic = freePicture; /*设置释放图片内存的函数*/
return XSIOT_EC_SUCCESS;
}
异步模式
回调函数定义如下:
/**
* @brief 抓图回调,异步接口,主要用于客户端图片展示、以及其他用途,设备收到此回调后,需要抓取当前图片,并通过 xs_stream_postTriggerPicture 接口将图片上传
*
* @param [IN] devId: 设备ID
* @param [IN] xs_int32: 服务ID
*
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_triggerPictureAsyncCallback)(xs_int32 devId, xs_int32 serviceId);
收到回调后,调用推图片接口上传,注意serviceId要和回调中保持一致。
/**
* @brief 异步抓图回复,收到xs_triggerPictureAsyncCallback 回调之后抓图成功,然后调用此接口
*
* @param [IN] service_id: 服务ID
* @param [IN] picture: 图片内容信息
*
* @notice:
* 1. 注意图片大小不要超过2MB
* 2. 此接口无上传结果的回调
*
* @return 0:成功, 其他值:错误码
*
*/
xs_int32 xs_stream_postTriggerPicture(xs_int32 serviceId, xs_pictureInfo_st* picture);
上报智能告警
功能描述:
报警推送是指当设备检测到报警事件时,通过 SDK 接口将事件以及图片信息上报到平台,平台将事件和图片信息保存下来,客户端可以回看报警事件列表,以及可以收到报警推送消息 。
报警推送接口:
/**
* @brief 上报智能报警信息,上报结果通过回调函数返回
* @param [IN] devAuth :设备认证信息
* @param [IN] param: 报警事件信息
* @param [OUT] serviceId: 返回值,用于和上报结果回调函数中的serviceId对应
* @return 0:成功, 其他值:错误码
*
* @notice:
* 1. 调用间隔短于云端设定值,返回失败
* 2. 附加字符串大于2048 字节时会被截断,但不会返回失败
* 3. 本接口为异步接口,存在图片数据时,SDK会进行图片数据的拷贝,拷贝受到image_size_max的限制,如果大小不足,直接返回失败,无回调
* 4. 事件也会进行拷贝,不受限制。事件和图片随后加入任务队列中
* 5. 任务处理时,先上传图片。如果有图片数据,则进行图片上传,否则进入下一步。如果图片上传失败,不会进行重传,进入下一步
* 6. 上报事件,如果事件上报失败,不会进行重传。
* 7. 上传结果会回调至 xs_asyncResultCallback
*/
xs_int32 xs_stream_postIntelligentAlarm(xs_deviceAuth_st* devAuth, const xs_intelligentAlarmParam_s *param, xs_int32* serviceId);
报警上报结果异步回调如下,通过 result 中的 serviceId 可以和请求进行匹配:
/**
* @brief SDK异步处理结果通知
*
* @param devId : 设备ID信息
* @param result : 异步结果
*
* @return void
*/
typedef void (*xs_asyncResultCallback)(xs_int32 devId, const xs_asyncResultData_st *result);
/* SDK异步处理结果通知内容 */
typedef struct {
xs_asyncResultType_en type;
struct {
xs_uint64 serviceId;
xs_int32 result; //0:成功 ,其他值:失败
} pictureUpload;
} xs_asyncResultData_st;
示例代码:
/*上报报警和图片信息*/
static void reportAlarm()
{
xs_pictureInfo_st picInfo;
/*此处实现抓取图片的功能,demo中从本地读取的文件图片*/
if (!GetSnapshot(&picInfo)){
LOG_PRINT("get snapshot failed\n");
return;
}
xs_intelligentAlarmParam_s alarm;
memset(&alarm,0,sizeof(alarm));
alarm.type = XS_INTELLIGENT_EVENT_MOVING_CHECK;
alarm.eventTime = currentTimeMillisecond();
alarm.extra.p = "this is motion event";
alarm.extra.len = strlen(alarm.extra.p);
alarm.media.height = picInfo.height;
alarm.media.width = picInfo.width;
alarm.media.p = picInfo.pic;
alarm.media.len = picInfo.picLen;
xs_uint64 serviceId=-1;
xs_int32 ret = xs_stream_postIntelligentAlarm(0, &alarm, &serviceId);
if (picInfo.pic){
free(picInfo.pic);
}
LOG_PRINT("post alarm, ret=%d, serviceId=%lld\n", ret, serviceId);
}
/*异步回调*/
static void asyncResultCb(xs_int32 devId, const xs_asyncResultData_st *result)
{
LOG_PRINT("asyncResultCb, devId=%d, serviceId=%lld, result:%d\n", devId, result->pictureUpload.serviceId, result->pictureUpload.result);
}
报警推送注意点:
- 设备检测到报警后,调用此接口上报
- 如果报警事件一直发生,则一直调用此接口(调用频率不小于 5 秒)
- SDK 内部控制报警事件和报警录像的结束
- 报警事件可以带图片,也可以不带,不带图片的话 SDK 只上报事件数据
- 报警事件可以带用户自定义数据 (xs_intelligentAlarmParam_s.extra 字段),只能传递字符串信息,注意长度不要超过 2KB
- 事件长度由SDK内部控制,目前支持定长和不定长两种模式,长度由服务器下发的套餐信息决定,不定长是指只要有事件触发就一直录像(一个事件最长10分钟),定长是指事件长度固定(默认15秒,服务器下发的套餐信息中可以修改)
双目/多目设备处理
双目/多目设备需要通过以下接口设置设备的镜头目数(不设置的话默认为 1)。
/**
* @brief 设置设备参数
* @param [IN] devAuth: 设备认证信息
* @param [IN] devParam: 设备参数信息
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_setDeviceParams(xs_deviceAuth_st* devAuth, xs_DeviceParams_st* devParam);
typedef struct {
xs_uint32 lensCount; /*设置镜头个数,多目设备必须设置,不设置的话默认为1*/
} xs_DeviceParams_st;
并且在塞流和报警相关接口、以及 SDK 的回调接口注意将镜头 ID (lensId)字段正确填写即可,接口主要包括:
- xs_stream_createStream
- xs_stream_postIntelligentAlarm
- xs_startPlaybackByTime
- xs_startPushLiveStream
- xs_livePreviewStatus
设备打开/关闭
设备关闭,也称休眠,休眠期间设备只维持和服务器之间的信令连接,不处理其他任何业务请求, 也不要上报视频和报警相关信息。
用户可以通过APP或者其他方式打开或者关闭设备,此功能一般是用户通过标准物模型实现(物模型属性中的 powerstate 属性),SDK内部没有处理此属性,需要客户自行处理。
设备关闭后,应用层需要调用 DestroyStream 将创建的流全部销毁,并且停止设备本地录制等功能。同时停止推送报警事件以及音视频数据到SDK。
当设备重新打开后,需要调用 CreateStream接口将流重新创建出来,并将其他被停止的业务恢复到正常状态。
定时计划
目前SDK内部支持的定时计划任务包括定时云录像计划,以及定时关闭计划,通过物模型属性实现,分别是物模型属性: CloudRecordPlan 和 DeviceOffPlan。
定时录像计划(CloudRecordPlan )
是指APP可以设置在指定的时间段内才触发云录像以及事件报警。此功能完全由SDK内部进行控制,使用者不需要关注。
定时关闭计划(DeviceOffPlan)
是指APP可以设置在指定的时间段内关闭设备(也称休眠)。
此功能SDK内部实现定时任务,需要关闭或打开的时候,触发以下回调:
/**
* @brief 定时开关机状态回调
*
* @param [IN] devId: 设备ID
* @param [IN] isOn: TRUE表示开机, FALSE表示关机
* @return void
* @note 当APP通过物模型(DeviceOffPlan)设置了定时开关机的计划,SDK内部会有定时器处理设备开关机计划,需要关闭或打开摄像头的时候,会触发此回调。
* 设备收到此回调后,需要处理相应的动作,关机后需要将流销毁(DestroyStream),并停止塞图,事件上报等操作,开机后需要恢复状态
*/
typedef xs_void (*xs_switchStatusCb)(xs_int32 devId, xs_bool isOn);
注意:
- 只有长上电设备才支持定时计划功能,低功耗设备无此功能