Loading...
首页问答    

ONVIF协议实现2:码流地址的获取并在ONVIF Device Manager测试成功

goodman
goodman  发布于 2015-01-05 21:26:10 26704

本帖最后由 goodman 于 2015-1-5 21:26 编辑

工作平台及工具: Ubuntu:12.04 + gcc + OnvifTestTool12.12 gsoap下载:http://www.cs.fsu.edu/~engelen/soap.html PC:live555.exe + test.264 目前的最新版本为:gsoap2.8.21 通过设备发现的实现,对ONVIF有了很多的了解,也对其开发步骤有了很好地认识,现在实现码流的获取也不难按如下步骤即可:

1.相关概念和前置知识 Profile:参见http://www.onvif.org/Portals/0/documents/op/ONVIF_Profile_Policy_v2-0.pdf 1.1、GetCapabilities #获取设备能力表 1.2、GetProfiles # 获取设备的Profile 1.3、GetStreamUri #填充rtsp路径如:rtsp://192.168.1.101/test.264 1.4、RTSP服务器 #因为我们的设备端不是摄像机所以要借助live555来模拟 1.5、GetVideoSourceConfiguration #获取视频源配置信息 1.6、GetVideoEncoderConfigurationOptions #获取编码配置选项 下面我们的框架和具体实现围绕这几个函数开实现。

2.生成框架代码 执行如下代码(我的工程里面有个gen.sh也可以生成代码): ./wsdl2h -o onvif.h -c -s -t ./typemap.dat http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl ./soapcpp2 -c onvif.h -x -d ./ -I ${HOME}/workspace/source/gsoap-2.8/gsoap/import -I ${HOME}/workspace/source/gsoap-2.8/gsoap/ 这里我们最小化实现,只包含这个2个其他的都排除在外,这样生成的代码才最小,将生成的代码从tools目录移动到src目录里面(目录结构请下载对用的工程文件) [attach]2081[/attach] 我们看到完整的工程包含这么多的WSDL如果全部实现,光是定义相应的函数就够你受得了。

3.启动一个gsoap服务 main函数里面启动了一个soap_server,这里使用8080端口sevice的的端口可以改成你自己的 [code] int gsoapInit(struct OnvifApp* app) { if (!app->bindPort) app->bindPort = 8080; strcpy(app->hostAddress, "192.168.1.104"); 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);

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;
}
return 0;

}

int main(int argc, char argv[]) { gApp = (struct OnvifApp)calloc(1, sizeof(*gApp)); if(!gApp) { fprintf(stderr, "calloc wsapp failed\n"); return -1; } for(;;) { if(gsoapInit(gApp)!=0) //具体的函数定义请参考工程文件 break; if (WsAppRun(gApp, 1) < 0) break; getchar(); gsoapFini(gApp); free(gApp); return 0; } free(gApp); return -1; } [/code]

4.整理需要实现的函数 上面的框架代码实现好后,一编译就会报一大推的链接错误,我们需要将这些函数实现,我们按命名空间来讲这些函数分类到2个单独的文件离去,分别为 tds_ssom.c 和 trt_ssom.c具体的内容就不贴了比较多,可以参看我工程里的代码。然后再Makefile里面把这2个文件添加上,就能找到这些引用了,这样 做的好处是,每次需要添加第三个WSDL的时候可以保证前面整理的函数都是可用的,这样可以避免很多重复的劳动。

5.实现关键代码 5.1 tdsGetCapabilities函数 [code] SOAP_FMAC5 int SOAP_FMAC6 tdsGetCapabilities(struct soap soap, struct _tds__GetCapabilities tdsGetCapabilities, struct _tdsGetCapabilitiesResponse *tdsGetCapabilitiesResponse) { printf("%s:%d\n",FUNCTION,LINE); enum ttCapabilityCategory CapabilityCategory; if (!tdsGetCapabilities->Category) { CapabilityCategory = ttCapabilityCategoryAll; } else { CapabilityCategory = (tdsGetCapabilities->Category); } if (CapabilityCategory != ttCapabilityCategoryAll && CapabilityCategory != ttCapabilityCategoryAnalytics && CapabilityCategory != ttCapabilityCategoryDevice && CapabilityCategory != ttCapabilityCategoryEvents && CapabilityCategory != ttCapabilityCategoryImaging && CapabilityCategory != ttCapabilityCategoryMedia && CapabilityCategory != ttCapabilityCategoryPTZ) { fprintf(stderr, "The requested WSDL service category is not supported by the NVT"); //return soap_sender_fault_subcode(soap, "env:Receiver/ter:ActionNotSupported/ter:NoSuchService", "The requested WSDL service category is not supported by the NVT", NULL); ; return SOAP_FAULT; } static struct ttCapabilities tCapabilities; soap_default_ttCapabilities(soap, &tCapabilities); tdsGetCapabilitiesResponse->Capabilities = &tCapabilities; / tt:AnalyticsCapabilities */ if ((CapabilityCategory == ttCapabilityCategoryAll) || (CapabilityCategory == ttCapabilityCategoryAnalytics)) { static struct ttAnalyticsCapabilities tAnalytics; soap_default_ttAnalyticsCapabilities(soap, &tAnalytics); tCapabilities.Analytics = &tAnalytics; static char Analytics_Addr[256]; sprintf(Analytics_Addr, "%sanalytics", gApp->xAddrRoot); tAnalytics.XAddr = Analytics_Addr; tAnalytics.RuleSupport = xsdbooleanfalse; tAnalytics.AnalyticsModuleSupport = xsdbooleanfalse; } / tt:DeviceCapabilities / if ((CapabilityCategory == ttCapabilityCategoryAll) || (CapabilityCategory == ttCapabilityCategoryDevice)) { static struct ttDeviceCapabilities tDevice; soap_default_ttDeviceCapabilities(soap, &tDevice); tCapabilities.Device = &tDevice; static char Device_Addr[64]; sprintf(Device_Addr, "%sdevice_service", gApp->xAddrRoot); tDevice.XAddr = Device_Addr; / tt:NetworkCapabilities / static struct ttNetworkCapabilities tNetwork; soap_defaultttNetworkCapabilities(soap, &tNetwork); tDevice.Network = &tNetwork; static enum xsdboolean enIPFilter = xsdbooleantrue; static enum xsdboolean enZeroConfiguration = xsdbooleanfalse_; static enum xsdboolean enIPVersion6 = xsdboolean_false; static enum xsdboolean enDynDNS = xsdbooleantrue_; tNetwork.IPFilter = &enIPFilter; tNetwork.ZeroConfiguration = &enZeroConfiguration; tNetwork.IPVersion6 = &enIPVersion6; tNetwork.DynDNS = &enDynDNS; static struct ttNetworkCapabilitiesExtension tNetworkCapabilitiesExtension; soap_default_ttNetworkCapabilitiesExtension(soap, &tNetworkCapabilitiesExtension); tNetwork.Extension = &tNetworkCapabilitiesExtension; static enum xsdboolean enDot11Configuration; tNetworkCapabilitiesExtension.Dot11Configuration = &enDot11Configuration; / tt:SystemCapabilities / static struct ttSystemCapabilities tSystemCapabilities; soap_default_ttSystemCapabilities(soap, &tSystemCapabilities); tDevice.System = &tSystemCapabilities; tSystemCapabilities.DiscoveryResolve = xsdbooleanfalse; tSystemCapabilities.DiscoveryBye = xsdbooleantrue; tSystemCapabilities.RemoteDiscovery = xsdbooleantrue; tSystemCapabilities.SystemBackup = xsdbooleanfalse; tSystemCapabilities.SystemLogging = xsdbooleanfalse; tSystemCapabilities.FirmwareUpgrade = xsdbooleantrue; tSystemCapabilities.sizeSupportedVersions = 3; static struct ttOnvifVersion tSupportedVersions[3]; tSupportedVersions[0].Major = 2; tSupportedVersions[0].Minor = 2; tSupportedVersions[1].Major = 2; tSupportedVersions[1].Minor = 1; tSupportedVersions[2].Major = 2; tSupportedVersions[2].Minor = 0; tSystemCapabilities.SupportedVersions = tSupportedVersions; static struct ttSystemCapabilitiesExtension tSystemCapabilitiesExtension; soap_defaultttSystemCapabilitiesExtension(soap, &tSystemCapabilitiesExtension); tSystemCapabilities.Extension = &tSystemCapabilitiesExtension; static enum xsdboolean enHttpFirmwareUpgrade = xsdbooleantrue; static enum xsdboolean enHttpSystemBackup = xsdbooleanfalse_; static enum xsdboolean enHttpSystemLogging = xsdboolean_false; static enum xsdboolean enHttpSupportInformation = xsdbooleanfalse_; tSystemCapabilitiesExtension.HttpFirmwareUpgrade = &enHttpFirmwareUpgrade; tSystemCapabilitiesExtension.HttpSystemBackup = &enHttpSystemBackup; tSystemCapabilitiesExtension.HttpSystemLogging = &enHttpSystemLogging; tSystemCapabilitiesExtension.HttpSupportInformation = &enHttpSupportInformation; / tt:IOCapabilities / static struct ttIOCapabilities tIO; soap_default_ttIOCapabilities(soap, &tIO); tDevice.IO = &tIO; static int iInputConnectors = 1; tIO.InputConnectors = &iInputConnectors; static int iRelayOutputs = 1; tIO.RelayOutputs = &iRelayOutputs; static struct ttIOCapabilitiesExtension tIOCapabilitiesExtension; soap_default_ttIOCapabilitiesExtension(soap, &tIOCapabilitiesExtension); tIO.Extension = &tIOCapabilitiesExtension; static enum xsdboolean enAuxiliary = xsdbooleanfalse_; tIOCapabilitiesExtension.Auxiliary = &enAuxiliary; tIOCapabilitiesExtension.sizeAuxiliaryCommands = 1; static char chAuxiliaryCommands[] = "nothing"; static char* p = chAuxiliaryCommands; tIOCapabilitiesExtension.AuxiliaryCommands = &p;//&chAuxiliaryCommands; static struct ttIOCapabilitiesExtension2 tIOCapabilitiesExtension2; soap_default_ttIOCapabilitiesExtension2(soap, &tIOCapabilitiesExtension2); tIOCapabilitiesExtension.Extension = &tIOCapabilitiesExtension2; tDevice.Security = NULL; } tCapabilities.Events = NULL; / tt:ImagingCapabilities / tCapabilities.Imaging = NULL; if ((CapabilityCategory == ttCapabilityCategoryAll) || (CapabilityCategory == ttCapabilityCategoryMedia)) { static struct ttMediaCapabilities tMedia; soap_default_tt__MediaCapabilities(soap, &tMedia); tCapabilities.Media = &tMedia; static char Media_Addr[64]; sprintf(Media_Addr, "%sMedia", gApp->xAddrRoot); tMedia.XAddr = Media_Addr; static struct ttRealTimeStreamingCapabilities tRealTimeStreamingCapabilities; soap_defaultttRealTimeStreamingCapabilities(soap, &tRealTimeStreamingCapabilities); tMedia.StreamingCapabilities = &tRealTimeStreamingCapabilities; static enum xsdboolean enRTPMulticast = xsdbooleanfalse; static enum xsdboolean enRTPUSCORETCP = xsdbooleantrue; static enum xsdboolean enRTP_USCORERTSPUSCORETCP = xsdbooleantrue; tRealTimeStreamingCapabilities.RTPMulticast = &enRTPMulticast; tRealTimeStreamingCapabilities.RTP_USCORETCP = &enRTP_USCORETCP; tRealTimeStreamingCapabilities.RTP_USCORERTSP_USCORETCP = &enRTP_USCORERTSP_USCORETCP; static struct ttMediaCapabilitiesExtension tMediaCapabilitiesExtension; soap_default_ttMediaCapabilitiesExtension(soap, &tMediaCapabilitiesExtension); tMedia.Extension = &tMediaCapabilitiesExtension; static struct ttProfileCapabilities tProfileCapabilities; soap_default_ttProfileCapabilities(soap, &tProfileCapabilities); tMediaCapabilitiesExtension.ProfileCapabilities = &tProfileCapabilities; tProfileCapabilities.MaximumNumberOfProfiles = 3; } tCapabilities.PTZ = NULL; tCapabilities.Extension = NULL; fprintf(stderr, "GetCapabilities ok....\n"); return SOAP_OK; } [/code] 5.2trtGetProfile函数 [code] int trtGetProfile(struct soap soap, struct _trt__GetProfile trt__GetProfile, struct _trtGetProfileResponse *trtGetProfileResponse) {

printf("%s:%d\n",FUNCTION,LINE); return GetProfile(soap, trtGetProfile->ProfileToken, trtGetProfileResponse); } int trtGetProfiles(struct soap soap, struct _trt__GetProfiles trtGetProfiles, struct _trtGetProfilesResponse *trtGetProfilesResponse) { printf("%s:%d\n",FUNCTION,LINE); return GetProfile(soap, NULL, trtGetProfilesResponse); } 5.3trtGetStreamUri函数 int trt__GetStreamUri(struct soap* soap, struct _trtGetStreamUri trt__GetStreamUri, struct _trt__GetStreamUriResponse trt__GetStreamUriResponse) {

printf("%s:%d\n",FUNCTION,LINE); if (trtGetStreamUri->StreamSetup) { if (trtGetStreamUri->StreamSetup->Stream == 1) { return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:InvalidStreamSetup", "Specification of StreamType or Transport part in StreamSetup is not supported.", NULL); } if (trtGetStreamUri->StreamSetup->Transport->Protocol== 3) { return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:InvalidStreamSetup", "The HTTP is not supported.", NULL); } } else { return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:GetStreamUri", "Invalid GetStreamUri.", NULL); } static struct ttMediaUri tMediaUri; static char Dev_Addr[64]; const char* v4_address = "192.168.1.101"; // 修改成你自己的流媒体服务器ip if (strcmp(trtGetStreamUri->ProfileToken, "media_profile1") == 0) { sprintf(Dev_Addr, "rtsp://%s/test.264", v4_address); } else if (strcmp(trt__GetStreamUri->ProfileToken, "media_profile2") == 0) { sprintf(Dev_Addr, "rtsp://%s/live1", v4_address); } else { return SOAP_ERR; } tMediaUri.Uri = Dev_Addr; tMediaUri.InvalidAfterConnect = xsdbooleanfalse; tMediaUri.InvalidAfterReboot = xsdbooleanfalse; static LONG64 SmTimeout; soap_s2xsdduration(soap,"PT100S",&SmTimeout); tMediaUri.Timeout = SmTimeout;//"PT100S"; trtGetStreamUriResponse->MediaUri = &tMediaUri; return SOAP_OK; } [/code] 5.4.其他函数trtGetVideoEncoderConfigurationOptions和trt__GetVideoSourceConfiguration 这2个函数也很重要,你也可以不实现,现实的话视频的信息无法获取,当然我这里是随便填的,详细参见工程文件,代码比较多

6.运行测试机 由于本次我们没有实现设备发现功能,并且我们的测试机器没有流媒体服务器,将live555.exe和test.264解码出来放在同一个目录 运行live555就得到了流媒体地址(这个地址就是trtGetStreamUri里面填的地址): [attach]2082[/attach] 我们需要手工的将ONVIF地址添加进来. [attach]2083[/attach]

点开我们的测试设备,就可以开到视频流了。 [attach]2084[/attach] [attach]2085[/attach]

7.总结 这样基于ONVIF的流媒体地址搜索就实现了,当然过程也耗费了一点时间调试,里面我基本全部使用了静态变量, 应该使用soap_malloc来动态申请,我没有完全搞明白soap的内存管理机制,所以使用保守的做法,至少不会出现内存泄露。 框架生成好再开发ONVIF协议的就是填充结构体,每个命令就要填一大推的结构体。所有的命令在下面可以看到 http://www.onvif.org/onvif/ver20/util/operationIndex.html 点开每一个都有详细的说明,只要按这个说明填好结构体,ONVIF协议的开发就没有什么难度。tds_ssom.c里面包含了一些基本信息网络等函数, 实现他们就可以在Device Manager的信息栏显示了: [attach]2086[/attach] 完整的工程下载:http://pan.baidu.com/s/1bnpQnJh附件包含测试视频,超过20M了丢个外链吧!

联系方式企鹅:2757364047
_nucong  发布于 2015-01-05 22:53:21
写的东西非常有价值,先收藏!
0
beegeen  发布于 2015-01-07 11:56:22
学习。感谢LZ分享。
0
hhzh126  发布于 2015-03-26 16:06:28
先收藏,谢谢分享。
0
ipook168  发布于 2015-03-31 21:37:44
我们公司把onvif项目外包,花了几十K的键子,已经搞得差不多了。
0
xy309428529  发布于 2015-04-10 21:07:46
目前正接触海思项目,正需要。
0
goodplayer  发布于 2015-06-15 13:30:28
问下楼主,设备端(服务端)加入鉴权怎么加啊?有没有相关资料啊?
0
iamheimawangzi  发布于 2015-09-16 18:40:31
厉害   真厉害   非常有价值  谢谢高手。
0
sxsong  发布于 2015-09-28 10:23:53
好东西,楼楼大爱啊
0
liujiaqi13  发布于 2015-11-23 14:06:06
弱弱的问一句,onvif是怎么适应丢包率20%以上的呢?   
0
admin  发布于 2016-06-30 13:42:08
66666666666666666
0
rafael_wl  发布于 2016-07-07 19:34:22
[quote][url=forum.php?mod=redirect&goto=findpost&pid=32882&ptid=4965]admin 发表于 2016-6-30 13:42[/url]
66666666666666666[/quote]

你的账号是admin 这个。。。。。
0
VVVV88  发布于 2016-08-31 14:48:34
先收藏,谢谢分享。
0
zltkf  发布于 2017-02-04 18:20:42
感谢楼主分享。很有帮助。
0
lihu  发布于 2017-02-05 10:34:14
感谢楼主分享
0
toread  发布于 2017-04-14 16:56:29
完整的工程下载:[url]http://pan.baidu.com/s/1bnpQnJh[/url] 附件包含测试视频,超过20M了丢个外链吧!
亲,这个外链已经无效了,可以重新发一个吗?谢谢
0
lyy111  发布于 2017-05-15 18:18:34
很不错,参考一下。
0
zhanghongquan  发布于 2017-05-15 20:01:45
很有价值的东西
0
qq328848298  发布于 2017-06-07 23:22:26
亲,这个外链已经无效了,可以重新发一个吗?谢谢
0
hero  发布于 2017-07-04 15:10:37

写的东西非常有价值,先收藏!
0
sbrr123  发布于 2017-07-19 16:45:08
onvif 可以直接用 IE直接访问吗?
0
lmf1793598583  发布于 2017-07-25 11:13:14
感谢楼主无私分享
0
MiSign  发布于 2018-05-15 15:28:31
ONVIF 接口调用,是否有必要,调用一个接口,创建一个SOAP对象?
0
hero  发布于 2018-05-16 18:47:22
谢谢分享啊,不错的资料:lol
0
uhunr  发布于 2018-07-09 10:09:04
顶一个,啦啦啦:victory:
0
qn1517969951  发布于 2018-09-17 10:53:00
学习学习。感谢楼主分享
0
stoneLinux  发布于 2018-09-17 13:38:38
感谢楼主的无私奉献和辛勤付出!
0
qn1511544493  发布于 2018-10-23 22:22:57
哪此哥们下过此套代码的,能分享下吗?
0
hero  发布于 2018-11-14 08:39:57
先收藏,谢谢分享。
0
tfh_jpcd  发布于 2018-11-22 14:35:16
学习学习,感谢楼主分享
0
wzawz  发布于 2019-04-29 13:39:24
链接失效了, 请问楼主还能补一下吗?
0
reynold192  发布于 2019-10-11 11:27:06
LZ链接失效了。还能分享下吗
0
dandy_777  发布于 2019-12-30 15:17:39
[分享] ONVIF协议实现2:码流地址的获取并在ONVIF Device Manager测试成功

很棒的分享
0
qn1576568042  发布于 2020-01-08 10:35:08
gsoap/stdsoap2.o: In function `soap_http_content_type':
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18900: undefined reference to `newlocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18901: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18904: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18920: undefined reference to `newlocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18921: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:18929: undefined reference to `uselocale'
gsoap/stdsoap2.o: In function `soap_set_fault':
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19031: undefined reference to `newlocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19031: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19031: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19069: undefined reference to `newlocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19073: undefined reference to `uselocale'
/home/software/Desktop/onvif/gsoap/stdsoap2.c:19079: undefined reference to `uselocale'
编译gsoap出现此类错误,哪位大佬帮忙解达啊
0
qn1513131564  发布于 2020-01-15 14:15:38
都是大神啊,向大神学习。
0
qn1551684254  发布于 2020-03-19 21:01:12
楼主,百度地址失效了
0
xyyangkun  发布于 2020-04-21 16:10:56

楼主,百度地址失效了
0
mao20602130  发布于 2020-04-23 20:11:46
学习了,谢谢分享!!!
0
123zxc  发布于 2020-07-16 20:52:43
谁有这个链接
0
途行者阿杜  发布于 2015-01-08 14:18:11
赞,:(:):):):):):)
0
给我一个支点,让我撬动地球~
liucuqi  发布于 2015-05-20 15:43:10
谢谢分享!
0
小虫洞  发布于 2015-10-08 18:34:49
感谢分享!
0
kgui  发布于 2015-11-28 14:39:44
感谢分享:)
0
lyd964201  发布于 2016-04-20 13:27:40
谢谢分享!
0
hero  发布于 2018-06-27 14:33:34
marking下:lol
0
zx_dvr  发布于 2018-07-09 09:39:26
很有帮助:(:$
0
itk  发布于 2018-07-31 16:50:45
学习了。。。
0
hero  发布于 2018-11-01 10:15:03
marking............
0
hero  发布于 2018-11-14 09:45:02
marking.........
0
qn1554878403  发布于 2019-04-10 16:57:13
MARK 感谢 !!!!!!!!!!!!
0
qinjinghua2012  发布于 2020-12-16 11:06:16
感谢分享!
0
添加附件:文件小于20M
上传
易百纳技术社区
确定要删除此文章、专栏、评论吗?
确定
取消
易百纳技术社区