A while back I bought a few network cameras to use at home for surveillance. Before buying them I discovered the ONVIF standard for IP cameras and how it was possible to develop applications that talked to the devices through WCF, so I made sure the cameras I bought had ONVIF.
When I got the first cameras I started to develop my own library and software for motion detection to use for surveillance. This I named OpenONVIF which I plan to make open source on GitHub at later stage.
But along the way I hit a few bumps on how to get everything to work together, like connecting to the cameras with a Service Reference in Visual Studio. But the problem with finding the cameras through WCF Discovery got me at first as I found no real solution on the internet, so I plan to describe the problem and the solution in a bit more in detail in this post.
A default WCF Discovery request might look something like this. A WCF Discovery like this works that it sends a request with UDP on a broadcast adress that WCF services should listen to, WCF services that then see this request responds back with some meta information about the service.
private static FindResponse WCFDiscovery()
{
var endPoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
var discoveryClient = new DiscoveryClient(endPoint);
FindCriteria findCriteria = new FindCriteria();
findCriteria.Duration = TimeSpan.FromSeconds(3);
return discoveryClient.Find(findCriteria);
}
At first I could not understand why I only got my printer as a result here and as I found atleast one service through the discovery the code appearently worked. So I hooked up WireShark to debug the data being sent. After viewing the result in WireShark the cameras do respond with what I could see a valid WCF response and could not really see a difference to the response from my printer.
Though after a bit of searching on the internet and comparing the responses being sent and found out that my cameras respond with an incomplete UUID.
For example, the WCF request UUID looks like this: urn:uuid:0aea361b-ec60-428b-8276-647c0f508348
My printer responds with this: urn:uuid:0aea361b-ec60-428b-8276-647c0f508348
But my camera responds with this UUID, without the "urn" part: uuid:0aea361b-ec60-428b-8276-647c0f508348
After knowing what probably was causing the problem I quickly found that WCF throws an exception with the following description and ignores the response. A ProbeMatches message with messageId='urn:uuid:0aea361b-ec60-428b-8276-647c0f508348' and relatesTo='uuid:0aea361b-ec60-428b-8276-647c0f508348' was dropped by the DiscoveryClient because either the corresponding Find operation was completed or the relatesTo value is invalid.
Knowing what was causing the error I then search on how to either somehow modify the response to add the "urn" part or manually accept the response even though it was an invalid one. While searching I came across a solution that could modify the request and remove the "urn" from the request instead.
Trying the code with the "urn" part removed I now got my response from my cameras and printer, success! I also viewed the data being sent with WireShark with this "fix" and could now see that the also the printer responsed without the "urn" part now that it was removed from the request.
private static FindResponse FindDevices()
{
var endPoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
var discoveryClient = new DiscoveryClient(endPoint);
FindCriteria findCriteria = new FindCriteria();
findCriteria.Duration = TimeSpan.FromSeconds(3);
findCriteria.ContractTypeNames.Add(new System.Xml.XmlQualifiedName("NetworkVideoTransmitter", "http://www.onvif.org/ver10/network/wsdl"));
using (new OperationContextScope(discoveryClient.InnerChannel))
{
OperationContext.Current.OutgoingMessageHeaders.MessageId = new System.Xml.UniqueId($"uuid:{Guid.NewGuid()}");
return discoveryClient.Find(findCriteria);
}
}
Comparing the modified code to the not working one we mainly see difference on lines 35-39. The OperationContextScope is used to create a scope around a WCF request and add or modify the header. So what we do is create a new scope and manually set the MessageId in the OutgoingMessageHeader to have an id without the "urn" part, which results in the cameras responding with a correct id.
Extra: The critera on line 33 is used to filter what devices/services that should be found with the discovery request. The line in this code is to filter that only devices that implements the ONVIF standard should reply or be accepted, so when I run this code only my three cameras are found and not my printer.
Even though there is a standard to follow for devices that implements ONVIF it seems that some models have, what should we call it, bugs? And as there are no easy fixes for the hardware, we have to fix it in the software.
But it what I understand not all models have this bug, it seems I just had a bit of unluck.