查看: 18566|回复: 41

[分享] ONVIF协议实现1:Server端Discovery的实现详解

    [复制链接]
发表于 2014-12-31 21:32:49 | 显示全部楼层 |阅读模式
本帖最后由 goodman 于 2014-12-31 21:37 编辑

最近在做摄像机ONVIF的协议,看了几天文档调了点代码和大家分享下,下步准备实现RTSP的流地址的获取。
附件里面是我的完整代码工程,使用的是arm-linux-gcc,代码也可以在X86的Linux上跑,只要将Makefile里面额CC=arm-linux-gcc换成gcc即可

工作平台及工具:
    Ubuntu:12.04 + arm-linux-gcc/gcc + OnvifTestTool12.12
    gsoap下载:http://www.cs.fsu.edu/~engelen/soap.html
    目前的最新版本为:gsoap2.8.21

1.搞定工具:
首先需要做的是提取工具一共有3样:
soapcpp2  wsdl2h  typemap.dat
我下载的的gsoap里面的typemap.dat已经包含了WS-Discovery的支持因此不需要再像网上那样去添加ONVIF支持了
下载好的源码解压出来,到gsoap-2.8/gsoap/bin目录里面一看,没有我们需要的soapcpp2  wsdl2h:

没有我们只好自己编译一个了,看了下他的README.txt里面有这么一句话(PS:我比较喜欢看项目里面的README能帮助我们解决很多问题).
For other platforms: see installation instructions INSTALL.txt in the root dir.
到根目录里面看下INSTALL.txt知道了怎么编译了
cd gsoap/src
make -f MakefileManual
cd gsoap/wsdl
make -f MakefileManual

2. Remotediscovery.wsdl产生onvif.h头文件
对于这里我们只要实现设备发现的功能,所以我们只需要Remotediscovery.wsdl这一个wsdl就可以了
./wsdl2h -o onvif.h -c -s -k -t ./typemap.dat http://www.onvif.org/onvif/ver10 ... emotediscovery.wsdl
生成的时候会报SOAP_ENV__Fault
重定义的错误,将gsoap-2.8/gsoap/import/wsa5.h里面的第277行的SOAP_ENV__Fault改为SOAP_ENV__Fault_ex就可以了

3.生成ONVIF的框架代码
./soapcpp2 -c onvif.h   -x  -d ./ -I ${HOME}/workspace/source/gsoap-2.8/gsoap/import -I ${HOME}/workspace/source/gsoap-2.8/gsoap/
这里的${HOME}/workspace/source/gsoap-2.8/gsoap/import 和${HOME}/workspace/source/gsoap-2.8/gsoap/注意修成自己的。
这里会报

4.拷贝相关代码
建立一个onvif_test的目录将${HOME}/workspace/source/gsoap-2.8如下文件拷贝过来过来
     gsoap/dom.c
    gsoap/stdsoap2.c
    gsoap/stdsoap2.h
    gsoap/custom/duration.c
    gsoap/plugin/mecevp.c
    gsoap/plugin/mecevp.h
    gsoap/plugin/smdevp.c
    gsoap/plugin/smdevp.h
    gsoap/plugin/threads.c
    gsoap/plugin/threads.h
    gsoap/plugin/wsaapi.c
    gsoap/plugin/wsaapi.h
    gsoap/plugin/wsseapi.c
    gsoap/plugin/wsseapi.h
    gsoap/plugin/wsddapi.c
    gsoap/plugin/ wsddapi.h
这些代码会帮我很多工作的,下面就知道了

5.实现关键代码
  1. soap_wsdd_mode wsdd_event_Probe(struct soap *soap, const char *MessageID, const char *ReplyTo, const char *Types, const char *Scopes, const char *MatchBy, struct wsdd__ProbeMatchesType *matches)
  2. {
  3. #if 0
  4. printf("%s,%d\n",__FUNCTION__, __LINE__);
  5. printf("MessageID:%s\n", MessageID);
  6. printf("ReplyTo:%s\n", ReplyTo);
  7. printf("Types:%s\n", Types);
  8. printf("Scopes:%s\n", Scopes);
  9. printf("MatchBy:%s\n", MatchBy);
  10. #endif
  11. soap_wsdd_init_ProbeMatches(soap, matches);
  12. soap_wsdd_add_ProbeMatch(soap, matches,
  13. "urn:uuid:464A4854-4656-5242-4530-313035394100",
  14. "tdn:NetworkVideoTransmitter",
  15. "onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/type/video_analytics onvif://www.onvif.org/hardware/HD-IPCAM onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/IPCAM",
  16. NULL,
  17. "http://192.168.1.22/onvif/device_service",10);
  18. return SOAP_WSDD_MANAGED;

  19. }
复制代码

这个就是回应设备发现工具的主要代码,是不是很简单如果不适用wsddapi.c 里面的代码将要写一大推的填充代码具体,具体可以看最后面的参考链接。
6.实现的main函数

  1. #include "soapH.h"  
  2. #include "wsdd.nsmap"   
  3. #include "wsddapi.h"
  4. #include <stdio.h>
  5. #include <sys/types.h>          /* See NOTES */
  6. #include <sys/socket.h>
  7. #include <unistd.h>
  8. #include <errno.h>
  9. int main(int argc, char* argv[])
  10. {
  11.     int m, s;
  12.     struct ip_mreq mcast;
  13.     struct soap soap;
  14.     soap_init2(&soap, SOAP_IO_UDP | SOAP_IO_FLUSH, SOAP_IO_UDP|SOAP_IO_FLUSH);  
  15.     soap_set_namespaces(&soap, namespaces);
  16.     soap_set_mode(&soap, SOAP_C_UTFSTRING);
  17.     soap.bind_flags        = SO_REUSEADDR;
  18.     soap.connect_timeout   = 0;
  19.     soap.recv_timeout      = 0;
  20.     soap.send_timeout      = 0;
  21.     soap_register_plugin(&soap, soap_wsa);  //这个很重要,我分析了很久才得出的
  22. // 打开调试信息
  23.     soap_set_recv_logfile(&soap, "./log/recv.xml");
  24.     soap_set_sent_logfile(&soap, "./log/send.xml");
  25.     soap_set_test_logfile(&soap, "./log/test.log");
  26.     if(!soap_valid_socket(soap_bind(&soap, NULL, 3702, 16)))
  27.      {
  28.   soap_print_fault(&soap, stderr);
  29.   exit(1);
  30.      }
  31.     mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
  32.     mcast.imr_interface.s_addr = inet_addr("0.0.0.0");
  33.     if(setsockopt(soap.master, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0) {
  34.         printf("setsockopt error!\n");
  35.         return 0;
  36.     }  
  37.     //成功绑定之后,便开始监听
  38.     for (;;) {
  39.         //监听直到有连接请求
  40.         soap_wsdd_listen(&soap, 0);
  41.         soap_destroy(&soap);
  42.         soap_end(&soap);
  43.         fprintf(stderr, "Socket connection successful: slave socket = %d\n", s);
  44.     }
  45.     soap_done(&soap);
  46.     return 0;
  47. }
复制代码

整体的主函数就是这样,需要注意的一点是需要在wsdd.nsmap里多添加一个命名空间
{"tds", "http://www.onvif.org/ver10/device/wsdl", NULL, NULL},

7.测试结果

消息格式:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <SOAP-ENV:Envelope
  3. xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
  4. xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" x
  5. mlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  7. xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  8. xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery"
  9. xmlns:tdn="http://www.onvif.org/ver10/network/wsdl"
  10. xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
  11. <SOAP-ENV:Header>
  12. <wsa:MessageID>urn:uuid:54a3c06c-96c8-47ca-b4b0-dc5119495cff</wsa:MessageID>
  13. <wsa:RelatesTo>uuid:5e054455-4d8e-4060-8ef8-e1cb9bfd7940</wsa:RelatesTo>
  14. <wsa:To SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
  15. <wsa:Action SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsa:Action>
  16. <wsdd:AppSequence MessageNumber="3" InstanceId="0"></wsdd:AppSequence>
  17. </SOAP-ENV:Header><SOAP-ENV:Body>
  18. <wsdd:ProbeMatches>
  19. <wsdd:ProbeMatch>
  20. <wsa:EndpointReference>
  21. <wsa:Address>urn:uuid:464A4854-4656-5242-4530-313035394100</wsa:Address>
  22. </wsa:EndpointReference>
  23. <wsdd:Types>tdn:NetworkVideoTransmitter</wsdd:Types>
  24. <wsdd:Scopes>onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/type/video_analytics onvif://www.onvif.org/hardware/HD-IPCAM .. onvif://www.onvif.org/location/country/china onvif://www.onvif.org/name/IPCAM</wsdd:Scopes>
  25. <wsdd:XAddrs>http://192.168.1.230/onvif/device_service</wsdd:XAddrs>
  26. <wsdd:MetadataVersion>10</wsdd:MetadataVersion>
  27. </wsdd:ProbeMatch>
  28. </wsdd:ProbeMatches>
  29. </SOAP-ENV:Body>
  30. </SOAP-ENV:Envelope>
复制代码

8.过程总结
ONVIF这个设备发现的实现耗费了我好几天近去调试和阅读相关文档,虽然网上有很多的资料可供参考,但是真正去理解所有的东西还是要花上一些功夫的
如xml的命名空间,gsoap的消息格式和wsdl等,这些东西还是很耗费时间的,尤其是调试出问题后怎么去解决问题。
PC端抓包收到了,但是还是发现不了设备?
可能出现的问题1:
"tdn:NetworkVideoTransmitter"填的不对他的前缀不是随便填的是和命名空间相关的
可能出现的问题2:
wsa:RelatesTo这个字段没有,这个没有的原因是因为我们使用到了 int  soap_wsa_reply(struct soap *soap, const char *id, const char *action) 函数
而这个函数里面里面有这些代码
  struct soap_wsa_data *data = (struct soap_wsa_data*)soap_lookup_plugin(soap, soap_wsa_id);
  struct SOAP_ENV__Header *oldheader, *newheader;
  DBGFUN1("soap_wsa_reply", "action=%s", action?action:"(null)");
  if (!data)    // 这里总是返回0
    return soap->error = SOAP_PLUGIN_ERROR;
网上收到了一些做法是将
  if (!data)    return soap->error = SOAP_PLUGIN_ERROR;
向后移一移解决的,填充了wsa:RelatesTo再判断,我觉得问题不是这么来的,最终我调试和血毒代码后,加上了   soap_register_plugin(&soap, soap_wsa);  
完美解决,同时知道了为什么这么做,这很重要。见gsoap-2.8/gsoap/doc/wsa里面的文档

参考链接:
http://blog.csdn.net/ghostyu/article/details/8182516
http://www.360doc.com/content/14/0828/15/9075092_405360193.shtml

本帖子中包含更多资源

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

x

评分

1

查看全部评分

楼主热帖
 楼主| 发表于 2015-2-2 14:17:58 | 显示全部楼层
地狱的猪 发表于 2015-2-2 12:29
用海思的arm-hisiv100-linux-gcc下编译报错
stdsoap2.c.text+0x20758):对‘__isnan’未定义的引用
是 ...

arm-hisiv100-linux-gcc 用uclib的在链接库的时候加上-lm就能解决了
发表于 2015-1-1 10:50:21 | 显示全部楼层
good 大赞 。 2014年 最后一天还干到这么晚。
发表于 2015-1-4 09:18:26 | 显示全部楼层
很详细。这个要赞。
发表于 2015-1-4 15:18:25 | 显示全部楼层
这个必须有奖品 ,奖励goodman 100元 话费,请站内你的手机号,希望更多精品。
发表于 2015-1-9 15:49:55 | 显示全部楼层
这个必须保存啊 ,赞一个。
发表于 2015-1-16 11:04:28 | 显示全部楼层
非常感谢大神
发表于 2015-1-24 09:20:54 | 显示全部楼层
这个很有意义
发表于 2015-1-28 08:54:37 | 显示全部楼层
好帖子,感谢大神
发表于 2015-2-1 14:29:46 | 显示全部楼层
这个厉害的,赞一个!
发表于 2015-2-2 12:29:16 | 显示全部楼层
用海思的arm-hisiv100-linux-gcc下编译报错
stdsoap2.c.text+0x20758):对‘__isnan’未定义的引用
是什么原因呢?
发表于 2015-5-20 15:36:32 | 显示全部楼层
非常棒,可以好好借鉴下!
发表于 2015-6-15 13:29:28 | 显示全部楼层
问下楼主,设备端(服务端)加入鉴权怎么加啊?有没有相关资料啊?
发表于 2015-7-30 17:16:24 | 显示全部楼层
很不错,最近正在弄 ONVIF 的东西!!谢谢!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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