ONVIF協議實現1:Server端Discovery的實現詳解
阿新 • • 發佈:2019-01-22
最近在做攝像機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.實現關鍵程式碼
這個就是迴應裝置發現工具的主要程式碼,是不是很簡單如果不適用wsddapi.c 裡面的程式碼將要寫一大推的填充程式碼具體,具體可以看最後面的參考連結。
6.實現的main函式
整體的主函式就是這樣,需要注意的一點是需要在wsdd.nsmap裡多新增一個名稱空間
{"tds", "http://www.onvif.org/ver10/device/wsdl", NULL, NULL},
7.測試結果
訊息格式:
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
附件裡面是我的完整程式碼工程,使用的是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.實現關鍵程式碼
-
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)
-
{
-
#if 0
-
printf("%s,%d\n",__FUNCTION__, __LINE__);
-
printf("MessageID:%s\n", MessageID);
-
printf("ReplyTo:%s\n", ReplyTo);
-
printf("Types:%s\n", Types);
-
printf("Scopes:%s\n", Scopes);
-
printf("MatchBy:%s\n", MatchBy);
-
#endif
-
soap_wsdd_init_ProbeMatches(soap, matches);
-
soap_wsdd_add_ProbeMatch(soap, matches,
-
"urn:uuid:464A4854-4656-5242-4530-313035394100",
-
"tdn:NetworkVideoTransmitter",
-
"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",
-
NULL,
-
"http://192.168.1.22/onvif/device_service",10);
-
return SOAP_WSDD_MANAGED;
- }
這個就是迴應裝置發現工具的主要程式碼,是不是很簡單如果不適用wsddapi.c 裡面的程式碼將要寫一大推的填充程式碼具體,具體可以看最後面的參考連結。
6.實現的main函式
-
#include "soapH.h"
-
#include "wsdd.nsmap"
-
#include "wsddapi.h"
-
#include <stdio.h>
-
#include <sys/types.h> /* See NOTES */
-
#include <sys/socket.h>
-
#include <unistd.h>
-
#include <errno.h>
-
int main(int argc, char* argv[])
-
{
-
int m, s;
-
struct ip_mreq mcast;
-
struct soap soap;
-
soap_init2(&soap, SOAP_IO_UDP | SOAP_IO_FLUSH, SOAP_IO_UDP|SOAP_IO_FLUSH);
-
soap_set_namespaces(&soap, namespaces);
-
soap_set_mode(&soap, SOAP_C_UTFSTRING);
-
soap.bind_flags = SO_REUSEADDR;
-
soap.connect_timeout = 0;
-
soap.recv_timeout = 0;
-
soap.send_timeout = 0;
-
soap_register_plugin(&soap, soap_wsa); //這個很重要,我分析了很久才得出的
-
// 開啟除錯資訊
-
soap_set_recv_logfile(&soap, "./log/recv.xml");
-
soap_set_sent_logfile(&soap, "./log/send.xml");
-
soap_set_test_logfile(&soap, "./log/test.log");
-
if(!soap_valid_socket(soap_bind(&soap, NULL, 3702, 16)))
-
{
-
soap_print_fault(&soap, stderr);
-
exit(1);
-
}
-
mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
-
mcast.imr_interface.s_addr = inet_addr("0.0.0.0");
-
if(setsockopt(soap.master, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0) {
-
printf("setsockopt error!\n");
-
return 0;
-
}
-
//成功繫結之後,便開始監聽
-
for (;;) {
-
//監聽直到有連線請求
-
soap_wsdd_listen(&soap, 0);
-
soap_destroy(&soap);
-
soap_end(&soap);
-
fprintf(stderr, "Socket connection successful: slave socket = %d\n", s);
-
}
-
soap_done(&soap);
-
return 0;
-
}
整體的主函式就是這樣,需要注意的一點是需要在wsdd.nsmap裡多新增一個名稱空間
{"tds", "http://www.onvif.org/ver10/device/wsdl", NULL, NULL},
7.測試結果
訊息格式:
-
<?xml version="1.0" encoding="UTF-8"?>
-
<SOAP-ENV:Envelope
-
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
-
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" x
-
mlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
-
xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery"
-
xmlns:tdn="http://www.onvif.org/ver10/network/wsdl"
-
xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
-
<SOAP-ENV:Header>
-
<wsa:MessageID>urn:uuid:54a3c06c-96c8-47ca-b4b0-dc5119495cff</wsa:MessageID>
-
<wsa:RelatesTo>uuid:5e054455-4d8e-4060-8ef8-e1cb9bfd7940</wsa:RelatesTo>
-
<wsa:To SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
-
<wsa:Action SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsa:Action>
-
<wsdd:AppSequence MessageNumber="3" InstanceId="0"></wsdd:AppSequence>
-
</SOAP-ENV:Header><SOAP-ENV:Body>
-
<wsdd:ProbeMatches>
-
<wsdd:ProbeMatch>
-
<wsa:EndpointReference>
-
<wsa:Address>urn:uuid:464A4854-4656-5242-4530-313035394100</wsa:Address>
-
</wsa:EndpointReference>
-
<wsdd:Types>tdn:NetworkVideoTransmitter</wsdd:Types>
-
<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>
-
<wsdd:XAddrs>http://192.168.1.230/onvif/device_service</wsdd:XAddrs>
-
<wsdd:MetadataVersion>10</wsdd:MetadataVersion>
-
</wsdd:ProbeMatch>
-
</wsdd:ProbeMatches>
-
</SOAP-ENV:Body>
-
</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
【from:http://www.ebaina.com/bbs/thread-4929-1-1.html】