内容:
- demo路径说明
- 常见的配网方式
- 连线和数据传输
- 常见的问题以及处理
- 相关头文件说明
一、相关sdk demo路径说明
二、常见的配网方式
可以参考:常见的配网方式实作纲要
三、连线和数据传输
主要使用的API: IOTCAPIs+AVAPIs
流程图:
- 连线
连线先使用IOTC_Get_SessionID获取一个空闲的sid,然后调用IOTC_Connect_ByUIDEx去连线设备。
tmpSID = IOTC_Get_SessionID(); if(tmpSID < 0) { printf(" [%s:%d] IOTC_Get_SessionID failed ret[%d]\n", __FUNCTION__, __LINE__, tmpSID); pthread_exit(0); } printf(" [%s:%d] IOTC_Get_SessionID tmpSID[%d]\n", __FUNCTION__, __LINE__, tmpSID); IOTCConnectInput connect_input; connect_input.cb = sizeof(connect_input); connect_input.authentication_type = AUTHENTICATE_BY_KEY; connect_input.timeout = 20; //连线超时时间 memcpy(connect_input.auth_key, gDemoConfig.iotc_auth_key, IOTC_AUTH_KEY_LENGTH); SID = IOTC_Connect_ByUIDEx(ServerInfo->UID, tmpSID, &connect_input);
IOTC_Connect_ByUIDEx连线成功(即 >= 0),则返回本次连线的session ID,失败则返回相关报错,常见为-90,-42,-19等。
【其他】参考:IOTC_Connect_ByUID_Parallel,IOTC_Connect_Stop_BySID,IOTC_Session_Close
- 创建传输通道
因为IOTCAPIs通道为不可靠传输,所以通常会创建一个AV通道,专门用以可靠传输音视频和信令数据。创建方法可参考:
AVClientStartInConfig avClientInConfig;
AVClientStartOutConfig avClientOutConfig;
memset(&avClientInConfig, 0, sizeof(avClientInConfig));
avClientInConfig.cb = sizeof(AVClientStartInConfig);
avClientInConfig.iotc_channel_id = 0;//av通道需使用的iotc channel id,范围[0,31]
avClientInConfig.iotc_session_id = SID;//相关的session id,参考上一步连线
avClientInConfig.timeout_sec = 30;
avClientInConfig.resend = 1; //1 为开启重传,0为关闭重传
if (ENABLE_DTLS)
avClientInConfig.security_mode = AV_SECURITY_DTLS;//使用DTLS加密
else
avClientInConfig.security_mode = AV_SECURITY_SIMPLE;//使用TUTK加密
if (AUTH_BY_TOKEN) { //使用token的方式验证,可以传更长的数据的验证数据
avClientInConfig.account_or_identity = gDemoConfig.av_identity;
avClientInConfig.password_or_token = gDemoConfig.av_token;
avClientInConfig.auth_type = AV_AUTH_TOKEN;
} else { //使用密码的范式验证,验证数据更短
avClientInConfig.account_or_identity = gDemoConfig.av_account;
avClientInConfig.password_or_token = gDemoConfig.av_password;
avClientInConfig.auth_type = AV_AUTH_PASSWORD;
}
avClientOutConfig.cb = sizeof(AVClientStartOutConfig);
avIndex = avClientStartEx(&avClientInConfig, &avClientOutConfig); //开启通道
avClientStartEx成功则放回通道ID,即avIndex。失败常见错误:-20011,-20015等
【其他】参考:avClientStart,avClientStart2,avServStop,avServExit。
- 数据传输
AVAPIs提供了透传的通道,针对不同的数据类型,提供了不同的传输API对。
- 图像:avSendFrameData / avRecvFrameData2
- 声音:avSendAudioData / avRecvAudioData
- IO控制:avSendIOCtrl / avRecvIOCtrl //io指令的定义,可以参考SDK Readme.html的AV Module Reference of AV IO Control部分,也可以自定义格式
>>接收视频:
APP先发送指令,再开线程接收视频:
SMsgAVIoctrlAVStream ioMsg;
memset(&ioMsg, 0, sizeof(SMsgAVIoctrlAVStream));
if((ret = avSendIOCtrl(avIndex, IOTYPE_USER_IPCAM_START, (char *)&ioMsg, sizeof(SMsgAVIoctrlAVStream))) < 0)
{
printf("start_ipcam_stream failed[%d]\n", ret);
return 0;
}
接收视频主要代码:
unsigned int frmNo;
int outBufSize = 0;
int outFrmSize = 0;
int outFrmInfoSize = 0;
bool need_key_frame = false;
int video_buf_size = VIDEO_BUF_SIZE;
char *buf = new char[VIDEO_BUF_SIZE];
FRAMEINFO_t frameInfo;
while(1){
int ret = avRecvFrameData2(avIndex, buf, video_buf_size, &outBufSize, &outFrmSize, (char *)&frameInfo, sizeof(FRAMEINFO_t), &outFrmInfoSize, &frmNo);
if(ret <= 0){
if(ret == AV_ER_DATA_NOREADY) {
usleep(5 * 1000);
continue;
}
else if(ret == AV_ER_LOSED_THIS_FRAME || ret == AV_ER_INCOMPLETE_FRAME) {
printf("Recv video, Lost video frame NO[%d]\n", frmNo);
need_key_frame = true;//中途丢帧,需要等I帧
}
else if(ret == AV_ER_BUFPARA_MAXSIZE_INSUFF){
printf("Video buffer is too small to store frame[%d]\n", frmNo);
//加大视频buffer,或者frameInfo的大小,这两个太小,都可能出现-20001。
resizeVideoBuffer();//or resizeFrameInfoBuffer()
need_key_frame = true;//需要等I帧
continue;
}
else{
printf("Recv video, error code[%d],will break;\n",ret);
break;
}
}
if(need_key_frame){
if(frameInfo.flags = IPC_FRAME_FLAG_IFRAME){
need_key_frame = false;
}
else{
continue;
}
}
//实际接收的帧大小:ret
//数据处理。
//把数据拷贝到解码队列,初始化播放器,解码显示图像。
//第一帧需要从I帧开始处理,不然会解码失败。
}
delete[] buf;
buf = NULL;
>>接收音频:
APP先发送指令,再开线程接收声音:
if((ret = avSendIOCtrl(avIndex, IOTYPE_USER_IPCAM_AUDIOSTART, (char *)&ioMsg, sizeof(SMsgAVIoctrlAVStream))) < 0)
{
printf("start_ipcam_stream failed[%d]\n", ret);
return 0;
}
接收声音的主要代码:
char buf[AUDIO_BUF_SIZE];
FRAMEINFO_t frameInfo;
while(1){
int ret = avRecvAudioData(avIndex, buf, AUDIO_BUF_SIZE, (char *)&frameInfo, sizeof(FRAMEINFO_t), &frmNo);
if(ret <= 0){
if(ret == AV_ER_DATA_NOREADY) {
usleep(5 * 1000);
continue;
}
else if(ret == AV_ER_LOSED_THIS_FRAME || ret == AV_ER_INCOMPLETE_FRAME) {
printf("Recv video, Lost audio frame NO[%d]\n", frmNo);
#if _ENABLE_FILL_LAST_FRAME_
//因为音频本身是线性的,所以中途有丢一帧,可以用上一帧来填充。
memcpy(dst_audio_buf,last_audio_frame,last_audio_size);
#else
continue;
#endif
}
else{
printf("Recv audio, error code[%d],will break;\n",ret);
break;
}
}
//实际接收的帧大小:ret
//数据处理。
//把数据拷贝到解码队列,初始化播放器,解码播放声音。
}
需要注意的问题:
1,视频和音频的接收需要分开不同线程处理,以免互相影响。
2,接收和解码播放需要分开不同线程处理,以免互相影响。
3,视频第一帧需从I帧开始处理,如果中途丢帧,需要等下一个I帧才继续处理。
>>对讲:
可以参考:基于AVAPIs的对讲实现
四、常见问题以及处理
五、相关头文件说明
其他文章:
概论:引导
APP端的传输状况推算和丢帧估算:APP端的传输状况推算和丢帧估算
API比较:API比较
局域网设备搜索:基于IOTCAPIs的局域网设备搜索