一、核心说明
云存储采用「服务器主动取流」模式(类似实时观看):设备端触发存储需求时,调用接口通知云服务器从设备端取流,底层基于 TUTK AVAPIs 协议。
设备端仅需对接 TUTK 公版实时音视频协议,无需额外开发复杂存储逻辑。
1.1 编码格式要求
图像:H264 / H265
音频:PCM、AAC、G711 等
音频:PCM、AAC、G711 等
二、云存储模块初始化
初始化时读取本地云存储配置文件(含 URL、Token),无配置则通过回调从 APP 端获取并持久化。
// 读取云存储配置文件(gVsaasInfoFilePath:本地配置文件路径)
char *vsaas_info = NULL;
FILE *vsaas_info_file_ptr = fopen(gVsaasInfoFilePath, "r");
int ret = 0;
if (vsaas_info_file_ptr == NULL) {
// 未配置:从APP端获取(通过VsaasConfigChangedHandle回调保存)
printf("Enable VSaaS without preload data!\n");
ret = avEnableVSaaS(
device_ctx, // 设备上下文(提前初始化)
NULL, // 无预加载配置
VsaasConfigChangedHandle, // 配置变更回调
VSaaSUpdateContractInfoHandle// 合约信息更新回调
);
} else {
// 已配置:读取本地文件初始化
long file_size = 0;
fseek(vsaas_info_file_ptr, 0, SEEK_END);
file_size = ftell(vsaas_info_file_ptr);
rewind(vsaas_info_file_ptr);
vsaas_info = (char*)malloc(sizeof(char)*file_size);
if (vsaas_info == NULL) {
printf("memory alloc fail!\n");
fclose(vsaas_info_file_ptr);
return -1;
}
fread(vsaas_info, 1, file_size, vsaas_info_file_ptr);
ret = avEnableVSaaS(device_ctx, vsaas_info, VsaasConfigChangedHandle, VSaaSUpdateContractInfoHandle);
fclose(vsaas_info_file_ptr);
free(vsaas_info);
gVsaasConfigExist = true;
}
if (ret != AV_ER_NoERROR) {
printf("avEnableVSaaS error [%d]\n", ret);
return -1;
}说明:device_ctx 为设备初始化后的核心句柄,需确保已正常创建
三、通知云服务器取流
支持「实时流(Live)」和「回放流(Playback)」两种模式,按参数要求构造 JSON 后调用接口。
/* 参数说明:
* starttime:实时流填"live",回放流填时间戳(秒)
* event_id:固定为 VSAAS_EVENT_GENERAL
* event_file:回放流必填,指定本地录像文件路径
* media_type:0=H264,1=H265,2=JPEG
* channel:回放流必填,传入回放专用IOTC通道ID(避免avServStartEx超时)
*/
char att_json_str[256] = {0};
bool isLiveView = true; // true=实时流,false=回放流
bool h264 = true; // true=H264,false=H265
unsigned long current_time_sec = 1699999999; // 回放起始时间戳
char *gRecordFile = "/mnt/record/20231114_100000.h264"; // 本地录像文件
int iotc_channel_id_for_playback = 1; // 回放IOTC通道ID
if (isLiveView) {
// 实时流模式
sprintf(att_json_str,
"{\"starttime\":\"live\",\"protocol\":\"tutk\",\"event_id\":\"%d\",\"media_type\":\"%d\"}",
VSAAS_EVENT_GENERAL,
h264 ? 0 : 1
);
} else {
// 回放流模式
sprintf(att_json_str,
"{\"starttime\":\"%lu\",\"protocol\":\"tutk\",\"event_id\":\"%d\",\"event_file\":\"%s\",\"media_type\":\"%d\",\"channel\":\"%d\"}",
current_time_sec,
VSAAS_EVENT_GENERAL,
gRecordFile,
h264 ? 0 : 1,
iotc_channel_id_for_playback
);
}
// 通知云服务器取流(超时3000ms)
Nebula_Json_Obj attr_obj = NULL;
Nebula_Json_Obj_Create_From_String(att_json_str, &attr_obj);
ret = avServNotifyCloudRecordStream(attr_obj, 3000, NULL);
printf("avServNotifyCloudRecordStream ret[%d]\n", ret);
if (ret != AV_ER_NoERROR) {
// 错误处理(例:-20043=用户未购买云存储方案)
printf("通知取流失败,错误码:%d\n", ret);
}通知成功后,云服务器会发起 IOTC 连线,通过 P2P 协议取流(设备端将收到 0x1ff、0x351 拉流指令)。
四、关键回调函数实现
4.1 配置变更回调(保存APP端下发的URL和Token)
APP 端将云存储配置(URL、Token)以 JSON 格式下发,设备端需保存到本地文件,供下次初始化使用。
static void VsaasConfigChangedHandle(const char *vsaas_config)
{
printf("Enter %s\n", __func__);
printf("收到云存储配置:\n%s\n", vsaas_config);
// 保存配置到本地文件
FILE *vsaas_info_file_ptr = fopen(gVsaasInfoFilePath, "w+");
if (vsaas_info_file_ptr == NULL) {
printf("%s:打开配置文件失败!路径:%s\n", __func__, gVsaasInfoFilePath);
return;
}
fwrite(vsaas_config, 1, strlen(vsaas_config), vsaas_info_file_ptr);
fclose(vsaas_info_file_ptr);
gVsaasConfigExist = true; // 标记已配置
}4.2 合约信息更新回调(适配云存储方案限制)
云存储合约变更时触发(如分辨率、码率、帧率限制),设备端需按合约调整录像参数。
// 全局变量:存储云存储合约信息
VSaaSContractInfo gVsaasContractInfo = {0};
static void VSaaSUpdateContractInfoHandle(const VSaaSContractInfo *contract_info)
{
printf("Enter %s\n", __func__);
// 打印合约信息
printf("合约类型:%u\n", contract_info->contract_type);
printf("最大录制时长:%d秒\n", contract_info->event_recording_max_sec);
printf("最大帧率:%d fps\n", contract_info->video_max_fps);
printf("最大码率:%d kbps\n", contract_info->recording_max_kbps);
printf("最大分辨率:%dx%d\n", contract_info->video_max_width, contract_info->video_max_high);
// 保存合约信息(设备端需按此调整录像参数)
memcpy(&gVsaasContractInfo, contract_info, sizeof(VSaaSContractInfo));
}说明:需包含 VSaaSContractInfo 结构体定义头文件(参考SDK文档)
五、注意事项
公版 Kalay APP 适配:需修改密码验证回调,增加对账号
"vsaas" 的支持(云服务器取流时使用该账号认证)。int ExPasswordAuthCallBackFn(const char *account, char *pwd, unsigned int pwd_buf_size)
{
// 允许设备默认账号和 "vsaas" 账号认证
if (strcmp(account, gAvAccount) != 0 && strcmp(account, "vsaas") != 0) {
return -1; // 账号不匹配
}
// 密码缓冲区长度校验
if (pwd_buf_size <= strlen(gAvPassword)) {
return -1; // 缓冲区不足
}
// 填充密码
strcpy(pwd, gAvPassword);
return 0; // 认证成功
}说明:gAvAccount、gAvPassword 为设备端默认音视频认证账号密码
六、优化策略(实时流模式)
实时流默认云端拉取 30s 流,设备端可通过智能算法(如人形识别、宠物识别)动态调整存储时长,节省云存储空间。
示例场景:10:00:00 侦测到人形→通知云端取流;10:00:10 人形消失→停止送流→关闭连接(最终存储10s)。
设备端操作步骤:
1. 识别连接来源:判断 IOTC 连线是否为云服务器发起(通过账号 "vsaas" 区分); 2. 标记 avIndex:记录该连接对应的音视频流索引(avIndex); 3. 动态停止存储:检测到无需继续存储时,停止向该 avIndex 推送音视频数据; 4. 安全关闭连接:等待 avIndex 中缓存的流全部发送完成后,关闭连接。
