一、相關頭文件說明
| 頭文件 | 說明 |
|---|---|
| AVFRAMEINFO.h | 幀信息定義 |
| AVIOCTRLDEFs.h | IO控制指令定義 |
| TUTKGlobalAPIs.h | 全局的設定 |
| IOTCClient.h | 客戶端連線相關專用接口定義 |
| IOTCCommon.h | 通用接口,默認值,錯誤碼定義 |
| IOTCDevice.h | 設備端連線相關專用接口定義 |
| AVClient.h | 客戶端數(shù)據(jù)發(fā)送相關接口定義 |
| AVCommon.h | 通用接口,默認值,錯誤碼定義 |
| AVServer.h | 設備端數(shù)據(jù)發(fā)送相關接口定義 |
| VSaaS.h | 云存儲相關接口定義 |
| DASA.h | 碼流自適應接口定義 |
二、相關SDK demo路徑說明
| 平臺 | 例子 |
|---|---|
| android | SDK目錄\Sample\Android\Sample_AVAPIs_Client |
| ios | SDK目錄\Sample\iOS\Sample_AVAPIs |
| windows | SDK目錄\Sample\Windows\Sample_AVAPIs_Client |
| mac | SDK目錄\Sample\MAC\Sample_AVAPIs |
三、配網(wǎng)
可以參考:常見的配網(wǎng)方式
四、連線和數(shù)據(jù)傳輸
主要使用的API: IOTCAPIs+AVAPIs
流程圖:

(一)連線
連線先使用IOTC_Get_SessionID獲取一個空閑的sid,然后調(diào)用IOTC_Connect_ByUIDEx去連線設備。
IOTC_Connect_ByUIDEx連線成功(即 >= 0),則返回本次連線的session ID,失敗則返回相關報錯,常見為-90,-42,-19等。
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);
其他參考API:
1.
2.
3.
1.
IOTC_Connect_ByUID_Parallel2.
IOTC_Connect_Stop_BySID3.
IOTC_Session_Close(二)創(chuàng)建傳輸通道
因為IOTCAPIs通道為不可靠傳輸,所以通常會創(chuàng)建一個AV通道,專門用以可靠傳輸音視頻和信令數(shù)據(jù)。創(chuàng)建方法如下:
avClientStartEx成功則返回通道ID(avIndex),失敗常見錯誤:-20011,-20015等。
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的方式驗證,可以傳更長的數(shù)據(jù)的驗證數(shù)據(jù)
avClientInConfig.account_or_identity = gDemoConfig.av_identity;
avClientInConfig.password_or_token = gDemoConfig.av_token;
avClientInConfig.auth_type = AV_AUTH_TOKEN;
} else { //使用密碼的方式驗證,驗證數(shù)據(jù)更短
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); //開啟通道
其他參考API:
1.
2.
3.
4.
1.
avClientStart2.
avClientStart23.
avServStop4.
avServExit(三)數(shù)據(jù)傳輸
AVAPIs提供了透傳的通道,針對不同的數(shù)據(jù)類型,提供了不同的傳輸API對:
- 圖像:
avSendFrameData/avRecvFrameData2 - 聲音:
avSendAudioData/avRecvAudioData - IO控制:
avSendIOCtrl/avRecvIOCtrl(io指令的定義,可以參考SDK Readme.html的AV Module Reference of AV IO Control部分,也可以自定義格式)
1. 接收視頻
APP先發(fā)送指令,再開線程接收視頻:
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的大小,這兩個太小,都可能出現(xiàn)-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;
}
}
//實際接收的幀大?。簉et
//數(shù)據(jù)處理。
//把數(shù)據(jù)拷貝到解碼隊列,初始化播放器,解碼顯示圖像。
//第一幀需要從I幀開始處理,不然會解碼失敗。
}
delete[] buf;
buf = NULL;
2. 接收音頻
APP先發(fā)送指令,再開線程接收聲音:
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;
}
}
//實際接收的幀大?。簉et
//數(shù)據(jù)處理。
//把數(shù)據(jù)拷貝到解碼隊列,初始化播放器,解碼播放聲音。
}
3. 對講
可以參考:基于AVAPIs的對講實現(xiàn)
需要注意的問題
1. 視頻和音頻的接收需要分開不同線程處理,以免互相影響。
2. 接收和解碼播放需要分開不同線程處理,以免互相影響。
3. 視頻第一幀需從I幀開始處理,如果中途丟幀,需要等下一個I幀才繼續(xù)處理。
