帮你快速理解、总结文档立即下载

设备端接入

最近更新时间:2026-10-10 14:07:32
我的收藏
本文档将介绍如何在设备端接入云存储能力。设备完成接入后,可将设备采集的音视频以全时录制、事件录制、事件抓拍等方式保存至云端,供用户随时在应用端查看。

前提条件

在接入云存储之前,请先完成以下工作(已完成的可跳过):
1. 在开始之前,请您先完成 登录与注册 文档中跑通 tc_iot_login 登录功能。
2. 移步至 云存开通服务 为设备购买云存服务套餐:
单通道设备:直接购买即可,无需指定通道号,默认使用通道 ID 为0。
多通道设备:按实际通道号分别购买对应通道的云存储套餐。
3. 设备已具备采集并编码音视频帧的能力。
注意:
通道号一致性原则:创建音视频通道、推帧、调用云存储 API 时传入的 channel_id,须与已开通套餐的通道号保持一致。后续各步骤中涉及的 channel_id 参数均遵循此原则。

接入步骤

云存储接入以「初始化 → 持续推帧 → 按套餐触发录制/抓拍」为主线。数据处理、上传、云端上报等均由 SDK 自动完成,业务侧主要负责:注册回调、持续推帧、按需触发事件录制/抓拍。

步骤 1:初始化云存储并注册回调

调用 tc_iot_cloud_storage_init,传入回调集合 tc_iot_cloud_storage_callback_t。初始化的结果通过 on_init_result 回调返回。
tc_iot_cloud_storage_callback_t 回调说明:
回调
说明
on_init_result
必填,初始化结果(含套餐查询结果)。error_code == TC_IOT_ERR_SUCCESS 表示成功。
on_event_result
可选,事件处理结果,返回事件上报结果 event_report_result 与图片上传结果 picture_upload_result。
on_plan_type_changed
可选,云端云存储套餐类型发生变化时回调,业务可据此调整录制策略和控制推流。
static void on_cs_init_result(tc_iot_error_e error_code, const char *error_message) {
if (error_code == TC_IOT_ERR_SUCCESS) {
printf("cloud storage init success\\n");
} else {
printf("cloud storage init failed: %d, %s\\n", error_code,
error_message ? error_message : "");
}
}

static void on_cs_event_result(uint8_t channel_id,
const tc_iot_cloud_storage_event_result_t *result) {
printf("event result|ch:%u|event_id:%u|report:%d|pic:%d\\n", channel_id,
result->event_id, result->event_report_result, result->picture_upload_result);
}

static void on_cs_plan_type_changed(uint8_t channel_id,
tc_iot_cloud_storage_plan_type_t plan_type) {
printf("plan type changed|ch:%u|plan:%d\\n", channel_id, plan_type);
}

void cloud_storage_init_sample(void) {
tc_iot_cloud_storage_callback_t cb = {0};
cb.on_init_result = on_cs_init_result;
cb.on_event_result = on_cs_event_result;
cb.on_plan_type_changed = on_cs_plan_type_changed;

tc_iot_error_e err = tc_iot_cloud_storage_init(&cb);
if (err != TC_IOT_ERR_SUCCESS) {
printf("init call failed: %d\\n", err);
}
}

步骤 2:查询云存储套餐类型

tc_iot_cloud_storage_get_plan_type 返回指定 channel_id 对应的视频套餐类型。业务侧根据实际需要查询已开通套餐的通道号,不同套餐类型决定了可调用的录制接口范围:
枚举值
说明
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_UNKNOWN
未知(未初始化 / 未获取到套餐)。
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_NONE
无套餐。
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_FULL_TIME
全时录制套餐。支持全时录制、事件录制、事件抓拍(见 步骤 5、步骤 6、步骤 7)。
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_EVENT
事件录制套餐。支持事件录制、事件抓拍(见 步骤 6、步骤 7)。
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_IMAGE
图片套餐。仅支持事件抓拍(见 步骤 7)。
说明:
on_init_result 回调通知云存初始化成功后调用此接口。
tc_iot_cloud_storage_plan_type_t plan =
tc_iot_cloud_storage_get_plan_type(/*channel_id*/ 0);

步骤 3:创建 / 复用音视频通道

云存储录制的帧来自音视频通道(AV Channel)——设备端采集编码后的音视频帧统一推送到此通道,云存储与实时监控/通话共享同一路帧数据。因此:只需推送一次,即可同时用于实时监控与云存储录制,无需为云存储单独喂帧。
通道用 tc_iot_av.h 的接口创建: tc_iot_create_video_channel 参数说明:
参数
说明
channel_id
通道号,对应设备的一路摄像头。
quality
视频清晰度,取值参考 tc_iot_video_quality_e。
通道用 tc_iot_av.h 的接口创建:
// 视频通道:每路摄像头一个 channel_id,各自独立创建
tc_iot_av_channel_t *video_channel =
tc_iot_create_video_channel(/*channel_id*/ 0, TC_IOT_VIDEO_QUALITY_HD);

// 音频通道:麦克风唯一,全进程只创建一次,多路共享
tc_iot_av_channel_t *audio_channel = tc_iot_create_audio_channel();
说明:
音视频通道是设备端上行帧的统一入口,云存储与实时监控 / 通话共用同一路通道。
若设备已接入实时监控 / 通话,av channel 已经创建、帧也已经在推,则云存储直接复用即可,无需再次创建 av channel、也无需再起一路推流。只要保证对应 channel_id 上持续有帧,云存储便能在调用对应的接口后从 av channel 中获取到数据完成录制。
只有当某路通道尚未被实时监控 / 通话创建(例如纯云存储、无人观看的场景)时,才由业务侧按上面的方式创建并推帧。
一句话概括:一路摄像头只创建一次通道、只推一次流,实时监控与云存储各取所需,切勿为云存储重复建通道 / 重复推流。
创建得到的 video_channel / audio_channel 句柄用于步骤 4 的推帧,并在退出时 (步骤 7)销毁。

步骤 4:向音视频通道推送编码帧

云存储录制依赖音视频通道中的编码帧。业务侧需持续把编码帧推入对应 channel_id:
tc_iot_push_video_frame / tc_iot_push_audio_frame 的第一个参数为 步骤 3 创建的通道句柄,第二个参数为待推送的编码帧。
tc_iot_video_frame 字段说明:
字段
说明
codec
视频编码格式,取值 TC_IOT_VIDEO_CODEC_H264 / TC_IOT_VIDEO_CODEC_H265 / TC_IOT_VIDEO_CODEC_MJPEG。
type
帧类型,TC_IOT_VIDEO_FRAME_TYPE_IDR(关键帧)或 TC_IOT_VIDEO_FRAME_TYPE_P(P 帧)。
rotation
画面旋转角度,取值 TC_IOT_VIDEO_ROTATION_0 / 90 / 180 / 270。
width
视频宽度(像素)。
height
视频高度(像素)。
data
编码后视频帧数据指针。
data_size
数据字节数。
pts_ms
帧显示时间戳(毫秒)。
tc_iot_audio_frame 字段说明:
字段
说明
codec
音频编码格式,取值 TC_IOT_AUDIO_CODEC_PCM / AAC / OPUS / G722。
sample_rate
采样率,取值 TC_IOT_AUDIO_SAMPLE_RATE_8000 / 16000 / 32000 / 48000。
channels
声道数,TC_IOT_AUDIO_CHANNEL_MONO(单声道)或 TC_IOT_AUDIO_CHANNEL_STEREO(立体声)。
frame_duration_ms
单帧时长(毫秒)。
data
编码后音频帧数据指针。
data_size
数据字节数。
pts_ms
帧时间戳(毫秒)。
说明:
云存音频格式支持 PCM 和 AAC
// 视频帧
tc_iot_video_frame vframe = {0};
vframe.codec = TC_IOT_VIDEO_CODEC_H264;
vframe.type = is_idr ? TC_IOT_VIDEO_FRAME_TYPE_IDR : TC_IOT_VIDEO_FRAME_TYPE_P;
vframe.width = 1920;
vframe.height = 1080;
vframe.data = encoded_data;
vframe.data_size = encoded_size;
vframe.pts_ms = current_time_ms();
tc_iot_push_video_frame(video_channel, &vframe);

// 音频帧
tc_iot_audio_frame aframe = {0};
aframe.codec = TC_IOT_AUDIO_CODEC_AAC;
aframe.sample_rate = TC_IOT_AUDIO_SAMPLE_RATE_16000;
aframe.channels = TC_IOT_AUDIO_CHANNEL_MONO;
aframe.data = audio_data;
aframe.data_size = audio_size;
aframe.pts_ms = current_time_ms();
tc_iot_push_audio_frame(audio_channel, &aframe);
注意:
视频流必须包含 IDR 帧。云存储起录时会从最近的 IDR 帧开始,若缺少 IDR 帧会导致无法起录或录制内容不可正常播放。

步骤 5:全时录制

对于全时录制套餐,调用以下接口开启/停止指定 channel 的持续录制。开启后,SDK 会自动录制并上传,业务侧只需保证持续推帧:
tc_iot_cloud_storage_start_continuous_recording(/*channel_id*/ 0);
// ... 录制期间 SDK 自动录制上传 ...
tc_iot_cloud_storage_stop_continuous_recording(/*channel_id*/ 0);
说明:
channel_id 合法范围为 [0, 4],用于支持多路摄像头设备,需与创建通道时的通道号、开通套餐的通道号保持一致。
适用套餐:FULL_TIME(全时录制套餐)

步骤 6:事件录制

发生告警、移动侦测等事件时,触发事件录制。可携带事件封面图与预录时长(从事件发生前若干秒开始录制):
tc_iot_cloud_storage_event_params_t 字段说明:
字段
说明
event_id
必填。事件 ID,业务侧生成,用于关联开始/停止与结果回调。
picture_data
事件封面/抓拍图数据指针,on_event_result 回调返回前需保持有效。
picture_size
图片字节数(与 picture_data 配合使用)。
pre_record_seconds
预录时长(秒),从事件发生前若干秒起录。最大值5秒。
extra_info
业务自定义附加信息(JSON 格式)。
说明:
channel_id 合法范围为 [0, 4],用于支持多路摄像头设备,需与创建 av 通道时的通道号、开通套餐的通道号保持一致。
适用套餐:FULL_TIME(全时录制套餐)、EVENT(事件录制套餐)。
tc_iot_cloud_storage_event_params_t params = {0};
params.event_id = generate_event_id();
params.picture_data = jpeg_data; // 事件封面图(可选)
params.picture_size = jpeg_size;
params.pre_record_seconds = 5; // 预录 5 秒
params.extra_info = "{\\"type\\":\\"motion\\"}";

tc_iot_cloud_storage_start_event_recording(/*channel_id*/ 0, &params);
// ... 事件持续期间 SDK 自动录制上传 ...
tc_iot_cloud_storage_stop_event_recording(/*channel_id*/ 0, params.event_id);

步骤 7:事件抓拍

对于图片套餐,仅需上报一张事件抓拍图,无需录制视频,图片上传与事件消息上报结果通过 on_event_result 回调返回。
tc_iot_cloud_storage_event_params_t params = {0};
params.event_id = generate_event_id();
params.picture_data = jpeg_data;
params.picture_size = jpeg_size;
params.extra_info = "{\\"type\\":\\"snapshot\\"}";

tc_iot_cloud_storage_report_event_snapshot(/*channel_id*/ 0, &params);
说明:
channel_id 合法范围为 [0, 4],用于支持多路摄像头设备,需与创建 av 通道时的通道号、开通套餐的通道号保持一致。
适用套餐:FULL_TIME(全时录制套餐)、EVENT(事件录制套餐)、IMAGE(图片套餐)。

步骤 8:反初始化

设备不再使用云存储时释放资源:
// 1. 反初始化云存储
tc_iot_cloud_storage_deinit();

// 2. 销毁步骤 3 中创建的音视频通道
// 注意:若通道由实时监控 / 通话复用,则应在其结束回调中统一销毁,此处勿重复销毁
tc_iot_destroy_video_channel(video_channel);
tc_iot_destroy_audio_channel(audio_channel);

完整示例代码

#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

#include "tc_iot.h"
#include "tc_iot_av.h"
#include "tc_iot_cloud_storage.h"
#include "tc_iot_def.h"

static volatile int s_login_done = 0;
static tc_iot_error_e s_login_rc = TC_IOT_ERR_SUCCESS;
static volatile int s_cloud_storage_init_done = 0;
static tc_iot_error_e s_cloud_storage_init_rc = TC_IOT_ERR_SUCCESS;
static volatile int s_event_result_done = 0;

static volatile bool g_pushing = false;
static pthread_t g_video_thread, g_audio_thread;
static tc_iot_av_channel_t *g_video_channel = NULL;
static tc_iot_av_channel_t *g_audio_channel = NULL;
static volatile int g_running = 1;

static uint8_t s_pic_data[128] = {0xFF, 0xD8, 0x00, 0x00, 0xFF, 0xD9};

/* 最小 H264 假帧(AnnexB):真实 SPS+PPS 过 avcC 校验,伪 IDR/P slice 仅过 SDK
* 校验, 录制文件不可解码;数组末尾多留几字节 0 吸收打包器 NAL 长度偏差。 */
static uint8_t s_h264_idr_frame[48] = {
0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xC0, 0x0A, 0xDA, 0x0A, 0x36,
0xC0, 0x44, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00,
0x50, 0x3C, 0x48, 0x9A, 0x80, 0x00, 0x00, 0x00, 0x01, 0x68, 0xCE,
0x01, 0x97, 0x20, 0x00, 0x00, 0x01, 0x65, 0x00,
};
static uint8_t s_h264_p_frame[12] = {
0x00, 0x00, 0x01, 0x41, 0x00,
};
#define S_H264_IDR_FRAME_SIZE 41
#define S_H264_P_FRAME_SIZE 5

static tc_iot_cloud_storage_plan_type_t s_plan_type =
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_UNKNOWN;

static void *video_push_thread_func(void *arg);
static void *audio_push_thread_func(void *arg);

static uint64_t current_time_ms(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static void on_login(tc_iot_error_e error_code, const char *error_message) {
s_login_rc = error_code;
s_login_done = 1;
if (error_code != TC_IOT_ERR_SUCCESS) {
printf("[demo] login failed: %d, msg=%s\\n", error_code,
error_message ? error_message : "");
} else {
printf("[demo] login success\\n");
}
}

static void on_cloud_storage_init_result(tc_iot_error_e error_code,
const char *error_message) {
s_cloud_storage_init_rc = error_code;
s_cloud_storage_init_done = 1;
if (error_code != TC_IOT_ERR_SUCCESS) {
printf("[demo] cloud storage init failed: %d, msg=%s\\n", error_code,
error_message ? error_message : "");
return;
}
printf("[demo] cloud storage init success\\n");

/* 查询套餐类型 */
s_plan_type = tc_iot_cloud_storage_get_plan_type(0);
printf("[demo] plan type: %d\\n", s_plan_type);
}

static void on_cloud_storage_event_result(
uint8_t channel_id,
const tc_iot_cloud_storage_event_result_t *event_result) {
printf("[demo] event result: ch=%d, ev_id=%u, report=%d, pic=%d\\n",
channel_id, event_result->event_id, event_result->event_report_result,
event_result->picture_upload_result);
s_event_result_done = 1;
}

static void on_plan_type_changed(uint8_t channel_id,
tc_iot_cloud_storage_plan_type_t plan_type) {
printf("[demo] plan type changed: ch=%d, plan=%d\\n", channel_id, plan_type);
s_plan_type = plan_type;
}

/* 推流线程 */
static void *video_push_thread_func(void *arg) {
(void)arg;
uint32_t frame_count = 0;

while (g_pushing) {
if (g_video_channel) {
/* 模拟编码器 GOP=50 帧(2s):周期推 IDR,保证云存按 10s 切片对齐 */
bool is_idr = (frame_count % 50 == 0);
tc_iot_video_frame vframe;
memset(&vframe, 0, sizeof(vframe));
vframe.codec = TC_IOT_VIDEO_CODEC_H264;
vframe.type =
is_idr ? TC_IOT_VIDEO_FRAME_TYPE_IDR : TC_IOT_VIDEO_FRAME_TYPE_P;
vframe.rotation = TC_IOT_VIDEO_ROTATION_0;
vframe.width = 160;
vframe.height = 96;
vframe.data = is_idr ? s_h264_idr_frame : s_h264_p_frame;
vframe.data_size = is_idr ? S_H264_IDR_FRAME_SIZE : S_H264_P_FRAME_SIZE;
vframe.pts_ms = current_time_ms();

tc_iot_push_video_frame(g_video_channel, &vframe);
frame_count++;
}
usleep(40 * 1000);
}
return NULL;
}

static void *audio_push_thread_func(void *arg) {
(void)arg;
static uint8_t dummy_audio[640] = {0};

while (g_pushing) {
if (g_audio_channel) {
tc_iot_audio_frame aframe;
memset(&aframe, 0, sizeof(aframe));
aframe.codec = TC_IOT_AUDIO_CODEC_PCM;
aframe.sample_rate = TC_IOT_AUDIO_SAMPLE_RATE_16000;
aframe.channels = TC_IOT_AUDIO_CHANNEL_MONO;
aframe.frame_duration_ms = 20;
aframe.data = dummy_audio;
aframe.data_size = sizeof(dummy_audio);
aframe.pts_ms = current_time_ms();

tc_iot_push_audio_frame(g_audio_channel, &aframe);
}
usleep(20 * 1000);
}
return NULL;
}

static int start_pushing(void) {
if (g_pushing)
return 0;

g_video_channel = tc_iot_create_video_channel(0, TC_IOT_VIDEO_QUALITY_HD);
g_audio_channel = tc_iot_create_audio_channel();
if (!g_video_channel || !g_audio_channel) {
printf("[demo] create AV channels failed\\n");
return -1;
}

g_pushing = true;
pthread_create(&g_video_thread, NULL, video_push_thread_func, NULL);
pthread_create(&g_audio_thread, NULL, audio_push_thread_func, NULL);
return 0;
}

static void stop_pushing(void) {
if (!g_pushing)
return;

g_pushing = false;
pthread_join(g_video_thread, NULL);
pthread_join(g_audio_thread, NULL);

if (g_video_channel) {
tc_iot_destroy_video_channel(g_video_channel);
g_video_channel = NULL;
}
if (g_audio_channel) {
tc_iot_destroy_audio_channel(g_audio_channel);
g_audio_channel = NULL;
}
}

static void signal_handler(int sig) {
printf("\\n[demo] signal %d, shutting down...\\n", sig);
g_running = 0;
}

int main(int argc, char **argv) {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

if (argc < 7) {
printf("Usage: %s -p <product_id> -d <device_id> -s <device_secret>\\n",
argv[0]);
return 0;
}

const char *product_id = NULL, *device_id = NULL, *device_secret = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-p") == 0 && i + 1 < argc)
product_id = argv[++i];
else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc)
device_id = argv[++i];
else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc)
device_secret = argv[++i];
}

if (!product_id || !device_id || !device_secret) {
printf("device info error\\n");
return 1;
}

/* 步骤1: SDK初始化 */
tc_iot_config_s config;
memset(&config, 0, sizeof(config));
config.storage_path = "./";
config.log_level = TC_IOT_LOG_LEVEL_INFO;
config.region = "ap-guangzhou";

tc_iot_error_e rc = tc_iot_init(&config);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("[ERROR] tc_iot_init failed: %d\\n", rc);
return 1;
}

/* 步骤2: 登录 */
tc_iot_device_info_s device_info;
memset(&device_info, 0, sizeof(device_info));
device_info.product_id = product_id;
device_info.device_id = device_id;
device_info.device_secret = device_secret;

rc = tc_iot_login(&device_info, on_login);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("[ERROR] tc_iot_login failed: %d\\n", rc);
tc_iot_deinit();
return 1;
}

int waited_ms = 0;
while (!s_login_done && waited_ms < 10000) {
usleep(100 * 1000);
waited_ms += 100;
}
if (!s_login_done || s_login_rc != TC_IOT_ERR_SUCCESS) {
printf("[demo] login failed or timeout\\n");
tc_iot_deinit();
return 1;
}

/* 步骤3: 云存初始化 */
tc_iot_cloud_storage_callback_t cs_callback;
memset(&cs_callback, 0, sizeof(cs_callback));
cs_callback.on_init_result = on_cloud_storage_init_result;
cs_callback.on_event_result = on_cloud_storage_event_result;
cs_callback.on_plan_type_changed = on_plan_type_changed;

rc = tc_iot_cloud_storage_init(&cs_callback);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("[ERROR] tc_iot_cloud_storage_init failed: %d\\n", rc);
tc_iot_logout();
tc_iot_deinit();
return 1;
}

waited_ms = 0;
while (!s_cloud_storage_init_done && waited_ms < 10000) {
usleep(100 * 1000);
waited_ms += 100;
}
if (!s_cloud_storage_init_done ||
s_cloud_storage_init_rc != TC_IOT_ERR_SUCCESS) {
printf("[demo] cloud storage init failed or timeout\\n");
tc_iot_cloud_storage_deinit();
tc_iot_logout();
tc_iot_deinit();
return 1;
}

/* 步骤4: FULL_TIME/EVENT 需要推流 */
if (s_plan_type == TC_IOT_CLOUD_STORAGE_PLAN_TYPE_FULL_TIME ||
s_plan_type == TC_IOT_CLOUD_STORAGE_PLAN_TYPE_EVENT) {
if (start_pushing() != 0) {
printf("[demo] start pushing failed, exit.\\n");
stop_pushing();
tc_iot_cloud_storage_deinit();
tc_iot_logout();
tc_iot_deinit();
return 1;
}
}

/* 步骤5: 按套餐类型调用对应接口 */
if (s_plan_type == TC_IOT_CLOUD_STORAGE_PLAN_TYPE_FULL_TIME) {
printf("[demo] Plan: FULL_TIME\\n");

rc = tc_iot_cloud_storage_start_continuous_recording(0);
if (rc == TC_IOT_ERR_SUCCESS) {
printf("[demo] recording 30s...\\n");
int elapsed = 0;
while (g_running && elapsed < 30) {
sleep(1);
elapsed++;
}
tc_iot_cloud_storage_stop_continuous_recording(0);
printf("[demo] recording stopped.\\n");
} else {
printf("[ERROR] start_continuous_recording failed: %d\\n", rc);
}
stop_pushing();

} else if (s_plan_type == TC_IOT_CLOUD_STORAGE_PLAN_TYPE_EVENT) {
printf("[demo] Plan: EVENT\\n");

uint32_t event_id = (uint32_t)(current_time_ms() / 1000);

tc_iot_cloud_storage_event_params_t event_params;
memset(&event_params, 0, sizeof(event_params));
event_params.event_id = event_id;
event_params.picture_data = s_pic_data;
event_params.picture_size = sizeof(s_pic_data);
event_params.pre_record_seconds = 5;
event_params.extra_info = "motion_detected";

rc = tc_iot_cloud_storage_start_event_recording(0, &event_params);
if (rc == TC_IOT_ERR_SUCCESS) {
printf("[demo] event recording 15s (id=%u)...\\n", event_id);
int elapsed = 0;
while (g_running && elapsed < 15) {
sleep(1);
elapsed++;
}
tc_iot_cloud_storage_stop_event_recording(0, event_id);

s_event_result_done = 0;
waited_ms = 0;
while (!s_event_result_done && waited_ms < 10000) {
usleep(100 * 1000);
waited_ms += 100;
}
printf("[demo] event recording done.\\n");
} else {
printf("[ERROR] start_event_recording failed: %d\\n", rc);
}
stop_pushing();

} else if (s_plan_type == TC_IOT_CLOUD_STORAGE_PLAN_TYPE_IMAGE) {
printf("[demo] Plan: IMAGE\\n");

uint32_t event_id = (uint32_t)(current_time_ms() / 1000);

tc_iot_cloud_storage_event_params_t snap_params;
memset(&snap_params, 0, sizeof(snap_params));
snap_params.event_id = event_id;
snap_params.picture_data = s_pic_data;
snap_params.picture_size = sizeof(s_pic_data);
snap_params.extra_info = "doorbell_pressed";

rc = tc_iot_cloud_storage_report_event_snapshot(0, &snap_params);
if (rc == TC_IOT_ERR_SUCCESS) {
s_event_result_done = 0;
waited_ms = 0;
while (!s_event_result_done && waited_ms < 10000) {
usleep(100 * 1000);
waited_ms += 100;
}
printf("[demo] snapshot done (id=%u)\\n", event_id);
} else {
printf("[ERROR] report_event_snapshot failed: %d\\n", rc);
}

} else {
printf("[demo] Plan: NONE/UNKNOWN, no valid plan.\\n");
sleep(60);
}

stop_pushing();
tc_iot_cloud_storage_deinit();
tc_iot_logout();
tc_iot_deinit();
printf("[demo] done.\\n");
return 0;
}

编译与运行

将上述代码保存为 main.c,在 SDK 目录下执行:
1. 编译。
cmake -B build
cmake --build build -j
2. 运行可执行文件,传入设备三元组。
./cloud_storage_demo -p YOUR_PRODUCT_ID -d YOUR_DEVICE_ID -s 'YOUR_DEVICE_SECRET'
观察日志输出 cloud storage init success 及对应套餐类型的录制/事件日志,即代表云存储接入成功。

常见问题

问题
排查方向
on_init_result 回调初始化失败。
检查设备是否已登录成功;检查网络连接是否正常。
录制后 App 无法回看或播放失败。
确认已持续调用 tc_iot_push_video_frame 推帧,且视频流包含 IDR 帧;确认套餐类型与调用的接口匹配。
调用录制接口无响应或报错。
确认当前套餐类型支持该接口(如 NONE 套餐不支持任何录制);确认传入的 channel_id 已开通对应套餐。
事件图片上传失败。
确认 picture_data 在 on_event_result 回调前保持有效;确认套餐已开通对应能力。
事件结果回调未触发。
确认事件录制已正确调用对应的 stop 接口;确认 event_id 未重复使用。
多路设备录制混乱。
确认各通道的 channel_id 与推帧的通道一一对应,且不超过合法范围 [0, 4]。

联系我们

如果您在接入或使用过程中有任何疑问或者建议,欢迎 联系我们 提交反馈。