StreamSDK接入
StreamSDK主要处理和图片视频相关的功能,主要包含直播、回放、云端视频录制、语音对讲、视频通话、抓图、上报报警信息等功能。
StreamSDK-API 列表
StreamSDK 相关的 API 功能说明如下:
API 接口 | 功能说明 |
---|---|
xs_stream_Init | SDK 初始化 |
xs_stream_unInit | SDK 反初始化 |
xs_stream_setDeviceParams | 设置设备参数,主设备在 xs_stream_start 之前就要调用 |
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 | 异步抓图回复 |
xs_stream_sendCmd | 发送控制命令 |
xs_stream_wakeup | 唤醒接口,低功耗设备使用 |
功能详述
初始化
初始化函数必须在其他函数之前调用,初始化的时候需要设置初始化参数以及回调函数指针列表
/**
* @brief 初始化函数,在下面所有的接口调用之前调用
* @param [IN] initConfig: 初始化参数配置
* @param [IN] cbFun: 回调函数指针列 表
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_Init(xs_streamConfig_st* initConfig, xs_stream_callbackFun_st* cbFun);
关于初始化参数中缓存大小(initConfig->streamBufferSize)的说明如下(重要):
- 如果设备内存比较紧张(并且上层有缓存),那么可以不设置缓存给设备SDK(streamBufferSize=0),此时设备启动后不要主动塞流,等待SDK的回调来控制是否塞流(xs_startPushLiveStream/xs_stopPushLiveStream),并且开始塞流的时候,根据SDK回调的预录秒数提前塞数据(因为事件发生的时候,视频录制需要预录一部分视频)
- 如果设备内存充足,建议设置缓存给SDK(不小于1M),此时设备启动后可以保持一直塞流 ,当事件发生或者有人播放时,SDK内部控制事件的视频预录功能。上层应用处理起来会比较简单一些
初始化过程中,需要设置回调函数列表,设备需要处理的回调函数说明:
回调函数 | 说明 |
---|---|
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 | 状态回调,主要包括设备休眠状态,直播状态等回调 |
设置参数
初始化之后,通过下面接口设置设备参数信息
/**
* @brief 设置设备参数,主设备在 xs_stream_start 之前就要调用
* @param [IN] devAuth: 设备认证信息
* @param [IN] devParam: 设备参数信息
* @return 0:成功, 其他值:错误码
*/
xs_int32 xs_stream_setDeviceParams(xs_deviceAuth_st* devAuth, xs_DeviceParams_st* devParam);
音视频服务
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);
注意:设备创建流资源之后,也可以保持持续推流(serviceId 为xs_stream_createStream返回的句柄值),此时就无需响应xs_startPushLiveStream和xs_stopPushLiveStream
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 内部处理。