本文档将介绍如何在设备端接入云存储能力。设备完成接入后,可将设备采集的音视频以全时录制、事件录制、事件抓拍等方式保存至云端,供用户随时在应用端查看。
前提条件
在接入云存储之前,请先完成以下工作(已完成的可跳过):
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 | |
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_EVENT | |
TC_IOT_CLOUD_STORAGE_PLAN_TYPE_IMAGE |
说明:
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 中获取到数据完成录制。只有当某路通道尚未被实时监控 / 通话创建(例如纯云存储、无人观看的场景)时,才由业务侧按上面的方式创建并推帧。
一句话概括:一路摄像头只创建一次通道、只推一次流,实时监控与云存储各取所需,切勿为云存储重复建通道 / 重复推流。
步骤 4:向音视频通道推送编码帧
云存储录制依赖音视频通道中的编码帧。业务侧需持续把编码帧推入对应 channel_id:
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, ¶ms);// ... 事件持续期间 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, ¶ms);
说明:
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 5static 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 buildcmake --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]。 |