linux设备上的Onvif 实现21:解决大华摄像头无法使用问题
好长时间没有再写该系列文章了,最近刚好摸索着解决了大华摄像头无法使用问题,记录下来,应该对其他博友有所帮助。之前虽然写了一大堆文章说明了如何使用gsoap连接摄像头,但这是针对一台海康的摄像头开发的,一旦使用了同品牌不同型号摄像头或者其他牌子的摄像头就可能出现兼容性问题,导致无法使用。我就是碰到了这个问题,测试过的多个品牌型号摄像头,有的能直接使用,有的不能使用,问题各部相同。本文就是针对大华摄像头的问题解决过程说明。
摄像头型号:DH-IPC-HDW130S-0600B
MAC:90:02:A9:3D:1A:63
默认IP: 192.168.16.108, 修改使用IP:192.168.0.108
默认用户名密码;admin:admin
软件里面查看版本信息如下:
设备类型 IPC-HDW4105S
软件版本 2.400.0000.0.R, build : 2013-12-31
WEB版本 3.2.4.161826
ONVIF版本 2.3
序列号 TGC4AW158W00019
有意思的一点是摄像头标签上的型号是HDW130s,但是固件中的设备类型却是HDW4105S,不一致的哦。
-------------------------------------------------------------------
问题1:我的分机发出probe命令,但是没有收到大华IPC应答,无法发现 。
问题分析:OnvifTest软件可以正确发现大华IPC,抓包比对OnvifTest报文和我的报文,发现SOAP-ENV不同。百度后明白原因如下:
我使用的gsoap版本生成 RemoteDiscoveryBinding.nsmap,内容是有版本区别的。
//SOAP 1.1版本
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope",
NULL},
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding",
NULL}, //1.1
//SOAP 1.2版本
{"SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://schemas.xmlsoap.org/soap/envelope/",
NULL},
{"SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://schemas.xmlsoap.org/soap/encoding/",
NULL}, //1.2
1.1版本的可以发现海康IPC,1.2版本才能发现大华IPC。使用1.2版本内容,收到大华Probe应答,解析成功。
-------------------------------------------------------------------
问题2:我的分机发出GetCapabilities命令,无应答。
问题分析:
还是对比OnvifTest软件和分机发出的GetCapabilities命令,发现主要是域名空间不同。
这是使用ONVIF TEST软件发送的GetCapabilities
POST /onvif/device_service HTTP/1.1
Host: 192.168.0.108
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 335
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetCapabilities xmlns="http://www.onvif.org/ver10/device/wsdl">
<Category>All</Category>
</GetCapabilities>
</s:Body>
</s:Envelope>
一共使用了4个命名,
xmlns s="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
xmlns="http://www.onvif.org/ver10/device/wsdl">
而gsoap生成的RemoteDiscoveryBinding.nsmap,完整的命令空间如下,密密麻麻好多啊!
SOAP_NMAC struct Namespace namespaces[] ={
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope",
NULL},
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding",
NULL},
{"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance",
NULL},
{"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema",
NULL},
{"wsa", "http://schemas.xmlsoap.org/ws/2004/08/addressing", NULL, NULL},
{"wsdd", "http://schemas.xmlsoap.org/ws/2005/04/discovery", NULL, NULL},
{"chan", "http://schemas.microsoft.com/ws/2005/02/duplex", NULL, NULL},
{"wsa5", "http://www.w3.org/2005/08/addressing", "http://schemas.xmlsoap.org/ws/2004/08/addressing",
NULL},
{"c14n", "http://www.w3.org/2001/10/xml-exc-c14n#", NULL, NULL},
{"wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
NULL, NULL},
{"xenc", "http://www.w3.org/2001/04/xmlenc#", NULL, NULL},
{"wsc", "http://schemas.xmlsoap.org/ws/2005/02/sc", NULL, NULL},
{"ds", "http://www.w3.org/2000/09/xmldsig#", NULL, NULL},
{"wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd", NULL},
{"xmime", "http://www.w3.org/2005/05/xmlmime", NULL, NULL},
{"ns5", "http://www.w3.org/2004/08/xop/include", NULL, NULL},
{"ns6", "http://docs.oasis-open.org/wsrf/bf-2", NULL, NULL},
{"ns3", "http://www.onvif.org/ver10/schema", NULL, NULL},
{"ns4", "http://docs.oasis-open.org/wsn/b-2", NULL, NULL},
{"ns7", "http://docs.oasis-open.org/wsn/t-1", NULL, NULL},
{"wsdd10", "http://tempuri.org/wsdd10.xsd", NULL, NULL},
{"ns1", "http://www.onvif.org/ver10/network/wsdl", NULL, NULL},
{"ns2", "http://www.onvif.org/ver10/device/wsdl", NULL, NULL},
{"ns8", "http://www.onvif.org/ver10/media/wsdl", NULL, NULL},
{NULL, NULL, NULL, NULL}
};
尝试只保留上述4个命名空间,就收到了大华IPC应答。由此确定是分机中的命令空间太多了,大华摄像头不支持某些命名空间,直接无视了,却不会发出任何错误应答。有了这个教训,后面的过程都是小心翼翼的试验只添加命令报文中使用的命名空间,这样就不会出现无应答错误了。最终各种命令汇总后的必须命名空间如下:
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" ‘
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
xmlns: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:ns1="http://www.onvif.org/ver10/network/wsdl"
xmlns:ns2="http://www.onvif.org/ver10/device/wsdl"
xmlns:ns3="http://www.onvif.org/ver10/schema"
xmlns:ns8="http://www.onvif.org/ver10/media/wsdl">
------------------------------------------------------------------
问题3: 我的分机设备发出Probe命令成功,发现了大华IPC,然后发送命令GetCapabilities,收到了大华IPC应答报文,但是分机却返回error=SOAP_TYPE,导致失败。使用OnvifTest软甲测试GetCapabilities,IPC发送了同样的报文,就可以正确解析。
问题分析过程:
SOAP_TYPE: 标准解释是An XML Schema type mismatch, 到底是个什么意思我也说不出来,猜测是一些字段或域名空间关键字错误。
(1)首先怀疑报文解析过程中的每个字段项解析是否出现了错误,确定是哪个字段项错误。
方法:编译分机程序时天机编译开关-DDEBUG,这样运行分机程序会自动生成调试文件:RECV.log、SENT.log、TEST.log。打开TEST.log文件,查找error没有发现。观察报文解析过程,发现出现在这一段:
<tt:Extension>
<tt:DeviceIO>
<tt:XAddr>http://192.168.0.108/onvif/deviceIO_service</tt:XAddr>
<tt:VideoSources>1</tt:VideoSources>
<tt:VideoOutputs>0</tt:VideoOutputs>
<tt:AudioSources>0</tt:AudioSources>
<tt:AudioOutputs>0</tt:AudioOutputs>
<tt:RelayOutputs>0</tt:RelayOutputs>
</tt:DeviceIO>
<tt:Extensions>
<tt:TelexCapabilities>
<tt:XAddr>http://192.168.0.108/onvif/telecom_service</tt:XAddr>
<tt:TimeOSDSupport>true</tt:TimeOSDSupport>
<tt:TitleOSDSupport>true</tt:TitleOSDSupport>
<tt:PTZ3DZoomSupport>true</tt:PTZ3DZoomSupport>
<tt:PTZAuxSwitchSupport>true</tt:PTZAuxSwitchSupport>
<tt:MotionDetectorSupport>true</tt:MotionDetectorSupport>
<tt:TamperDetectorSupport>true</tt:TamperDetectorSupport>
</tt:TelexCapabilities>
</tt:Extensions>
</tt:Extension>
遇到Extension段中包含Extensions段,内部的Extensions段都未能识别出来,后面的log中都没有出现该段内容的解析过程。这种TelexCapabilities是海康IPC报文中没有的,在gsoap代码中也找不到该字段。由此确定是该段解析出错。
(2)确定代码中的错误位置
从GetCapabilities命令的调用关系一层层的跟踪下去,
soap_call___ns2__GetCapabilities() soapClient.c
soap_get__ns2__GetCapabilitiesResponse() 该函数执行后出现error=SOAP_TYPE
soap_in__ns2__GetCapabilitiesResponse()
soap_in_PointerTons3__Capabilities()
soap_in_ns3__Capabilities()
soap_in_PointerTons3__CapabilitiesExtension()
soap_in_ns3__CapabilitiesExtension()
soap_in_PointerTons3__CapabilitiesExtension2()
soap_in_ns3__CapabilitiesExtension2()
最后的CapabilitiesExtension2()返回error=SOAP_TYPE,通过添加打印信息确定是if (soap_in_byte(soap, "-any", a->__any, "xsd:byte"))出现失败。 仔细观察soap_in_byte()函数,内部调用 stdsoap2.c soap_s2byte()函数,目的是把输入字符串转换成10进制的整数。
soap_s2byte(struct soap *soap, const char *s, char *p)
{ if (s)
{ long n;
char *r;
n = soap_strtol(s, &r, 10);
if (s == r || *r || n < -128 || n > 127)
{
soap->error = SOAP_TYPE;
}
*p = (char)n;
}
return soap->error;
}
soap_s2byte这个函数调用soap_strtol进行转换,s是输入字符串,p指向转换后的单字节。但是对于s=“”空字符串无法转换成功,引起s==r成立,导致错误返回SOAP_TYPE。 看起来好像是gsoap的一个bug。修改方法是去除s==r的判断条件,避免报告错误。
if ( n < -128 || n > 127)
{
soap->error = SOAP_TYPE;
}
这样修改后就不会出现错误了,soap_call___ns2__GetCapabilities() 解析成功,TEST.log文件中也可以看到相关字段的解析了,下列内容是发现了字段,但是无法识别,予以忽略。
Tags and (default) namespaces match: ‘tt:Extensions‘ ‘ns3:Extensions‘
Begin element found (level=6) ‘tt:Extensions‘=‘ns3:Extensions‘
Reverting to last element ‘tt:Extensions‘ (level=5)
Tags and (default) namespaces match: ‘tt:Extensions‘ ‘ns3:Extensions‘
Begin element found (level=6) ‘tt:Extensions‘=‘ns3:Extensions‘
Enter id=‘‘ type=781 loc=(nil) size=8 level=0
stdsoap2.c(7512): malloc(20) = 0x410d98
New block sequence (prev=(nil))
stdsoap2.c(2187): malloc(12) = 0x410dc8
Push block of 1 bytes (1 bytes total)
stdsoap2.c(2209): malloc(9) = 0x410df0
Enter id=‘‘ type=3 loc=0x410df8 size=1 level=0
Element content value=‘‘
Push block of 1 bytes (2 bytes total)
stdsoap2.c(2209): malloc(9) = 0x410e18
Enter id=‘‘ type=3 loc=0x410e20 size=1 level=0
Element content value=‘‘
Unexpected element ‘tt:TelexCapabilities‘ in input (level=6, 1)
Tags ‘tt:TelexCapabilities‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:TelexCapabilities‘
Unexpected element ‘tt:XAddr‘ in input (level=7, 1)
Tags ‘tt:XAddr‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:XAddr‘
End element found (level=8) ‘tt:XAddr‘=‘‘
Unexpected element ‘tt:TimeOSDSupport‘ in input (level=7, 1)
Tags ‘tt:TimeOSDSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:TimeOSDSupport‘
End element found (level=8) ‘tt:TimeOSDSupport‘=‘‘
Unexpected element ‘tt:TitleOSDSupport‘ in input (level=7, 1)
Tags ‘tt:TitleOSDSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:TitleOSDSupport‘
End element found (level=8) ‘tt:TitleOSDSupport‘=‘‘
Unexpected element ‘tt:PTZ3DZoomSupport‘ in input (level=7, 1)
Tags ‘tt:PTZ3DZoomSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:PTZ3DZoomSupport‘
End element found (level=8) ‘tt:PTZ3DZoomSupport‘=‘‘
Unexpected element ‘tt:PTZAuxSwitchSupport‘ in input (level=7, 1)
Tags ‘tt:PTZAuxSwitchSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:PTZAuxSwitchSupport‘
End element found (level=8) ‘tt:PTZAuxSwitchSupport‘=‘‘
Unexpected element ‘tt:MotionDetectorSupport‘ in input (level=7, 1)
Tags ‘tt:MotionDetectorSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:MotionDetectorSupport‘
End element found (level=8) ‘tt:MotionDetectorSupport‘=‘‘
Unexpected element ‘tt:TamperDetectorSupport‘ in input (level=7, 1)
Tags ‘tt:TamperDetectorSupport‘ and ‘SOAP-ENV:‘ match but namespaces differ
IGNORING element ‘tt:TamperDetectorSupport‘
End element found (level=8) ‘tt:TamperDetectorSupport‘=‘‘
End element found (level=7) ‘tt:TelexCapabilities‘=‘‘
Pop block
stdsoap2.c(2236): free(0x410e18)
stdsoap2.c(7512): malloc(12) = 0x410e18
Save all blocks in contiguous memory space of 1 bytes (0x410df0->0x410e18)
总结: 通过上述修改,解决了大华IPC无法使用问题,分机可以发现设备、查询设备能力、修改媒体配置、获取流媒体地址。最后,分机能通过rtsp连接摄像头,显示出了视频画面。
致谢:最近才在CSDN发现一篇博文,简直就是黑夜中指路明灯啊,一下子解决困扰我大半年的问题,太谢谢这位小兵_小白大神了!
原文:关于onvif对接海康设备出现soap->error=4的问题