查看: 20334|回复: 39

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

    [复制链接]
发表于 2015-1-5 21:26:10 | 显示全部楼层 |阅读模式
本帖最后由 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/d ... ile_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目录里面(目录结构请下载对用的工程文件)

我们看到完整的工程包含这么多的WSDL如果全部实现,光是定义相应的函数就够你受得了。

3.启动一个gsoap服务
main函数里面启动了一个soap_server,这里使用8080端口sevice的的端口可以改成你自己的

  1. int gsoapInit(struct OnvifApp* app)
  2. {
  3.     if (!app->bindPort)
  4.          app->bindPort = 8080;
  5.      strcpy(app->hostAddress, "192.168.1.104");
  6.     snprintf(app->xAddr, sizeof(app->xAddr), "http://%s:%d/onvif/device_service",
  7.      app->hostAddress, app->bindPort);
  8.     fprintf(stderr, "xAddr:%s\n", app->xAddr);
  9.     snprintf(app->xAddrRoot, sizeof(app->xAddrRoot), "http://%s:%d/onvif",
  10.     app->hostAddress, app->bindPort);

  11.     soap_init2(&app->soap, SOAP_ENC_MTOM | SOAP_ENC_MIME, 0 );
  12.     soap_set_namespaces(&app->soap, namespaces);
  13.     soap_set_recv_logfile(&app->soap, "./log/recv.xml");
  14.     soap_set_sent_logfile(&app->soap, "./log/send.xml");
  15.     soap_set_test_logfile(&app->soap, "./log/test.log");
  16.     app->soap.bind_flags        = SO_REUSEADDR;
  17.     app->soap.accept_timeout    = 10;
  18.     app->soap.recv_timeout      = 10;
  19.     app->soap.send_timeout      = 10;
  20.     app->soap.max_keep_alive    = 30;
  21.     app->masterSocket = soap_bind(&app->soap, "0.0.0.0", app->bindPort, 100);
  22.     if (app->masterSocket < 0) {
  23.         soap_print_fault(&app->soap, stderr);
  24.         return -1;
  25.     }
  26.     return 0;
  27. }

  28. int main(int argc, char* argv[])
  29. {
  30. gApp = (struct OnvifApp*)calloc(1, sizeof(*gApp));
  31. if(!gApp) {
  32.   fprintf(stderr, "calloc wsapp failed\n");
  33.   return -1;
  34. }
  35. for(;;) {
  36.   if(gsoapInit(gApp)!=0)  //具体的函数定义请参考工程文件
  37.    break;
  38.   if (WsAppRun(gApp, 1) < 0)
  39.    break;
  40.   getchar();
  41.   gsoapFini(gApp);
  42.   free(gApp);
  43.   return 0;
  44. }
  45. free(gApp);
  46. return -1;
  47. }
复制代码


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

5.实现关键代码
5.1 __tds__GetCapabilities函数

  1. SOAP_FMAC5 int SOAP_FMAC6 __tds__GetCapabilities(struct soap* soap, struct _tds__GetCapabilities *tds__GetCapabilities, struct _tds__GetCapabilitiesResponse *tds__GetCapabilitiesResponse)
  2. {
  3. printf("%s:%d\n",__FUNCTION__,__LINE__);
  4.     enum tt__CapabilityCategory CapabilityCategory;
  5.     if (!tds__GetCapabilities->Category) {
  6.         CapabilityCategory = tt__CapabilityCategory__All;
  7.     } else {
  8.         CapabilityCategory =  *(tds__GetCapabilities->Category);
  9.     }
  10.     if (CapabilityCategory != tt__CapabilityCategory__All &&
  11.         CapabilityCategory != tt__CapabilityCategory__Analytics &&
  12.         CapabilityCategory != tt__CapabilityCategory__Device &&
  13.         CapabilityCategory != tt__CapabilityCategory__Events &&
  14.         CapabilityCategory != tt__CapabilityCategory__Imaging &&
  15.         CapabilityCategory != tt__CapabilityCategory__Media &&
  16.         CapabilityCategory != tt__CapabilityCategory__PTZ) {
  17.         fprintf(stderr, "The requested WSDL service category is not supported by the NVT");
  18.         //return soap_sender_fault_subcode(soap, "env:Receiver/ter:ActionNotSupported/ter:NoSuchService", "The requested WSDL service category is not supported by the NVT", NULL); ;
  19.         return SOAP_FAULT;
  20.     }
  21.     static struct tt__Capabilities tCapabilities;
  22.     soap_default_tt__Capabilities(soap, &tCapabilities);
  23.     tds__GetCapabilitiesResponse->Capabilities = &tCapabilities;
  24.     /* tt:AnalyticsCapabilities */
  25.     if ((CapabilityCategory == tt__CapabilityCategory__All) || (CapabilityCategory == tt__CapabilityCategory__Analytics)) {
  26.         static struct tt__AnalyticsCapabilities tAnalytics;
  27.         soap_default_tt__AnalyticsCapabilities(soap, &tAnalytics);
  28.         tCapabilities.Analytics = &tAnalytics;
  29.         static char Analytics_Addr[256];
  30.         sprintf(Analytics_Addr, "%sanalytics", gApp->xAddrRoot);
  31.         tAnalytics.XAddr = Analytics_Addr;
  32.         tAnalytics.RuleSupport = xsd__boolean__false_;
  33.         tAnalytics.AnalyticsModuleSupport = xsd__boolean__false_;
  34.     }
  35.     /* tt:DeviceCapabilities */
  36.     if ((CapabilityCategory == tt__CapabilityCategory__All) || (CapabilityCategory == tt__CapabilityCategory__Device)) {
  37.         static struct tt__DeviceCapabilities tDevice;
  38.         soap_default_tt__DeviceCapabilities(soap, &tDevice);
  39.         tCapabilities.Device = &tDevice;
  40.         static char Device_Addr[64];
  41.         sprintf(Device_Addr, "%sdevice_service", gApp->xAddrRoot);
  42.         tDevice.XAddr = Device_Addr;
  43.         /* tt:NetworkCapabilities */
  44.         static struct tt__NetworkCapabilities tNetwork;
  45.         soap_default_tt__NetworkCapabilities(soap, &tNetwork);
  46.         tDevice.Network = &tNetwork;
  47.         static enum xsd__boolean  enIPFilter         = xsd__boolean__true_;
  48.         static enum xsd__boolean  enZeroConfiguration = xsd__boolean__false_;
  49.         static enum xsd__boolean  enIPVersion6       = xsd__boolean__false_;
  50.         static enum xsd__boolean  enDynDNS           = xsd__boolean__true_;
  51.         tNetwork.IPFilter                 = &enIPFilter;
  52.         tNetwork.ZeroConfiguration        = &enZeroConfiguration;
  53.         tNetwork.IPVersion6               = &enIPVersion6;
  54.         tNetwork.DynDNS                   = &enDynDNS;
  55.         static struct tt__NetworkCapabilitiesExtension tNetworkCapabilitiesExtension;
  56.         soap_default_tt__NetworkCapabilitiesExtension(soap, &tNetworkCapabilitiesExtension);
  57.         tNetwork.Extension = &tNetworkCapabilitiesExtension;
  58.         static enum xsd__boolean enDot11Configuration;
  59.         tNetworkCapabilitiesExtension.Dot11Configuration = &enDot11Configuration;
  60.         /* tt:SystemCapabilities */
  61.         static struct tt__SystemCapabilities tSystemCapabilities;
  62.         soap_default_tt__SystemCapabilities(soap, &tSystemCapabilities);
  63.         tDevice.System = &tSystemCapabilities;
  64.         tSystemCapabilities.DiscoveryResolve = xsd__boolean__false_;
  65.         tSystemCapabilities.DiscoveryBye = xsd__boolean__true_;
  66.         tSystemCapabilities.RemoteDiscovery = xsd__boolean__true_;
  67.         tSystemCapabilities.SystemBackup = xsd__boolean__false_;
  68.         tSystemCapabilities.SystemLogging = xsd__boolean__false_;
  69.         tSystemCapabilities.FirmwareUpgrade = xsd__boolean__true_;
  70.         tSystemCapabilities.__sizeSupportedVersions = 3;
  71.         static struct tt__OnvifVersion tSupportedVersions[3];
  72.         tSupportedVersions[0].Major                    = 2;
  73.         tSupportedVersions[0].Minor                    = 2;
  74.         tSupportedVersions[1].Major                    = 2;
  75.         tSupportedVersions[1].Minor                    = 1;
  76.         tSupportedVersions[2].Major                    = 2;
  77.         tSupportedVersions[2].Minor                    = 0;
  78.         tSystemCapabilities.SupportedVersions = tSupportedVersions;
  79.         static struct tt__SystemCapabilitiesExtension tSystemCapabilitiesExtension;
  80.         soap_default_tt__SystemCapabilitiesExtension(soap, &tSystemCapabilitiesExtension);
  81.         tSystemCapabilities.Extension = &tSystemCapabilitiesExtension;
  82.         static enum xsd__boolean enHttpFirmwareUpgrade = xsd__boolean__true_;
  83.         static enum xsd__boolean enHttpSystemBackup = xsd__boolean__false_;
  84.         static enum xsd__boolean enHttpSystemLogging = xsd__boolean__false_;
  85.         static enum xsd__boolean enHttpSupportInformation = xsd__boolean__false_;
  86.         tSystemCapabilitiesExtension.HttpFirmwareUpgrade = &enHttpFirmwareUpgrade;
  87.         tSystemCapabilitiesExtension.HttpSystemBackup = &enHttpSystemBackup;
  88.         tSystemCapabilitiesExtension.HttpSystemLogging = &enHttpSystemLogging;
  89.         tSystemCapabilitiesExtension.HttpSupportInformation = &enHttpSupportInformation;
  90.         /* tt:IOCapabilities */
  91.         static struct tt__IOCapabilities tIO;
  92.         soap_default_tt__IOCapabilities(soap, &tIO);
  93.         tDevice.IO = &tIO;
  94.         static int iInputConnectors = 1;
  95.         tIO.InputConnectors = &iInputConnectors;
  96.         static int iRelayOutputs = 1;
  97.         tIO.RelayOutputs = &iRelayOutputs;
  98.         static struct tt__IOCapabilitiesExtension tIOCapabilitiesExtension;
  99.         soap_default_tt__IOCapabilitiesExtension(soap, &tIOCapabilitiesExtension);
  100.         tIO.Extension = &tIOCapabilitiesExtension;
  101.         static enum xsd__boolean enAuxiliary = xsd__boolean__false_;
  102.         tIOCapabilitiesExtension.Auxiliary = &enAuxiliary;
  103.         tIOCapabilitiesExtension.__sizeAuxiliaryCommands = 1;
  104.         static char chAuxiliaryCommands[] = "nothing";
  105.         static char* p = chAuxiliaryCommands;
  106.         tIOCapabilitiesExtension.AuxiliaryCommands = &p;//&chAuxiliaryCommands;
  107.         static struct tt__IOCapabilitiesExtension2 tIOCapabilitiesExtension2;
  108.         soap_default_tt__IOCapabilitiesExtension2(soap, &tIOCapabilitiesExtension2);
  109.         tIOCapabilitiesExtension.Extension = &tIOCapabilitiesExtension2;
  110.   tDevice.Security = NULL;
  111. }
  112.   tCapabilities.Events = NULL;
  113. /* tt:ImagingCapabilities */
  114.    tCapabilities.Imaging = NULL;
  115.     if ((CapabilityCategory == tt__CapabilityCategory__All) || (CapabilityCategory == tt__CapabilityCategory__Media)) {
  116.         static struct  tt__MediaCapabilities tMedia;
  117.         soap_default_tt__MediaCapabilities(soap, &tMedia);
  118.         tCapabilities.Media = &tMedia;
  119.         static char Media_Addr[64];
  120.         sprintf(Media_Addr, "%sMedia", gApp->xAddrRoot);
  121.         tMedia.XAddr = Media_Addr;
  122.         static struct tt__RealTimeStreamingCapabilities tRealTimeStreamingCapabilities;
  123.         soap_default_tt__RealTimeStreamingCapabilities(soap, &tRealTimeStreamingCapabilities);
  124.         tMedia.StreamingCapabilities = &tRealTimeStreamingCapabilities;
  125.         static enum xsd__boolean  enRTPMulticast         = xsd__boolean__false_;
  126.         static enum xsd__boolean  enRTP_USCORETCP = xsd__boolean__true_;
  127.         static enum xsd__boolean  enRTP_USCORERTSP_USCORETCP       = xsd__boolean__true_;
  128.         tRealTimeStreamingCapabilities.RTPMulticast = &enRTPMulticast;
  129.         tRealTimeStreamingCapabilities.RTP_USCORETCP = &enRTP_USCORETCP;
  130.         tRealTimeStreamingCapabilities.RTP_USCORERTSP_USCORETCP = &enRTP_USCORERTSP_USCORETCP;
  131.         static struct tt__MediaCapabilitiesExtension tMediaCapabilitiesExtension;
  132.         soap_default_tt__MediaCapabilitiesExtension(soap, &tMediaCapabilitiesExtension);
  133.         tMedia.Extension = &tMediaCapabilitiesExtension;
  134.         static struct tt__ProfileCapabilities tProfileCapabilities;
  135.         soap_default_tt__ProfileCapabilities(soap, &tProfileCapabilities);
  136.         tMediaCapabilitiesExtension.ProfileCapabilities = &tProfileCapabilities;
  137.         tProfileCapabilities.MaximumNumberOfProfiles = 3;
  138.     }
  139.     tCapabilities.PTZ = NULL;
  140. tCapabilities.Extension = NULL;
  141. fprintf(stderr, "GetCapabilities ok....\n");
  142.     return SOAP_OK;
  143. }
复制代码

5.2__trt__GetProfile函数

  1. int __trt__GetProfile(struct soap* soap, struct _trt__GetProfile *trt__GetProfile, struct _trt__GetProfileResponse *trt__GetProfileResponse)
  2. {

  3. printf("%s:%d\n",__FUNCTION__,__LINE__);
  4.     return GetProfile(soap, trt__GetProfile->ProfileToken, trt__GetProfileResponse);
  5. }
  6. int __trt__GetProfiles(struct soap* soap, struct _trt__GetProfiles *trt__GetProfiles, struct _trt__GetProfilesResponse *trt__GetProfilesResponse)
  7. {
  8.     printf("%s:%d\n",__FUNCTION__,__LINE__);
  9.     return GetProfile(soap, NULL, trt__GetProfilesResponse);
  10. }
  11. 5.3__trt__GetStreamUri函数
  12. int __trt__GetStreamUri(struct soap* soap, struct _trt__GetStreamUri *trt__GetStreamUri, struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse)
  13. {

  14. printf("%s:%d\n",__FUNCTION__,__LINE__);
  15.         if (trt__GetStreamUri->StreamSetup) {
  16.         if (trt__GetStreamUri->StreamSetup->Stream == 1) {
  17.             return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:InvalidStreamSetup", "Specification of StreamType or Transport part in StreamSetup is not supported.", NULL);
  18.         }
  19.         if (trt__GetStreamUri->StreamSetup->Transport->Protocol== 3) {
  20.             return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:InvalidStreamSetup", "The HTTP is not supported.", NULL);
  21.         }
  22.     } else {
  23.         return soap_sender_fault_subcode(soap, "ter:InvalidArgVal/ter:GetStreamUri", "Invalid GetStreamUri.", NULL);
  24.     }
  25.     static struct tt__MediaUri tMediaUri;
  26.     static char Dev_Addr[64];
  27.     const char* v4_address = "192.168.1.101";  // 修改成你自己的流媒体服务器ip
  28.     if (strcmp(trt__GetStreamUri->ProfileToken, "media_profile1") ==  0) {
  29.         sprintf(Dev_Addr, "rtsp://%s/test.264", v4_address);
  30.     } else    if (strcmp(trt__GetStreamUri->ProfileToken, "media_profile2") ==  0) {
  31.         sprintf(Dev_Addr, "rtsp://%s/live1", v4_address);
  32.     } else {
  33.         return SOAP_ERR;
  34.     }
  35.     tMediaUri.Uri                 = Dev_Addr;
  36.     tMediaUri.InvalidAfterConnect = xsd__boolean__false_;
  37.     tMediaUri.InvalidAfterReboot  = xsd__boolean__false_;
  38.     static LONG64 SmTimeout;
  39.     soap_s2xsd__duration(soap,"PT100S",&SmTimeout);
  40.     tMediaUri.Timeout             = SmTimeout;//"PT100S";
  41.     trt__GetStreamUriResponse->MediaUri = &tMediaUri;
  42.     return SOAP_OK;
  43. }
复制代码

5.4.其他函数__trt__GetVideoEncoderConfigurationOptions和__trt__GetVideoSourceConfiguration
这2个函数也很重要,你也可以不实现,现实的话视频的信息无法获取,当然我这里是随便填的,详细参见工程文件,代码比较多

6.运行测试机
由于本次我们没有实现设备发现功能,并且我们的测试机器没有流媒体服务器,将live555.exe和test.264解码出来放在同一个目录
运行live555就得到了流媒体地址(这个地址就是__trt__GetStreamUri里面填的地址):

我们需要手工的将ONVIF地址添加进来.


点开我们的测试设备,就可以开到视频流了。



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

完整的工程下载:http://pan.baidu.com/s/1bnpQnJh 附件包含测试视频,超过20M了丢个外链吧!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
楼主热帖
发表于 2015-1-5 22:53:21 | 显示全部楼层
写的东西非常有价值,先收藏!
发表于 2015-1-7 11:56:22 | 显示全部楼层
学习。感谢LZ分享。
发表于 2015-3-26 16:06:28 | 显示全部楼层
先收藏,谢谢分享。
发表于 2015-3-31 21:37:44 | 显示全部楼层
我们公司把onvif项目外包,花了几十K的键子,已经搞得差不多了。
发表于 2015-4-10 21:07:46 | 显示全部楼层
目前正接触海思项目,正需要。
发表于 2015-6-15 13:30:28 | 显示全部楼层
问下楼主,设备端(服务端)加入鉴权怎么加啊?有没有相关资料啊?
发表于 2015-9-16 18:40:31 | 显示全部楼层
厉害   真厉害   非常有价值  谢谢高手。
发表于 2015-9-28 10:23:53 | 显示全部楼层
好东西,楼楼大爱啊
发表于 2015-11-23 14:06:06 | 显示全部楼层
弱弱的问一句,onvif是怎么适应丢包率20%以上的呢?   
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2008-2017 当前位置 易百纳技术社区论坛 返回 易百纳技术社区 ( 苏ICP备14036084 )   Powered by Discuz! X3.1
快速回复 返回顶部 返回列表