基于AVAPIs的文件下载
主要内容:

1.IO交互
2.通道的创建和销毁
3.数据的传输
4.结束标志的判断
IO交互:
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。
