1、简介
根据《GB/T 28181 —2016》7.10、9.10的要求,GB28181设备网络校时功能描述如下:
联网系统内的IP 网络服务器设备宜支持 NTP(见IETF RFC2030) 协议的网络统一校时服务。 网络校时设备分为时钟源和客户端, 支持客户/服务器的工作模式; 时钟源应支持 TCP/IP、UDP 及 NTP协议, 能将输入的或自身产生的时间信号以标准的 NTP 信息包格式输出。联网系统内的IP 网络接入设备应支持SIP 信令的统一校时, 接入设备应在注册时接受来自SIP 服务器通过消息头 Date 域携带的授时。
2、基本流程
联网内设备支持基于SIP 方式或 NTP 方式的网络校时功能, 标准时间为北京时间。流程如下所示:
在注册成功情况下, 注册流程的最后一个 SIP 应答消息200 OK 中的 Date 头域中携带时间信息。采用的格式为 XML 标准格式:Date: yyyy-MM-dd’T’HH: mm:ss.SSS。
若SIP 代理通过注册方式校时, 其注册过期时间宜设置为小于 SIP 代理与 SIP 服务器出现1 s 误差所经过的运行时间。 例如:SIP 代理与SIP 服务器校时后,SIP 代理运行10 h 后设备时间与SIP 服务器时间相差大于1 s, 则宜将注册过期时间设置为10 h(36 000s) , 以保证SIP 代理与SIP 服务器之间时间误差小于1 s。
3、基于SIP的校时
sip信令处理:
int SipReg(GB28181Param_t *pGB28181Param, int isReg)
{
int ret = 0;
int len = 0;
char *msg;
int expires = 0 ;
int regState = 0;
int unAuthorized = 0;
eXosip_event_t *je = NULL;
osip_header_t *dest = NULL;
osip_message_t *reg = NULL;
long interval = GetSysSec();
if (!pGB28181Param)
{
return SIP_FAILED;
}
if (isReg)
{
expires = strtoul(pGB28181Param->userParam.sipExpires, 0, 0);
}
while(GetSysSec() -interval <= MAX_SIP_REG_TIMEOUT_SEC)
{
// 先进行不认证注册
if (0 == unAuthorized)
{
ret = SipRegisterUnauthorized(pGB28181Param, expires);
if (ret < 0)
{
if(je)
{
eXosip_event_free(je);
}
return SIP_FAILED;
}
else
{
unAuthorized = 1;
}
}
je = eXosip_event_wait(0, 100); /* 等待新消息的到来 */
if(NULL == je)
{
/* 以下语句会导致eXosip_register_send_register失败 */
eXosip_automatic_action();
usleep(100*1000);
continue;
}
/* 返回注册失败 */
if(EXOSIP_REGISTRATION_FAILURE == je->type)
{
/* 未认证注册失败,那么使用认证的方式进行注册 */
if((je->response!=NULL) && (401==je->response->status_code))
{
ret = SipRegisterAuthorized(pGB28181Param, je->rid, expires);
eXosip_event_free(je);
if(ret != OSIP_SUCCESS)
{
return SIP_FAILED;
}
}
else
{
eXosip_event_free(je);
unAuthorized = 0; /* 注册失败, 重新走一遍注册流程 */
if (isReg)
{
return SIP_REREG_AFTER_60S;
}
return SIP_FAILED;
}
}
else if (EXOSIP_REGISTRATION_SUCCESS == je->type)
{
regState = 1;
/* 收到服务器返回的注册成功 */
g_SipState.registerID = je->rid;
if (MSG_IS_REGISTER(je->request) && je->response)
{
if (OSIP_SUCCESS == osip_message_to_str(je->response, &msg, &len))
{
if (osip_message_get_date(je->response, 0, &dest) > 0)
{
SipSetSystemTime(dest->hvalue);
}
}
}
eXosip_execute();
eXosip_automatic_action();
eXosip_event_free(je);
break;
}
else
{
eXosip_event_free(je);
}
}
if( (GetSysSec() -interval > MAX_SIP_REG_TIMEOUT_SEC) && (regState == 0))
{
return SIP_FAILED;
}
pthread_mutex_lock(&g_SipState.mutex);
// 更新sip的注册状态
if (isReg)
{
g_SipState.sipRegStatus = 1;
g_SipState.keepliveAckTime = GetSysSec();
}
else
{
g_SipState.sipRegStatus = 0;
}
pthread_mutex_unlock(&g_SipState.mutex);
return SIP_SUCCESS;
}
设置系统时间接口:
static int SipSetSystemTime(char *timeStr)
{
char *beginStr = timeStr;
char *endStr = NULL;
char tempStr[10] = {0, };
struct tm st_time = {0, };
struct timeval tv = {0, };
uint32_t stime = 0;
if (!timeStr)
{
return SIP_FAILED;
}
if ((endStr = strstr(beginStr, "-")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_year = stime - 1900;
}
if ((endStr = strstr(beginStr, "-")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_mon = stime - 1;
}
if ((endStr = strstr(beginStr, "T")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_mday = stime;
}
if ((endStr = strstr(beginStr, ":")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_hour = stime;
}
if ((endStr = strstr(beginStr, ":")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_min = stime;
}
if ((endStr = strstr(beginStr, ".")))
{
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, beginStr, endStr-beginStr);
beginStr = endStr + 1;
stime = strtoul(tempStr, NULL, sizeof(tempStr));
st_time.tm_sec = stime;
}
// 设置系统时间
if ((tv.tv_sec = mktime(&st_time)) < 0)
{
GB_PrintError("mktime failed\n");
return SIP_FAILED;
}
settimeofday(&tv, NULL);
// 设置RTC时间
struct tm *stTime = gmtime(&tv);
if (SetRtcTime(stTime) < 0)
{
return SIP_FAILED;
}
return SIP_SUCCESS;
}
参考资料:
《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》
推荐阅读:GB28181协议–设备注册和注销