跳到主要内容

StreamSDK接入

更新时间:2025-04-25 18:01:31

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_streamLogCallbackSDK日志回调
xs_streamMsgCallbacksdk内部消息的回调函数 [设备侧收到此回调之后,直接将参数转给IoTSDK处理即可(xs_iot_handleExternalMsg),不需要关心参数内容]
xs_triggerPictureCallback抓图回调,同步接口,主要用于客户端图片展示、以及其他用途,详情参考 [图片服务]章节
xs_triggerPictureAsyncCallback抓图回调,异步接口
xs_asyncResultCallbackSDK异步处理结果通知,主要用来通知报警图片上传结果
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 函数:

  1. 从本地私有格式录制的媒体文件中逐帧读取音视频数据,将其通过 xs_stream 相关接口传入 SDK
  2. demo 中的此部分代码和本地存储的回放是共用的

音视频推流注意点:

  1. 推流端需要保证传入的音视频时间戳(pts)是同步的,并且是按帧率均匀增长的,误差尽量控制在 150ms 以内
  2. 视频格式支持 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);

本地回放注意点:

  1. 按文件回放过程的 StreamSDK 和摄像头应用程序(CameraAPP)交互流程与按开始时间回放过程类似
  2. 两者的区别主要有3个 2.1. 用queryRecordFileList 代替queryRecordTimeSection查询得到录像文件列表信息 2.2. 用startPlaybackByName代替startPlaybackByTime开始按文件名回放 2.3. seek操作在按时间回放的模式下,seekTime为utc绝对时间戳,在按文件回放的模式下,seekTime为相对于文件开始时间偏移的时间量
  3. queryRecordTimeSection 回调的参数 xs_recordTimeList_st* result,其变量 xs_recordTime_st* section(录像片段列表)需由设备端根据查询到的录像片段条数来分配对应的缓存空间,最后由 SDK 释放
  4. queryRecordFileList 回调的参数 xs_recordFileList_st* result,其变量 xs_recordFile_st* section(录像片段列表)需由设备端根据查询到的录像文件条数来分配对应的缓存空间,最后由 SDK 释放
  5. 设备端查询录像片段时,应该从回调参数 xs_queryRecord_st *queryParam 的变量 xs_uint64 endTime 所对应的查询结束时间往前查询,也即优先查询并返回最新的录像片段(文件)
  6. SDK 通过回调 startPlaybackByTime 开始回放后,设备端需创建一个独立线程用于本地录像的推送,从回调参数 startTime 对应的时间点开始发送录像数据。注意音视频录像按帧推送,当前推送速率为 1 倍速(正常速率),且第一帧为关键帧,同时保持音视频同步。
  7. SDK 通过回调 startPlaybackByName 开始回放后,设备端需创建一个独立线程用于本地录像的推送,从回调参数 fileName 对应的录像文件起始位置开始发送录像数据。注意音视频录像按帧推送,当前推送速率为 1 倍速(正常速率),且第一帧为关键帧,同时保持音视频同步。
  8. SDK 通过回调 seekPlayback 触发 seek 操作后,设备端需首先切换到对应时间点的录像数据,然后需从关键帧开始继续推送音视频录像帧
  9. 回放复用了 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);
}

报警推送注意点:

  1. 设备检测到报警后,调用此接口上报
  2. 如果报警事件一直发生,则一直调用此接口(调用频率不小于 5 秒)
  3. SDK 内部控制报警事件和报警录像的结束
  4. 报警事件可以带图片,也可以不带,不带图片的话 SDK 只上报事件数据
  5. 报警事件可以带用户自定义数据 (xs_intelligentAlarmParam_s.extra 字段),只能传递字符串信息,注意长度不要超过 2KB
  6. 事件长度由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)字段正确填写即可,接口主要包括:

  1. xs_stream_createStream
  2. xs_stream_postIntelligentAlarm
  3. xs_startPlaybackByTime
  4. xs_startPushLiveStream
  5. 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);

注意:

  1. 只有长上电设备才支持定时计划功能,低功耗设备无此功能

远程拉取日志

设备提供了远程拉取日志功能(APP触发物模型服务:TriggerFileCapture),设备SDK收到拉取日志请求后,会触发以下回调函数,然后将应用层打包的日志文件上传到服务器,触发者可以获取到临时下载的 URL链接。

/**
* @brief 拉取日志文件回调,应用层收到此回调后需要将日志打包,SDK会将日志文件上传到服务器用于问题分析,上传完毕后会回调 xs_uploadLogResult 函数
*
* @param [IN] devId: 设备ID
* @param [OUT] logPath: 打包后的日志文件全路径
* @param [IN] logPathLen: logPath 指针buffer的长度,目前为256
* @return 0:成功,其他值:失败
*/
typedef xs_int32 (*xs_captureLog)(xs_int32 devId, xs_char* logPath, xs_uint32 logPathLen);

/**
* @brief 设置日志等级
*
* @param [IN] logLevel: 日志等级
* @return void
*/
typedef xs_void (*xs_setLogLevel)(xs_logLevel_en logLevel);

/**
* @brief 日志上传结果回调,收到此回调后可以将日志打包文件删除
*
* @param [IN] result: 上传结果,0:成功 ,其他:失败
* @param [IN] logPath: 日志文件路径
* @return void
*/
typedef xs_void (*xs_uploadLogResult)(xs_uint32 result, xs_char* logPath);