ONVIF+BOA文档
http://blog.sina.com.cn/s/blog_b65e8d3d0102v4c4.html
一、 准备工作:
首先需要从网上下载gSoap工具、纯净的BOA代码,并且解压gSoap压缩包
二、 ONVIF:
1. 生成.h文件的过程:
gSoap支持linux、mac和window系统,进入到我们刚刚解压后生成的gSoap目录中的bin目录中的相应的目录中(其中每个目录中都有两个文件wsdl2h和soapcpp2两个工具),此时我们可以通过网络自动生成onvif.h文件或者把我们需要的所有的wsdl文件已经下载到了本地。
网络:
wsdl2h -sck-oonvif.h -c -s -t .\typemap.dathttp://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl http://www.onvif.org/onvif/ver10/display.wsdl http://www.onvif.org/onvif/ver10/deviceio.wsdl http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl http://www.onvif.org/onvif/ver10/receiver.wsdl http://www.onvif.org/onvif/ver10/recording.wsdl http://www.onvif.org/onvif/ver10/search.wsdl http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl http://www.onvif.org/onvif/ver10/replay.wsdl http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl http://www.onvif.org/onvif/ver10/schema/onvif.xsd http://www.onvif.org/ver10/actionengine.wsdl
此时可能很慢有时会失败
离线:
wsdl2h -sck -t typemap.dat -o onvif.hanalytics.wsdlanalyticsdevice.wsdldevicemgmt.wsdldisplay.wsdlevent.wsdlimaging.wsdlmedia.wsdlptz.wsdlreceiver.wsdlrecording.wsdlremotediscovery.wsdlreplay.wsdlsearch.wsdldeviceio.wsdl
这一步你会发现生成速度非常慢,还会提示找不到onvif.xsd,速度慢得原因是wsdl2h会根据wsdl中引用去网上下载一下xsd和wsdl文件,我们可以将这些文件下载到本地,然后修改wsdl中的路径。
修改之前schemaLocation是一个url,如果你放的和你onvif的wsdl文件相同的目录中,这样写就ok了,如果不是,可以写上相对路径。
2. 生成代码
soapcpp2 -2 -L -c -x -d ../ -I /opt/nfshost/gsoap-2.8/gsoap:/opt/nfshost/gsoap-2.8/gsoap/import/:/opt/nfshost/gsoap-2.8/gsoap/custom/ onvif.h
生成代码时需要依靠部分其他文件所以我们用-I指定目录,根据你的实际情况修改上面红色字
生成的过程中会出现SOAP_ENV__Fault重复定义,注视掉soapSub.h中得内容就行了
好了,我们的代码已经生成。
修改任意一个后缀名字为.nsmap的文件为nsmap.h(所有的.nsmap文件中的内容都是相同的)
并且拷贝gsoap/custom/duration.c、gsoap/stdsoap2.c、gsoap/stdsoap2.h文件到工程中
3. 实现接口:
在soapStub.h中为我们定义了的函数,我们必须要实现,当然不是要一下子写完,你可以写个空的函数,新建一个c文件,将函数实现。
查找soapStub.h中的Server-Side Operations 注释后面的所有函数都需要我们给出实现(一直到soap_server开头的函数为止)
Probe函数是用来实现设备扫描的,实现如下:
SOAP_FMAC5 int SOAP_FMAC6 __wsdd__Probe(struct soap* soap, structwsdd__ProbeType *wsdd__Probe)
{
#define MACH_ADDR_LENGTH 6
#define INFO_LENGTH 512
#define LARGE_INFO_LENGTH 1024
#define SMALL_INFO_LENGTH 512
printf("[%d] __wsdd__Probe start !\n", __LINE__);
unsigned char macaddr[6] = {0};
char _IPAddr[INFO_LENGTH] = {0};
char _HwId[1024] = {0};
wsdd__ProbeMatchesTypeProbeMatches;
ProbeMatches.ProbeMatch = (structwsdd__ProbeMatchType *)soap_malloc(soap, sizeof(structwsdd__ProbeMatchType));
ProbeMatches.ProbeMatch->XAddrs = (char *)soap_malloc(soap, sizeof(char) * INFO_LENGTH);
ProbeMatches.ProbeMatch->Types = (char *)soap_malloc(soap, sizeof(char) * INFO_LENGTH);
ProbeMatches.ProbeMatch->Scopes = (structwsdd__ScopesType*)soap_malloc(soap,sizeof(structwsdd__ScopesType));
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties = (structwsa__ReferencePropertiesType*)soap_malloc(soap,sizeof(structwsa__ReferencePropertiesType));
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters = (structwsa__ReferenceParametersType*)soap_malloc(soap,sizeof(structwsa__ReferenceParametersType));
ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName = (structwsa__ServiceNameType*)soap_malloc(soap,sizeof(structwsa__ServiceNameType));
ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType = (char **)soap_malloc(soap, sizeof(char *) * SMALL_INFO_LENGTH);
ProbeMatches.ProbeMatch->wsa__EndpointReference.__any = (char **)soap_malloc(soap, sizeof(char*) * SMALL_INFO_LENGTH);
ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute = (char *)soap_malloc(soap, sizeof(char) * SMALL_INFO_LENGTH);
ProbeMatches.ProbeMatch->wsa__EndpointReference.Address = (char *)soap_malloc(soap, sizeof(char) * INFO_LENGTH);
netGetMac("eth1", macaddr); //eth0 得到IP地址,根据实际情况
macaddr[0]=0x01;macaddr[1]=0x01;macaddr[2]=0x01;macaddr[3]=0x01;macaddr[4]=0x01;macaddr[5]=0x01;
sprintf(_HwId,"urn:uuid:2419d68a-2dd2-21b2-a205-XXXXXX",macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
unsignedintlocalIp = 0;
netGetIp("eth1", &localIp); //eth0 得到IP地址,根据实际情况
sprintf(_IPAddr, "http://%s/onvif/device_service", inet_ntoa(*((structin_addr *)&localIp)));
printf("[%d] _IPAddr ==== %s\n", __LINE__, _IPAddr);
ProbeMatches.__sizeProbeMatch = 1;
ProbeMatches.ProbeMatch->Scopes->__item =(char *)soap_malloc(soap, 1024);
memset(ProbeMatches.ProbeMatch->Scopes->__item,0,sizeof(ProbeMatches.ProbeMatch->Scopes->__item));
//Scopes MUST BE
strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/NetworkVideoTransmitter");
ProbeMatches.ProbeMatch->Scopes->MatchBy = NULL;
strcpy(ProbeMatches.ProbeMatch->XAddrs, _IPAddr);
strcpy(ProbeMatches.ProbeMatch->Types, wsdd__Probe->Types);
printf("wsdd__Probe->Types=%s\n",wsdd__Probe->Types);
ProbeMatches.ProbeMatch->MetadataVersion = 1;
//ws-discovery瑙勫畾涓哄彲閫夐」
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__size = 0;
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__any = NULL;
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__size = 0;
ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__any = NULL;
ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_INFO_LENGTH);
//ws-discovery瑙勫畾涓哄彲閫夐」
strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0], "ttl");
ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__item = NULL;
ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->PortName = NULL;
ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__anyAttribute = NULL;
ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_INFO_LENGTH);
strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0], "Any");
strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute, "Attribute");
ProbeMatches.ProbeMatch->wsa__EndpointReference.__size = 0;
strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.Address, _HwId);
soap->header->wsa__To = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous";
soap->header->wsa__Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches";
soap->header->wsa__RelatesTo = (structwsa__Relationship*)soap_malloc(soap, sizeof(structwsa__Relationship));
soap->header->wsa__RelatesTo->__item = soap->header->wsa__MessageID;
soap->header->wsa__RelatesTo->RelationshipType = NULL;
soap->header->wsa__RelatesTo->__anyAttribute = NULL;
soap->header->wsa__MessageID =(char *)soap_malloc(soap, sizeof(char) * INFO_LENGTH);
strcpy(soap->header->wsa__MessageID,_HwId+4);
if (SOAP_OK == soap_send___wsdd__ProbeMatches(soap, "http://", NULL, &ProbeMatches))
{
printf("send ProbeMatches success !\n");
return SOAP_OK;
}
printf("[%d] soap error: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
return soap->error;;
}
注释掉soap_wsa_2005,此时,我们的webserver可以编译通过了
三、 ONVIF集成到BOA中
我们在boa的src目录中创建一个onvif的目录把我们与onvif相关的代码放到该目录中
1. 实现设备的扫描功能:
由于onvif的扫描是用UDP(组播)请求的,所以我们需要在boa.c中的main函数中创建一个线程用于应答onvif的设备扫描的应答功能,线程函数的代码如下:
void *hello_thr(void *arg)
{
int count = 0;
struct soap ServerSoap;
structip_mreqmcast;
soap_init1(&ServerSoap, SOAP_IO_UDP | SOAP_XML_IGNORENS);
soap_set_namespaces(&ServerSoap, namespaces);
printf("[%s][%d][%s][%s] ServerSoap.version = %d \n", __FILE__, __LINE__, __TIME__, __func__, ServerSoap.version);
if(!soap_valid_socket(soap_bind(&ServerSoap, NULL, 3702, 10)))
{
soap_print_fault(&ServerSoap, stderr);
exit(1);
}
mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mcast.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(ServerSoap.master, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0)
{
printf("setsockopt error! error code = %d,err string = %s\n",errno,strerror(errno));
return NULL;
}
while(1)
{
if(soap_serve(&ServerSoap))
{
fprintf(stderr,"soap----udp fault\n");
soap_print_fault(&ServerSoap, stderr);
}
fprintf(stderr,"soap server----over\n");
soap_destroy(&ServerSoap);
soap_end(&ServerSoap);
//客户端的IP地址
fprintf(stderr,"RECEIVE count %d, connection from IP = %lu.%lu.%lu.%lu socket = %d \r\n",count, ((ServerSoap.ip)>>24)&0xFF, ((ServerSoap.ip)>>16)&0xFF, ((ServerSoap.ip)>>8)&0xFF, (ServerSoap.ip)&0xFF, (ServerSoap.socket));
count++;
}
//分离运行时的环境
soap_done(&ServerSoap);
return NULL;
}
2. 实现用onvif Test Tool工具的其他功能:
Onvif的其他功能是通过TCP(80端口)请求的,所以我们要与我们的boa中获得其他HTTP包时同时获得请求并且判断是否为onvif请求,如果是我们就进入onvifServer中,不是我们就进入boa的http应答中,代码如下:
struct soap* soap;
soap=soap_new();
soap_init(soap);
soap->master = server_s;
soap->socket = server_s;
soap->errmode = 0;
soap->bind_flags = 1;
我们知道boa所有的请求循环是在select_loop函数中得到的,所以我们修改该函数
Void select_loop(intserver_s,struct soap* soap);
并且修改process_requests函数,
Void process_requests(intserver_s,struct soap *soap);
这样就可以每次获得我们的onvif请求
下面我们主要修改process_requests函数(如果是onvif请求则进入onvifserver中进行应答,如果是普通的请求则有boa进行应答)
在process_requests函数内添加:
intOnvifEN = 0;
intlookupindex = 0;
charservice_uri[100] = "";
memset((void*)&soap->peer, 0, sizeof(soap->peer));
soap->socket = SOAP_INVALID_SOCKET;
soap->error = SOAP_OK;
soap->errmode = 0;
soap->keep_alive = 0;
在while(current)中添加
OnvifEN = isOnvif(current->client_stream, service_uri, &lookupindex);
if(OnvifEN == 1)
{
//soap->socket = midhnewsock;
soap->socket = current->fd;
soap->peer = onvif_remote_addr;
if (soap_valid_socket(soap->socket))
{
soap->ip = ntohl(soap->peer.sin_addr.s_addr);
soap->port = (int)ntohs(soap->peer.sin_port);
soap->keep_alive = (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) != 0);
}
ONVIF_BUFFER = (char *)soap_malloc(soap, sizeof(current->client_stream));
strcpy(ONVIF_BUFFER, current->client_stream);//缓冲区在frecv函数中需要把ONVIF_BUFFER中的内容再重新拷贝到soap中
soap_begin_recv(soap);
if (soap_envelope_begin_in(soap))
{
soap_send_fault(soap);
}
if (soap_recv_header(soap))
{
soap_send_fault(soap);
}
if (soap_body_begin_in(soap))
{
soap_send_fault(soap);
}
interrorCode = 0;
if (errorCode = soap_serve_request(soap))
{
fprintf(stderr, "[onvif]soap_serve_request fail, errorCode %d \n", errorCode);
soap_send_fault(soap);
}
//strcpy(current->client_stream, "");
memset(current->client_stream, 0, CLIENT_STREAM_SIZE );
soap_dealloc(soap, NULL);
soap_destroy(soap);
soap_end(soap);
current->status = DONE;
close(soap->socket);
}
IsOnvif函数的代码如下:
intisOnvif(char *current_client_stream , char *service_uri, int *lookupindex)
{
char segments[3][100];
charpost_req[80];
charservice_name[40];
intservice_index = 0,ret;
intindex_post = 0;
intstream_index = 0;
if (strncmp(current_client_stream, "POST ", 5)==0)
{
index_post = 0;
stream_index = 0;
while(current_client_stream[stream_index] != '\n' &¤t_client_stream[stream_index] != '\r')
{
post_req[index_post++] = current_client_stream[stream_index++];
}
post_req[index_post] = '\0';
explodeRequest(post_req, ' ', segments);
index_post = 1;
service_index = 0;
while(segments[1][index_post] != '/' && segments[1][index_post] != '\0')
{
service_name[service_index++] = segments[1][index_post++];
}
service_name[service_index] = '\0';
ret = lookupservices(service_name,lookupindex);
if(ret)
{
strcpy(service_uri,segments[1]);
}
return ret;
}
else
{
return 0;
}
}
voidexplodeRequest(char *in, char dl, char list[][100])
{
int i = 0;
int j = 0;
int k = 0;
intlen = strlen(in);
for(i = 0; i
{
if(in[i] == dl)
{
*( *(list + j) + k) = 0;
j++;
k = 0;
}
else
{
*( *(list + j) + k) = in[i];
k++;
}
}
*(*(list + j) + k) = 0;
j++;
if(j < 3)
**(list + j) = 0;
}
intlookupservices(char *service, int *lookupindex)
{
int i;
char lookup[10][30]={"device",
"imaging",
"media",
"custom",
"onvif",
""};
// for(i = 0;lookup[i][0];i++)
for(i = 0; i<6; i++)
{
if(strcasecmp(lookup[i], service) == 0)
{
*lookupindex = i;
return 1;
}
}
*lookupindex = -1;
return 0;
}
3.修改boa的Makefile文件:
把我们的onvif目录中的文件添加到Makefile中,make就可以了