Onvif基础框架搭建
第一步、下载gsoap
从gsoap官网(http://www.genivia.com/products.html#notice)下载最新版gsoap(博主用的是gsoap_2.8.46)。gsoap有三个版本:Open Source gSOAP、Commercial gSOAP Edition、Commercial gSOAP Edition。我们下载开源版本即可。
这是博主分享的gsoap下载链接:链接:http://pan.baidu.com/s/1mi2tWLy 密码: rpms
第二步、下载所有wsdl与xsd
从onvif官网(https://www.onvif.org/profiles/specifications/)下载所有wsdl与xsd(右键-将链接另存为即可)
这是博主分享的wsdl与xsd链接:http://pan.baidu.com/s/1o8hrjWe 密码: 9gqm
第三步、解压gsoap并建立文件夹层次
新建一个文件夹,命名为OnvifFramework,将下载下来的gsoap_x.x.xx.zip解压到该文件夹。在该文件夹下再新建两个文件夹,分别命名为:level1和ver10。在level1文件夹下新建一个文件夹,命名为:level2。在level2文件夹下新建三个文件夹,分别命名为:FrameworkSource、GenerateSource、ver10。在ver10文件夹下建立三个文件夹,分别命名为:device、media、schema。在device和media文件夹下新建一个文件夹,命名为wsdl。在ver10文件夹下新建一个文件夹,命名为schema。文件夹请务必按这个顺序建立。最后将OnvifFramework\gsoap-2.8\gsoap路径下的typemap.dat和OnvifFramework\gsoap-2.8\gsoap\bin\win32路径下的wsdl2h.exe、soapcpp2.exe拷贝到GenerateSource文件夹下。
具体文件夹层次结构如下图所示:
第四步、下载Onvif相关文件
从Onvif官网(http://www.onvif.org/Documents/Specifications.aspx)的ONVIF WSDL and XML Schemas Specifications页面中下载wsdl(右键-将链接另存为即可),并将下载下来的wsdl放在GenerateSource文件夹内。拷贝devicemgmt.wsdl到D:\ONVIF\OnvifFramework\level1\level2\ver10\device\wsdl路径下,拷贝media.wsdl到OnvifFramework\level1\level2\ver10\media\wsdl路径下,拷贝所有xsd到OnvifFramework\level1\level2\ver10\schema与OnvifFramework\ver10\schema路径下。
第五步、生成onvif.h
生成方式分为在线生成和本地生成两种,鉴于在线生成的不稳定性,博主采用本地生成方式。打开控制台,切换到GenerateSource路径下,在控制台中输入wsdl2h -x -o onvif.h -c -s -t ./typemap.dat devicemgmt.wsdl media.wsdl
;因为我只用到了媒体相关部分,所以只用了这两个wsdl,如果需要其他的,后面加上即可(各个wsdl间用一个空格隔开)
第六步、通过onvif.h文件生成源代码框架
修改xsd和wsdl中的schemaLocation为本地路径(仅部分wsdl需要修改),将 OnvifFramework\gsoap-2.8\gsoap\import路径下的wsa5.h中的SOAP_ENV__Fault结构体注释掉或改为其他名字,如 SOAP_ENV__Fault_ex。修改完后在控制台中输入soapcpp2 -c onvif.h -x -d .\ -I..\..\..\gsoap-2.8\gsoap\import -I ..\..\..\gsoap-2.8\gsoap\,看到Compilation successful即为成功。
第七步、整理源代码
将生成的.h和.cpp、.c文件以及.cnsmap文件拷贝到FrameworkSource中,并拷贝OnvifFramework\gsoap-2.8\gsoap路径下的stdsoap2.h和stdsoap2.cpp到该文件夹。还需要拷贝OnvifFramework\gsoap-2.8\gsoap\custom路径下的duration.h和duration.c到该文件下
总结
通过以上七个步骤即可完成ONVIF开发框架搭建,以后开发Onvif应用时,直接拷贝FrameworkSource文件夹中的文件即可。Onvif框架文件已上传,请到此处(链接:http://pan.baidu.com/s/1ge2nXRX 密码: 1say)下载。
onvif开发必备资料链接:
https://www.onvif.org/onvif/ver20/util/operationIndex.html
Onvif简单开发
#include "FrameworkSource/MediaBinding.nsmap"
#include "FrameworkSource/soapH.h"
#include "FrameworkSource/stdsoap2.h"
struct OnvifApp {
char hostAddress[32];
char xAddr[128];
unsigned short bindPort;
char xAddrRoot[128];
struct soap *soap;
SOAP_SOCKET masterSocket;
};
static struct OnvifApp *gApp = new struct OnvifApp();
int UserGetCapabilities(struct soap *soap, struct __wsdd__ProbeMatches *resp,
struct _tds__GetCapabilities *capa_req, struct _tds__GetCapabilitiesResponse *capa_resp)
{
int result = 0;
capa_req->Category = (enum tt__CapabilityCategory *)soap_malloc(soap, sizeof(int));
capa_req->__sizeCategory = 1;
*(capa_req->Category) = (enum tt__CapabilityCategory)(tt__CapabilityCategory__Media);
capa_resp->Capabilities = (struct tt__Capabilities*)soap_malloc(soap, sizeof(struct tt__Capabilities));
//soap_wsse_add_UsernameTokenDigest(soap, "user", ONVIF_USER, ONVIF_PASSWORD);
printf("\n--------------------Now Gettting Capabilities NOW --------------------\n\n");
result = soap_call___tds__GetCapabilities(soap, "http://192.168.0.55/onvif/device_service", NULL, capa_req, capa_resp);
if (soap->error)
{
printf("[%s][%d]--->>> soap error: %d, %s, %s\n", __func__, __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
result = soap->error;
}
else
{
printf(" \n--------------------GetCapabilities OK! result=%d--------------\n \n", result);
if (capa_resp->Capabilities == NULL)
{
printf(" GetCapabilities failed! result=%d \n", result);
}
else
{
printf(" Media->XAddr=%s \n", capa_resp->Capabilities->Media->XAddr);
}
}
return result;
}
int UserGetProfiles(struct soap *soap, struct _tds__GetCapabilitiesResponse *capa_resp,
struct _trt__GetProfiles *trt__GetProfiles, struct _trt__GetProfilesResponse *trt__GetProfilesResponse)
{
int result = 0;
printf("\n-------------------Getting Onvif Devices Profiles--------------\n\n");
//soap_wsse_add_UsernameTokenDigest(soap, "user", ONVIF_USER, ONVIF_PASSWORD);
result = soap_call___trt__GetProfiles(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetProfiles, trt__GetProfilesResponse);
if (soap->error)
//NOTE: it may be regular if result isn't SOAP_OK.Because some attributes aren't supported by server.
//any question email leoluopy@gmail.com
{
printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
result = soap->error;
}
else {
printf("\n-------------------Profiles Get OK--------------\n\n");
if (trt__GetProfilesResponse->Profiles != NULL)
{
if (trt__GetProfilesResponse->Profiles->Name != NULL) {
printf("Profiles Name:%s \n", trt__GetProfilesResponse->Profiles->Name);
}
if (trt__GetProfilesResponse->Profiles->token != NULL) {
printf("Profiles Taken:%s\n", trt__GetProfilesResponse->Profiles->token);
}
}
else {
printf("Profiles Get inner Error\n");
}
}
printf("Profiles Get Procedure over\n");
return result;
}
int UserGetUri(struct soap *soap, struct _tds__GetCapabilitiesResponse *capa_resp, struct _trt__GetProfilesResponse *trt__GetProfilesResponse,
struct _trt__GetStreamUri *trt__GetStreamUri, struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse)
{
int result = 0;
trt__GetStreamUri->StreamSetup = (struct tt__StreamSetup*)soap_malloc(soap, sizeof(struct tt__StreamSetup));//初始化,分配空间
trt__GetStreamUri->StreamSetup->Stream = tt__StreamType__RTP_Unicast;//stream type
trt__GetStreamUri->StreamSetup->Transport = (struct tt__Transport *)soap_malloc(soap, sizeof(struct tt__Transport));//初始化,分配空间
trt__GetStreamUri->StreamSetup->Transport->Protocol = tt__TransportProtocol__UDP;
trt__GetStreamUri->StreamSetup->Transport->Tunnel = 0;
//trt__GetStreamUri->StreamSetup->__size = 1;
//trt__GetStreamUri->StreamSetup->__any = NULL;
//trt__GetStreamUri->StreamSetup->__anyAttribute = NULL;
trt__GetStreamUri->ProfileToken = trt__GetProfilesResponse->Profiles->token;
printf("\n\n---------------Getting Uri----------------\n\n");
//soap_wsse_add_UsernameTokenDigest(soap, "user", ONVIF_USER, ONVIF_PASSWORD);
result = soap_call___trt__GetStreamUri(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetStreamUri, trt__GetStreamUriResponse);
//rtsp://192.168.0.55:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
if (soap->error) {
printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
result = soap->error;
}
else {
printf("!!!!NOTE: RTSP Addr Get Done is :%s \n", trt__GetStreamUriResponse->MediaUri->Uri);
}
return result;
}
int get(struct soap *soap)
{
struct _tds__GetCapabilities capa_req;
struct _tds__GetCapabilitiesResponse capa_resp;
int ret = UserGetCapabilities(soap, NULL, &capa_req, &capa_resp);
if (ret != 0)
return ret;
struct _trt__GetProfiles trt__GetProfile;
struct _trt__GetProfilesResponse trt__GetProfileResponse;
trt__GetProfileResponse.Profiles = NULL;
ret = UserGetProfiles(soap, &capa_resp, &trt__GetProfile, &trt__GetProfileResponse);
if (ret != 0)
return ret;
struct _trt__GetStreamUri trt__GetStreamUri;
struct _trt__GetStreamUriResponse trt__GetStreamUriResponse;
ret = UserGetUri(soap, &capa_resp, &trt__GetProfileResponse, &trt__GetStreamUri, &trt__GetStreamUriResponse);
if (ret != 0)
return ret;
return ret;
}
int _gsoapInit(struct OnvifApp* app)
{
if (!app->bindPort)
app->bindPort = 80;
strcpy(app->hostAddress, "192.168.0.55");
snprintf(app->xAddr, sizeof(app->xAddr), "http://%s:%d/onvif/device_service",
app->hostAddress, app->bindPort);
fprintf(stderr, "xAddr:%s\n", app->xAddr);
snprintf(app->xAddrRoot, sizeof(app->xAddrRoot), "http://%s:%d/onvif",
app->hostAddress, app->bindPort);
app->soap = soap_new();
//soap_init2(app->soap, SOAP_ENC_MTOM | SOAP_ENC_MIME, 0);
soap_set_namespaces(app->soap, namespaces);
soap_set_recv_logfile(app->soap, "./log/recv.xml");
soap_set_sent_logfile(app->soap, "./log/send.xml");
soap_set_test_logfile(app->soap, "./log/test.log");
app->soap->bind_flags = SO_REUSEADDR;
app->soap->accept_timeout = 10;
app->soap->recv_timeout = 10;
app->soap->send_timeout = 10;
app->soap->max_keep_alive = 30;
app->masterSocket = soap_bind(app->soap, "0.0.0.0", app->bindPort, 100);
if (app->masterSocket < 0) {
soap_print_fault(app->soap, stderr);
return -1;
}
get(app->soap);
return 0;
}
int gsoapInit()
{
return _gsoapInit(gApp);
}
int main(int argc, char *argv[])
{
return gsoapInit();
}
在中UserGetUri中获取到rtsp如下:rtsp://192.168.0.55:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif。至此通过ip地址获取rtsp地址成功,接下来剩下rtsp对接,这部分以后再补上。
该地址是否可用可以用VLC验证:
1.打开VLC,在媒体按钮中点击【打开网络串流】。
2.输入获取到rtsp地址即可播放,视频能正确播放就成功了(再次申明目前不支持带验证的)
开发过程中遇到获取设备能力参数时发生错误得到soap->error=4,
解决办法:
1.在stdsoap2.cpp中找到soap_s2byte(struct soap *soap, const char *s, char *p)函数的实现,加上注释改为if (/*s == r || *r ||*/ n < -128 || n > 127)
2.在onvif.h生成时加入-x,这个博主已经在文中讲解过了
以下博客给予博主很大帮助,特此感谢: