简体中文

基于AVAPIs的文件下载

星空无限传媒xk8027-高清全集免费看
基于AVAPIs的文件下载流程示意图

图 1:基于AVAPIs的文件下载整体流程示意图

一、IO交互流程

文件下载的指令(相关内容:AV帧信息通用定义)交互通过 avSendIOCtrl/avRecvIOCtrl 接口完成,分为文件列表查询、下载请求、响应三个关键阶段,最终触发通道创建和数据传输。

阶段1:文件列表查询

• APP 调用 avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_REQ),传入查询条件(时间范围、文件类型等,对应 stFileListReq 结构体);

• 设备端通过 avRecvIOCtrl 接收请求,查询符合条件的文件列表,通过 avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_RESP) 回复(对应 stFileListResp 结构体)。

阶段2:下载请求发起

• 用户在APP端选中待下载文件,APP 调用 avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_REQ),传入待下载文件列表(对应 stFileDownloadReq 结构体)。

阶段3:下载响应确认

• 设备端接收下载请求后,根据硬件性能(如CPU、带宽)和软件资源,自定义分配传输通道数;

• 设备端通过 avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_RESP) 回复APP,包含分配的IOTC通道ID 和传输协议类型(此处为AVAPIs),对应 stFileDownloadResp 结构体。

阶段4:通道创建与数据传输

• APP 和设备端根据响应中的 IOTC通道ID,分别创建对应的AV通道;

• 通道创建成功后,设备端开始发送文件数据,APP端接收并存储数据。

阶段5:通道销毁

• 所有文件下载完成(检测到 endFlag=1),APP 和设备端分别销毁AV通道,释放资源。

二、AV通道的创建和销毁

通道创建需基于已建立的P2P会话(SID)和分配的IOTC通道ID,APP端与设备端的创建逻辑对称,且必须开启 resend=1(重传模式)确保数据可靠传输。
(一)APP端(客户端)通道操作
APP端作为文件接收方,通过 avClientStartEx 创建AV客户端通道,avClientStop 销毁通道。
int createDataChannelOfClientForDownload(int sid, int iotc_channel_id) {    AVClientStartInConfig in;    AVClientStartOutConfig out;    memset(&in,0, sizeof(in));    memset(&out,0, sizeof(out));    in.cb = sizeof(in);    in.iotc_session_id = sid;        // P2P会话ID(已建立的会话)    in.iotc_channel_id = iotc_channel_id; // 设备分配的IOTC通道ID    in.timeout_sec = 30;             // 连接超时时间(30秒)    in.resend = 1; //此处务必要开启resend模式(确保数据可靠传输)    in.security_mode = AV_SECURITY_AUTO;    in.auth_type = AV_AUTH_PASSWORD;      in.account_or_identity = "camera account"; // 设备账号,需跟设备端一致    in.password_or_token = "camera password"; // 设备密码,需跟设备端一致    in.sync_recv_data = 0; // 0=异步接收数据(推荐)    in.dtls_cipher_suites = nullptr; // DTLS加密套件(默认NULL)    out.cb = sizeof(out);    return avClientStartEx(&in, &out); // 创建通道,返回AV通道索引(avIndex) }
说明:resend必须设为1,否则丢包时无法重传导致文件损坏;SDK版本需区分认证类型,避免认证失败。
void destoryDataChannelOfClient(int avIndex) {    if(avIndex < 0)        return ;        avClientStop(avIndex); // 销毁AV通道,释放资源 }
(二)设备端(服务端)通道操作
设备端作为文件发送方,通过 avServStartEx 创建AV服务端通道,avServStop 销毁通道。
int createDataChannelOfDeviceForDownload(int sid, int iotc_channel_id) {    AVServStartInConfig avStartInConfig;    AVServStartOutConfig avStartOutConfig;        clearMemory(&avStartInConfig, sizeof(AVServStartInConfig));    clearMemory(&avStartOutConfig, sizeof(avStartOutConfig));    avStartInConfig.cb                    = sizeof(AVServStartInConfig);    avStartInConfig.iotc_session_id       = sid; // P2P会话ID    avStartInConfig.iotc_channel_id       = iotc_channel_id; // 分配的IOTC通道ID    avStartInConfig.timeout_sec           = 30; // 超时时间(30秒)    avStartInConfig.password_auth         = ExPasswordAuthCallBackFn; // 密码认证回调(v3版本用)    avStartInConfig.server_type           = 0; // 服务端类型(默认0)    avStartInConfig.resend                = 1; // 必须开启重传模式    avStartInConfig.security_mode = AV_SECURITY_DTLS; // 启用DTLS加密(安全传输)    //avStartInConfig.json_request = ExJsonRequest; // JSON请求回调(可选)        avStartOutConfig.cb = sizeof(AVServStartOutConfig);    return avServStartEx(&avStartInConfig, &avStartOutConfig); // 创建通道,返回AV通道索引 }
说明:security_mode设为AV_SECURITY_DTLS启用加密传输,保护文件数据安全;password_auth回调用于v3版本的密码校验。
void destoryDataChannelOfDevice(int avIndex) {    if(avIndex < 0)        return ;    avServStop(avIndex); // 销毁AV通道,释放资源 }

三、数据传输流程

数据传输依赖 avSendFrameData(设备端发送)和 avRecvFrameData2(APP端接收)接口,文件信息通过 FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t 结构体携带(相关内容:AV帧信息通用定义),传输单元为“文件信息+二进制数据”。
(一)设备端(发送方)数据发送
设备端按文件分帧读取二进制数据,搭配文件信息帧头发送,支持多文件连续传输,处理丢包重传(错误码-20006)。
#define MAX_BUFFER_SIZE 10243 // 单次发送最大缓冲区大小(可根据实际调整) // 从文件读取二进制数据(工具函数) int readBinaryDataFromFile(file f, char* buffer, size_t bufferSize) {    return f.read(buffer, bufferSize); } // 发送文件列表(核心函数) void sendFileList(void* arg) {    int avIndex = createDataChannelOfDeviceForDownload(sid, iotc_channel_id);    if(avIndex < 0){        printf("createDataChannelOfDeviceForDownload failed, error code:%d\n", avIndex);        return ;    }        foreach(auto f, files){ // 遍历待发送文件列表        // 打开文件        f.open();        // 初始化文件信息帧头        FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t frmInfo = {0};        strcpy(frmInfo.fileName, f.name().c_str()); // 文件名(含扩展名)        frmInfo.fileSize = f.size(); // 文件总大小(字节)                while(1){            char buffer[MAX_BUFFER_SIZE] = {0};            // 读取文件数据            int readSize = readBinaryDataFromFile(f, buffer, MAX_BUFFER_SIZE);            if(readSize < MAX_BUFFER_SIZE){                if(readSize <= 0)                    break; // 读取完毕,退出循环                // 若为文件列表中最后一个文件,设置结束标志                if(f.isLastFileOfList()){                    frmInfo.endFlag = 1;                                    }            }                        frmInfo.frmSize = readSize; // 当前帧数据大小            int ret = 0;                        // 发送数据:遇-20006(丢包)则重传            do{                ret = avSendFrameData(                    avIndex,                // AV通道索引                    buffer,                 // 二进制数据缓冲区                    readSize,               // 数据长度                    (const void*)&frmInfo, // 文件信息帧头                    sizeof(FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t) // 帧头长度                );                if(ret < 0){                    if(ret == -20006){ // 丢包错误,等待后重传                        msleep(20);                    } else { // 其他错误,退出发送                        break;                    }                }            } while(ret == -20006);            // 异常退出:关闭文件并销毁通道            if(ret < 0){                printf("avSendFrameData error :%d\n", ret);                f.close();                goto LAB_END;            }                        // 最后一帧发送完成,退出当前文件循环            if(frmInfo.endFlag){                break;            }        }        f.close(); // 关闭当前文件    }    // 等待重传缓冲区清空(避免数据残留)    int i_count = 0;    while(i_count++ < 10*300){ // 最多等待30秒        float userate = avResendBufUsageRate(avIndex); // 获取重传缓冲区使用率        if(userate > 0.0){            msleep(100);        } else {            break; // 缓冲区为空,可安全销毁通道        }    } LAB_END:    destoryDataChannelOfDevice(avIndex); // 销毁通道 }
(二)APP端(接收方)数据接收
APP端循环接收数据,解析帧头信息,处理文件切换(新文件打开/旧文件关闭),验证结束标志,完成后关闭文件和通道。
#define MAX_BUFFER_SIZE 300*1024 // 接收缓冲区大小(建议大于发送端缓冲区) void recvDataAndSaveToLocalFile(void *arg) {    // 创建接收通道    int avIndex = createDataChannelOfClientForDownload(sid, iotc_channel_id);    if(avIndex < 0){        printf("createDataChannelOfClientForDownload failed\n");        return;    }    char buffer[MAX_BUFFER_SIZE];    string fileName;    uint fileSize;    uint dataSize;    int endFlag;    FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t frmInfo;    int tmpInt1, tmpInt2, tmpInt3, tmpInt4;    uint frmNo;    file f; // 本地文件对象        // 循环读取数据    while(1){        int ret = avRecvFrameData2(            avIndex,                // AV通道索引            buffer,                 // 接收缓冲区            MAX_BUFFER_SIZE,        // 缓冲区最大长度            &tmpInt1,               // 预留参数(忽略)            &tmpInt2,               // 预留参数(忽略)            (char*)&frmInfo,        // 接收文件信息帧头            sizeof(FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t), // 帧头长度            &tmpInt3,               // 预留参数(忽略)            &frmNo                  // 帧序号(忽略)        );                if(ret < 0){            if(ret == AV_ER_DATA_NOREADY){ // 暂无数据,短暂等待                msleep(5);                continue;            } else if(ret == AV_ER_INCOMPLETE_FRAME || ret == AV_ER_LOSED_THIS_FRAME){                printf("lost data, frameNo:%d\n", frmNo); // 帧不完整/丢失,继续接收                continue;            } else { // 其他错误(如通道断开),退出循环                printf("avRecvFrameData2 return error:%d\n", ret);                break;            }        }                // 处理文件切换:未打开文件→打开新文件;文件名变化→关闭旧文件,打开新文件        if(!f.isOpen()){            fileName = frmInfo.fileName;            f.setFileName(fileName);            if(!f.open(QIODevice::WriteOnly)){ // 以只写模式打开文件                printf("open file failed:%s\n", fileName.toUtf8().data());                break;            }        } else {            if(fileName != frmInfo.fileName){ // 检测到新文件                f.close(); // 关闭旧文件                fileName = frmInfo.fileName;                f.setFileName(fileName);                if(!f.open(QIODevice::WriteOnly)){                    printf("open file failed:%s\n", fileName.toUtf8().data());                    break;                }            }        }                // 写入本地文件(ret为实际接收的数据长度)        f.write(buffer, ret);                // 检测结束标志:最后一个文件的最后一包,退出循环        if(frmInfo.endFlag){            printf("file download complete, fileName:%s\n", fileName.toUtf8().data());            break;        }    }    // 关闭本地文件    if(f.isOpen()){        f.close();    }    // 销毁通道,释放资源,建议收完最后一帧后延迟1s再关闭通道    destoryDataChannelOfClient(avIndex); }

四、结束标志的判断

结束标志通过 FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t 结构体的 endFlag 字段判断,规则如下:

endFlag = 0:非最后一个文件的数据包,或最后一个文件的非最后一包数据;

endFlag = 1:仅当所有待下载文件中,最后一个文件的最后一包数据 才设为1,用于标识整个下载流程结束。

注意:多文件连续传输时,前序文件的最后一包 endFlag 仍为0,仅最后一个文件的最后一包设为1,避免APP端误判提前关闭通道。

即刻开启您的物联网之旅

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

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

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

TUTK服务尽在掌握

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

返回顶部