物联网开发平台 设备端接入

这篇档将介绍如何在设备端接入云存储能力。设备完成接入后,可将设备采集的音视频以全时录制、事件录制、事件抓拍等方式保存至云端,供用户随时在应用端查看。

相关产品
物联网开发平台 产品页,含版本规格、价格与购买入口。

一、前提条件

在接入云存储之前,请先完成以下工作(已完成的可跳过):

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();

注意:说明:

注意:音视频通道是设备端上行帧的统一入口,云存储与实时监控 / 通话共用同一路通道。

注意:一句话概括:一路摄像头只创建一次通道、只推一次流,实时监控与云存储各取所需,切勿为云存储重复建通道 / 重复推流。

注意:创建得到的 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 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]。

十四、联系我们

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

赞 (0)
上一篇 4天前
下一篇 4天前