简体中文

基于AVAPIs模块开发网络摄像机-设备端

星空无限传媒xk8027-高清全集免费看

一、相关头文件说明

头文件说明
AVFRAMEINFO.h帧信息定义
AVIOCTRLDEFs.hIO控制指令定义
TUTKGlobalAPIs.h全局的设定
IOTCClient.h客户端连线相关专用接口定义
IOTCCommon.h通用接口,默认值,错误码定义
IOTCDevice.h设备端连线相关专用接口定义
AVClient.h客户端数据发送相关接口定义
AVCommon.h通用接口,默认值,错误码定义
AVServer.h设备端数据发送相关接口定义
VSaaS.h云存储相关接口定义
DASA.h码流自适应接口定义

二、相关demo路径

平台例子路径
AndroidSDK目录\Sample\Android\Sample_AVAPIs_Server
LinuxSDK目录\Sample\Linux\Sample_AVAPIs

三、常见的配网方式

可以参考:常见的配网方式

四、登录、监听和访问处理

AV Server 工作流程示意图

图:AV Server 工作流程示意图

主要使用的API: IOTCAPIs+AVAPIs

(一)登录

设备在上电后进行IOTC_Device_LoginEx,如果失败,需要重复调用,直到成功为止。此接口同时会开启局域网搜索功能,即使没有login成功,客户端也可以通过路由器下通过IOTC_Lan_Search等API搜索到,可以通过IOTC_Connect_ByUIDEx等API进行连线。

login成功后,就不再需要调用IOTC_Device_LoginEx,可以继续在此线程,调用IOTC_Get_LoginInfo来检查设备端是否从服务器掉线。

// 设备登录配置参数 DeviceLoginInput auth_option; printf("thread_Login start\n"); // 在程序运行状态下循环尝试登录 while (gProcessRun) {    // 配置登录参数    auth_option.cb = sizeof(DeviceLoginInput);    auth_option.authentication_type = AUTHENTICATE_BY_KEY;    memcpy(auth_option.auth_key, gIotcAuthkey, IOTC_AUTH_KEY_LENGTH);    // 尝试设备登录(使用带密钥的扩展登录函数)    // 如不使用authkey,可替换为IOTC_Device_Login函数    int ret = IOTC_Device_LoginEx((char*)arg, &auth_option);        printf("IOTC_Device_LoginEx() ret = %d\n", ret);    // 登录成功则退出循环    if (ret == IOTC_ER_NoERROR) {        break;    }    // 登录失败则处理错误并重试    else {        PrintErrHandling(ret);                // 等待5秒后重试(可被程序退出信号中断)        for (int sleep_count = 0; sleep_count < 5 && gProcessRun; sleep_count++) {            sleep(1);        }    } }
1. 登录状态检查(基于返回值判断)

IOTC_Get_Login_Info用以检查login状态,可以拿到两个值。返回值表示 try login失败的次数,输出参数 login_state 为7表示正常在login,并且有收到P2P服务器回应的login response。

/* * IOTC_Get_Login_Info用以检查login状态,可以拿到两个值。 * 返回值表示 try login失败的次数 * 输出参数 login_state 为7表示正常在login,并且有收到P2P服务器回应的login response。 */ unsigned int login_state; int login_fail_count = 0; // 循环检查登录状态(程序运行期间) while (gProcessRun) {    // 获取登录状态信息    int ret = IOTC_Get_Login_Info(&login_state);    printf("IOTC_Get_Login_Info() ret = %d, login_state = %u\n", ret, login_state);    // 处理获取状态失败的情况    if (ret < IOTC_ER_NoERROR) {        break;    }    // 处理多次登录重试失败的情况(与服务器失联)    if (ret > 4) {        // 当无用户访问且网络畅通时(网络畅通可通过ping等方式检测)        if (/* 无用户访问条件 */ && /* 网络畅通条件 */) {            printf("device logout from p2p server, will call IOTC_Listen_Exit\n"); #ifdef _RESTART_P2P_MODULE_            // 重启P2P模块:调用后IOTC_Listen会返回IOTC_ER_EXIT_LISTEN            IOTC_Listen_Exit();            break; #elif defined(_REINIT_SOCKET_)            // 仅重置底层socket,不重新登录            IOTC_ReInitSocket(0);            continue; #elif defined(_FORCE_LOGIN_)            // 重置socket并强制重新登录(无需重新初始化)            IOTC_ReInitSocket(0);            IOTC_Device_Update_Authkey(device_authkey);            continue; #endif        }    }    // 5秒后再次检查(可被程序退出信号中断)    for (int sleep_count = 0; sleep_count < 5 && gProcessRun; sleep_count++) {        sleep(1);    } }
2. 登录状态检查(基于login_state判断)
unsigned int login_state; int login_fail_count = 0; // 循环检查登录状态(程序运行期间) while (gProcessRun) {    // 获取登录状态信息    int ret = IOTC_Get_Login_Info(&login_state);    printf("IOTC_Get_Login_Info() ret = %d, login_state = %u\n", ret, login_state);    // 处理获取状态失败的情况    if (ret < IOTC_ER_NoERROR) {        break;    }    // 检查登录状态(7表示与P2P服务器通信正常)    if (login_state == 7) {        // 状态正常,重置失败计数器        login_fail_count = 0;    } else {        // 状态异常,递增失败计数器        if (++login_fail_count > 10) { // 超过10次检查异常(约50秒)            // 当无用户访问且网络畅通时(网络畅通可通过ping等方式检测)            if (/* 无用户访问条件 */ && /* 网络畅通条件 */) {                printf("device logout from p2p server, will call IOTC_Listen_Exit\n"); #ifdef _RESTART_P2P_MODULE_                // 重启P2P模块:调用后IOTC_Listen会返回IOTC_ER_EXIT_LISTEN                IOTC_Listen_Exit();                break; #elif defined(_REINIT_SOCKET_)                // 仅重置底层socket,不重新登录                IOTC_ReInitSocket(0);                continue; #elif defined(_FORCE_LOGIN_)                // 重置socket并强制重新登录(无需重新初始化)                IOTC_ReInitSocket(0);                IOTC_Device_Update_Authkey(device_authkey);                continue; #endif            }        }    }    // 5秒后再次检查(可被程序退出信号中断)    for (int sleep_count = 0; sleep_count < 5 && gProcessRun; sleep_count++) {        sleep(1);    } }
提示:如遇到一些小运营商的网络不稳定的情况,建议使用4.3.6.x以上的SDK,并在初始化之前调用IOTC_Device_Setup_Network_Adaption(IOTC_DEVICE_ADAPTION_MODE_CHOOSE_BEST_UDP_SERVER_OR_SWITCH_TO_TCP)开启TCP自适应。
(二)监听
unsigned char need_reboot = 0; // 主循环:处理连接请求 while (gProcessRun) {    // 监听连接请求(超时时间1000ms)    int SID = IOTC_Listen(1000);        if (SID < 0) {        // 处理监听错误        PrintErrHandling(SID);                // 超过最大会话数        if (SID == IOTC_ER_EXCEED_MAX_SESSION) { #if TEST_FAST_RECOVERY            // 快速恢复模式:等待5秒检查是否仍达最大在线人数            for (int i = 0; i < 5 && /* 在线人数已经达到最大 */; i++) {                sleep(1);            } #else            // 普通模式:等待5秒后重试            sleep(5); #endif            continue;        }        // 监听超时(正常情况,继续监听)        else if (SID == AV_ER_TIMEOUT) {            continue;        }        // 退出监听(需要重启)        else if (SID == IOTC_ER_EXIT_LISTEN) {            need_reboot = 1;            break;        }        // 其他异常(可能需要重启)        else {            // 程序异常处理逻辑        }    }    else {        // 成功获取会话ID,创建线程处理AV服务(avServStart是阻塞接口)        int* sid = (int*)malloc(sizeof(int));        *sid = SID;                pthread_t Thread_ID;        int ret = pthread_create(&Thread_ID, NULL, &thread_ForAVServerStart, (void*)sid);                if (ret < 0) {            printf("pthread_create failed ret[%d]\n", ret);            free(sid); // 创建线程失败时释放内存        } else {            pthread_detach(Thread_ID); // 分离线程,自动释放资源            gOnlineNum++; // 增加在线人数计数        }    } } // 处理重启逻辑 if (need_reboot) {    // 停止所有数据收发    // 等待所有连接退出    // 等待其他AV/IOTC相关API退出        // 反初始化AV和IOTC模块    avDeInitialize();    IOTC_DeInitialize();        // 跳转到程序开始处重新执行    goto BEGIN; }
(三)访问处理
1. 创建数据通道
// 初始化AV服务启动配置结构体 AVServStartInConfig avStartInConfig; AVServStartOutConfig avStartOutConfig; // 清空输入配置结构体,避免脏数据 memset(&avStartInConfig, 0, sizeof(AVServStartInConfig)); // -------------------------- 配置输入参数(核心参数)-------------------------- // 结构体大小(固定赋值,用于SDK版本兼容) avStartInConfig.cb                                  = sizeof(AVServStartInConfig); // 关联的IOTC会话ID(由IOTC_Listen返回) avStartInConfig.iotc_session_id                     = SID; // 数据通道使用的IOTC通道ID(默认0为主通道) avStartInConfig.iotc_channel_id                     = 0; // 通道创建超时时间(单位:秒) avStartInConfig.timeout_sec                         = 30; // 密码验证回调函数(用于客户端密码校验) avStartInConfig.password_auth                       = ExPasswordAuthCallBackFn; // 设备端功能限制(0表示不限制,具体参考SDK文档的AV Module Service Type Definition) avStartInConfig.server_type                         = 0; // 重传模式配置(1=开启重传,2=关闭重传) avStartInConfig.resend                              = 1; // 非必须回调:修改密码请求(按需实现) avStartInConfig.change_password_request = ExChangePasswordCallBackFn; // 非必须回调:设备能力查询(按需实现) avStartInConfig.ability_request                     = ExAbilityRequestFn; // -------------------------- 条件配置:Token认证(按需启用)-------------------------- #if ENABLE_TOKEN_AUTH // 当APP端选择Token认证时,启用以下回调 avStartInConfig.token_auth                         = ExTokenAuthCallBackFn;    // Token验证回调 avStartInConfig.token_delete                       = ExTokenDeleteCallBackFn; // Token删除回调 avStartInConfig.token_request                      = ExTokenRequestCallBackFn; // Token申请回调 avStartInConfig.identity_array_request = ExGetIdentityArrayCallBackFn; // 已存在ID查询回调 #endif // -------------------------- 条件配置:安全加密模式 -------------------------- #if ENABLE_DTLS // 启用DTLS加密(推荐,安全性更高) avStartInConfig.security_mode = AV_SECURITY_DTLS; #else // 使用默认SIMPLE加密(兼容性优先) avStartInConfig.security_mode = AV_SECURITY_SIMPLE; #endif // -------------------------- 配置输出参数 -------------------------- // 结构体大小(固定赋值,用于接收SDK返回数据) avStartOutConfig.cb                                  = sizeof(AVServStartOutConfig); // -------------------------- 创建AV数据通道 -------------------------- // avIndex:返回的AV数据通道索引,后续数据操作基于此索引 int avIndex = avServStartEx(&avStartInConfig, &avStartOutConfig); if (avIndex < 0) {    printf("avServStartEx failed!! SID[%d] code[%d]!!!\n", SID, avIndex);    printf("thread_ForAVServerStart: exit!! SID[%d]\n", SID);        // 启动失败:关闭对应的IOTC会话,更新在线人数,退出线程    IOTC_Session_Close(SID);    gOnlineNum--;    pthread_exit(0); }

avServStartEx除了可以拿到avIndex,还可以拿到此通道相关的一些其他特性(不同版本会略有差异,以实际版本为准),具体如下:

/** * @brief AV服务启动输出配置结构体 * @details 用于接收 avServStartEx 接口启动AV服务后的返回信息,包含通道能力、认证方式、用户标识等关键结果 */ typedef struct AVServStartOutConfig {    uint32_t cb;    int32_t resend;//是否开启重传    int32_t two_way_streaming;//是否支持全双工    AvAuthType auth_type;//auth的类型    char account_or_identity[256];//自定义identity信息,如云存可能使用"vsaas"来标识 } AVServStartOutConfig; typedef AVServStartOutConfig * LPAVSERV_START_OUT_CONFIG;
提示:全双工的通道,在进行对讲的时候,不需要建立新的avIndex
2. 接收IO指令
// 循环接收并处理AV通道的IO控制指令 while (gProcessRun) {    // 接收IO控制指令:超时时间1000ms,缓冲区大小AV_MAX_IOCTRL_DATA_SIZE    int ret = avRecvIOCtrl(        avIndex,        &ioType,        (char*)ioCtrlBuf,        AV_MAX_IOCTRL_DATA_SIZE,        1000    );    // 处理接收结果    if (ret >= 0) {        // 成功接收指令:调用处理函数(支持图像、声音、对讲、回放等业务逻辑)        // 注:IO指令定义参考SDK文档「AV Module Reference of AV IO Control」,也可自定义格式        Handle_IOCTRL_Cmd(SID, avIndex, ioCtrlBuf, ioType);    }    else if (ret != AV_ER_TIMEOUT) {        printf("avIndex[%d], avRecvIOCtrl error, code[%d]\n", avIndex, ret);        break; // 错误退出循环,执行资源清理    } } // -------------------------- 资源清理与连接关闭流程 -------------------------- // 1. 停止发送IO控制数据 // 2. 关闭音视频发送(从音视频客户端列表中注销) unregedit_client_from_video(SID); unregedit_client_from_audio(SID); // 3. 关闭对讲功能(需确保对应资源已释放) // 4. 关闭回放功能(需确保对应资源已释放) // 5. 关闭AV通道与IOTC会话,更新在线人数 printf("SID[%d], avIndex[%d], thread_ForAVServerStart exit!!\n", SID, avIndex); // 关闭AV服务通道 avServStop(avIndex); // 关闭IOTC会话连接 IOTC_Session_Close(SID); // 减少在线人数计数 gOnlineNum--;
3. 发送视频
// 循环发送视频帧(程序运行期间持续执行) while (gProcessRun) {    char* frame; // 视频帧数据指针(当前代码未使用,可根据实际需求补充逻辑)    int size;    // 视频帧大小(当前代码未使用,可根据实际需求补充逻辑)    // 1. 从编码缓冲区取出图像帧(需补充实际取帧逻辑,如:frame = get_encoded_video_frame(&size);)    // 2. 轮询所有需要接收视频的AV通道,逐个发送视频帧    foreach (int _avIndex, avIndex_need_video) { // foreach:遍历需视频推送的avIndex列表        int avIndex = _avIndex; // 当前待发送的AV通道索引        // 发送视频帧数据(携带帧信息FRAMEINFO_t)        int ret = avSendFrameData(            avIndex,            videoBuf,    // 视频数据缓冲区(需确保数据有效)            videoSize,   // 视频数据大小            &frameInfo,  // 视频帧信息(如帧类型、分辨率等)            sizeof(FRAMEINFO_t) // 帧信息结构体大小        );        // 处理发送结果        if (ret < 0) {            // 情况1:缓冲区溢出(帧丢失,需标记后续发送关键帧避免花屏)            if (ret == AV_ER_EXCEED_MAX_SIZE) {                need_iframe_flag_by_avIndex = 1; // 标记该avIndex需后续补发I帧            }            // 情况2:其他发送异常(停止向该avIndex推送视频)            else {                unregedit_client_from_video(SID); // 从视频推送列表中注销该客户端                continue; // 跳过当前avIndex,处理下一个            }        }    } }
4. 动态码率配置

如果需要加上动态码率,可以参考以下规则:

  • (1)如果有多人在同时观看,且大部分是P2P/RLY模式,降低码率(或者分辨率);
  • (2)如果有某个通道重传使用率一直维持在较高水平,则降低码率。
5. 对讲实现

五、常见的问题以及处理

(一)如何监测传输状况?

通过独立线程定时检查AV通道缓冲区使用率,根据使用率分级处理,避免缓冲区溢出导致的帧丢失。

设备端需要定时调用avResendBufUsageRate检查对应avIndex的Resend Buffer使用率(使用率越高,说明数据累积越多)。

设备端在考虑码率自适应的时候,需要参考当前使用率(currentRate)相对于上一次(prevRate)的变化方向,判断传输状况是改善还是恶化:

  • currentRate小于prevRate:传输状况可能在改善;
  • currentRate大于prevRate:传输状况可能在恶化;
  • currentRate接近prevRate:状况维持,需持续观察。

可将currentRate划分为多个拥堵档位,示例如下:

  • currentRate ∈(0, 0.3f ] → CONGESTION_LEVEL_0;
  • currentRate ∈(0.3f, 0.5f ] → CONGESTION_LEVEL_1;
  • currentRate ∈(0.5f, 0.7f ] → CONGESTION_LEVEL_2;
  • currentRate ∈(0.7f, 0.9f ] → CONGESTION_LEVEL_3;
  • currentRate ∈(0.9f, 1.0f ] → CONGESTION_LEVEL_4。

档位变化的处理逻辑示例:

  • prevRate = CONGESTION_LEVEL_1、currentRate = CONGESTION_LEVEL_2:数据累积,需考虑降低码率;
  • prevRate = CONGESTION_LEVEL_4、currentRate = CONGESTION_LEVEL_3:数据累积减少,但需观察至currentRate ≤ CONGESTION_LEVEL_1再提升码率。

多通道场景需综合评估,核心调整原则:

  • 1. 大变大调,小变小调,微变不调(如prevRate=LEVEL_1、currentRate=LEVEL_4时需大幅调整);
  • 2. 恶化需快调,改善需慢调(恶化时及时调整,改善时观察后再调);
  • 3. 调整范围包含分辨率、压缩比、帧率;
  • 4. currentRate进入CONGESTION_LEVEL_4后,需尽快清空Resend Buffer,避免缓冲区溢出。
(二)如何加快出图速度?

通常加快出图的方法,是尽快让APP拿到第一个I帧,所以可以调整的策略是:

  • 1. 收到拉流指令后,设备强制编码输出一个I帧。

  • 2. 刚出图的时候,宜采用低码率(或者低分辨率),等出图几秒后,再缓慢提升码率。

(三)码率如何设计?

SDK提供三种连线模式,分别为:

  • LAN:局域网(网络稳定,带宽充足)
  • P2P:点对点(网络稳定性中等,带宽取决于双方网络)
  • RLY:数据转发(网络稳定性较差,带宽受服务器限制)

因为LAN模式通常网络最干净,不易受波动,所以码率可以传比较大。而RLY模式需要服务器中转,所以消耗服务器的流量,建议降低码率。

重要提示

目前开启重传模式,设备端才会有缓存区,每个avIndex独享各自的缓存区,默认大小为256KB,通常建议值为1~2s的数据量。如果设备端使用avServSetResendSize修改了设备端的缓存区,APP端建议也做调整,建议值为设备端缓存区大小的3~4倍(默认1MB)。

即刻开启您的物联网之旅

联系解决方案专家
Kalay App
资讯安全白皮书
全球专利布局
解决方案
新闻动态
公司动态
行业资讯
媒体报道
永续发展
经营者的话
社会参与
环境永续
公司治理

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 星空无限传媒xk8027版权所有粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

全国免费服务热线
+86 755 27702549

返回顶部