C语言开发:Onvif(一)

发布于:2025-06-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

根据ONVIF官网 的介绍:

ONVIF是一个开放的安防行业组织,致力于为安防行业提供和促进标准化开放接口,以实现IP网络安防产品和服务的有效互操作性。

在具体实现上,ONVIF使用了Web Service的方式,设备通过WSDL定义的接口提供服务。ONVIF支持的接口众多,本文仅涉及使用C语言与Onvif摄像机互操作的过程。

C语言使用Web Service

在C语言中使用Web Service,可以用gsoap来自动生相应的实现。

方法分为三步:

  1. 安装gsoap软件包。
  2. 取得Web Service的xsd、wsdl。
  3. 生成接口函数。
  4. 调用接口函数,完成Web Service的使用。

安装gsoap软件包

安装gsoap软件包的过程,与其它软件并无二致。唯一需要注意的是,安装的版本如果比较低,可能个别API的实现稍有不同。

另外,有的系统上把gsoap分成了gsoap与gsoap-devel多个包。因为我们是开发Web Service,所以需要安装gsoap-devel包,安装完成之后,/usr/share/gsoap目录下会有:

~/$ ls /usr/share/gsoap/  
custom  import  plugin  WS

这些目录。

对于不同的Web Service服务,会使用这里面提供的实现。

比如,如果我们的Onvif摄像机需要认证,就需要使用/usr/share/gsoap/plugin里面的httpda.hhttpda.c等实现。

取得Web Service的xsd、wsdl

与Onvif摄像机取流相关的文件,大概有:

b-2.xsd   common.xsd       event.wsdl   media.wsdl  t-1.xsd      ws-discovery.wsdl  
bf-2.xsd  devicemgmt.wsdl  media2.wsdl  onvif.xsd   typemap.dat

其中,xsd主要是一些结构定义,wsdl则是接口定义。这些文件可以从Onvif 官网的网络接口规范页面下载。

生成接口函数

我们使用gsoap的工具软件,根据这些wsdl文件,生成接口函数。

主要使用两个工具,wsdl2h与soapcpp2。

wsdl2h

wsdl2h的功能,是把我们下载的xsd、wsdl等文件,生成相应的数据结构以及接口定义。参数比较多,我们只需要调整其中的很少的一部分参数就足够了。

比如:

wsdl2h -c -d -o onvif.h -t typemap.dat *.xsd *.wsdl

其中,

  • -c 表示生成C语言的定义
  • -d 表示使用Dom来表示xs:any等
  • -o onvif.h 表示生成到onvif.h这个头文件中
  • -t typemap.dat 表示使用自定义的typemap

执行完成之后,将会生成一个onvif.h头文件。我们的程序中,使用这个头文件即可完成Web Service相关函数的引入。

soapcpp2

soapcpp2的功能是根据onvif.h来生成接口实现的代码片段。

如:

soapcpp2 -c -d ./impl -L -x -T -I/usr/include

其中,

  • -c 表示生成C语言源文件
  • -d 表示生成文件的路径
  • -L 表示不用生成Server相关的实现
  • -x 表示不用生成示例消息文件
  • -T 表示生成Server测试代码
  • -I 表示头文件包含路径

我们执行上面的命令,将在./impl 目录中得到soapC.c、soapClient.c、soapServer.c 等源文件。

当然,为了做Onvif的认证授权,我们还需要在项目中编译连接

wsseapi.c  
mecevp.c  
smdevp.c  
wsaapi.c  
wsddapi.c  
httpda.c

等源文件。

使用接口函数

有了onvif.h头文件,又有了soapC.c等实现文件,我们就可以使用Onvif的相关函数,进行Onvif的开发了。

使用gsoap生成的接口函数,核心需要一个struct soap结构的指针。

使用struct soap的大概步骤为:

  1. 生成struct soap指针
  2. 执行struct soap相应的函数
  3. 释放struct soap指针

其中在第二步,也就是使用struct soap相应的函数过程中,会使用我们前面根据wsdl生成的接口函数,来处理Web Service相应的结构。

生成struct soap指针

生成struct soap的方法为使用soap_new()函数。

如果打开/usr/include/stdsoap2.h查看,会发现struct soap结构的定义很长,包括了各种各样的参数。

其中大部分参数,都可以见文知义,只有一少部分,需要额外解释。

比如,

  • user 上层应用的指针
  • modeimodeomode指定协议,比如使用soap_new1(SOAO_IO_UDP)来生成一个UDP的soap结构。
  • bind_flags 绑定的标识,比如SO_REUSEADDR。

以下代码,生成了一个struct soap,又设置了一些超时相关的参数,最后注册了HTTP认证插件。

struct soap *s;

s = soap_new ();  

s->connect_timeout = 2;  
s->recv_timeout = 2;  
s->send_timeout = 2;  
s->transfer_timeout = 2;

soap_register_plugin (service->s, http_da);

释放struct soap指针

soap_destroy (s);
soap_end (s);
soap_free (s);

执行struct soap相应的函数

void
soap_getdeviceinformation (struct soap *soap)  
{  
  struct _tds__GetDeviceInformation req[1];  
  struct _tds__GetDeviceInformationResponse response[1];
  int res;  
  
  res = soap_call___tds__GetDeviceInformation(soap, req, response);
  if (res == 0)
    {  
      if (response->SerialNumber != NULL)  
        {    
          printf ("Manufacturer: %s\n",  response->Manufacturer); 
          printf ("Model: %s\n", response->Model);  
          printf ("FirmwareVersion: %s\n", response->FirmwareVersion);  
          printf ("SerialNumber: %s\n", response->SerialNumber);  
          printf ("HardwareId: %s\n", response->HardwareId);
        }
    }  
  
  soap_destroy(soap);
  soap_end(soap);  
}

以上代码中,struct _tds__GetDeviceInformationstruct _tds__GetDeviceInformationResponse结构,以及soap_call___tds__GetDeviceInformation函数的定义,都是gsoap的工具自动生成的。

soapClient.c中, soap_call___tds__GetDeviceInformation实现如下:

SOAP_FMAC5 int SOAP_FMAC6 soap_call___tds__GetDeviceInformation(struct soap *soap, const char *soap_endpoint, const char *soap_action, struct _tds__GetDeviceInformation *tds__GetDeviceInformation, struct _tds__GetDeviceInformationResponse *tds__GetDeviceInformationResponse)  
{   
  if (soap_send___tds__GetDeviceInformation(soap, soap_endpoint, soap_action, tds__GetDeviceInformation) || soap_recv___tds__GetDeviceInformation(soap, tds__GetDeviceInformationResponse))  
       return soap->error;  
    return SOAP_OK;  
}

这个函数的下面,就是soap_send___tds__GetDeviceInformationsoap_recv___tds__GetDeviceInformation的实现,也都是这样类似的代码:

SOAP_FMAC5 int SOAP_FMAC6 soap_send___tds__GetDeviceInformation(struct soap *soap, const char *soap_endpoint, const char *soap_action, struct _tds__GetDeviceInformation *tds__GetDeviceInformation)
{	struct __tds__GetDeviceInformation soap_tmp___tds__GetDeviceInformation;
	if (soap_action == NULL)
		soap_action = "http://www.onvif.org/ver10/device/wsdl/GetDeviceInformation";
	soap_tmp___tds__GetDeviceInformation.tds__GetDeviceInformation = tds__GetDeviceInformation;
	soap_begin(soap);
	soap->encodingStyle = NULL; /* use SOAP literal style */
	soap_serializeheader(soap);
	soap_serialize___tds__GetDeviceInformation(soap, &soap_tmp___tds__GetDeviceInformation);
	if (soap_begin_count(soap))
		return soap->error;
	if ((soap->mode & SOAP_IO_LENGTH))
	{	if (soap_envelope_begin_out(soap)
		 || soap_putheader(soap)
		 || soap_body_begin_out(soap)
		 || soap_put___tds__GetDeviceInformation(soap, &soap_tmp___tds__GetDeviceInformation, "-tds:GetDeviceInformation", "")
		 || soap_body_end_out(soap)
		 || soap_envelope_end_out(soap))
			 return soap->error;
	}
	if (soap_end_count(soap))
		return soap->error;
	if (soap_connect(soap, soap_endpoint, NULL)
	 || soap_envelope_begin_out(soap)
	 || soap_putheader(soap)
	 || soap_body_begin_out(soap)
	 || soap_put___tds__GetDeviceInformation(soap, &soap_tmp___tds__GetDeviceInformation, "-tds:GetDeviceInformation", "")
	 || soap_body_end_out(soap)
	 || soap_envelope_end_out(soap)
	 || soap_end_send(soap))
		return soap_closesock(soap);
	return SOAP_OK;
}

SOAP_FMAC5 int SOAP_FMAC6 soap_recv___tds__GetDeviceInformation(struct soap *soap, struct _tds__GetDeviceInformationResponse *tds__GetDeviceInformationResponse)
{
	if (!tds__GetDeviceInformationResponse)
		return soap_closesock(soap);
	soap_default__tds__GetDeviceInformationResponse(soap, tds__GetDeviceInformationResponse);
	if (soap_begin_recv(soap)
	 || soap_envelope_begin_in(soap)
	 || soap_recv_header(soap)
	 || soap_body_begin_in(soap))
		return soap_closesock(soap);
	soap_get__tds__GetDeviceInformationResponse(soap, tds__GetDeviceInformationResponse, "tds:GetDeviceInformationResponse", NULL);
	if (soap->error)
		return soap_recv_fault(soap, 0);
	if (soap_body_end_in(soap)
	 || soap_envelope_end_in(soap)
	 || soap_end_recv(soap))
		return soap_closesock(soap);
	return soap_closesock(soap);
}