主要内容:
- IO交互
- 通道的创建和销毁
- 数据的传输
- 结束标志 的判断
IO交互:
- APP会通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_REQ)查询指定时间内的文件。设备通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_RESP)回复对应的文件列表。
- 当用户选中部分文件下载的时候,则APP端会avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_REQ)告知设备要下载哪些文件。设备则根据自身的硬件和软件资源,可以自定义使用的通道数,通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_RESP)告知APP使用哪些通道以及使用哪种APIs来进行数据传输。
- APP和设备端分别创建对应的通道。
- 数据传输。
- 数据传输后关闭通道。
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。