添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Intro

OnVif is a remote-control protocol for manipulating IP cameras, developed by Axis .

You can use it to PTZ (pan-tilt-zoom) the camera, for setting camera’s credentials and resolution, and for almost anything else you can imagine.

OnVif is based on SOAP , i.e. on sending rather complex XML messages between your client computer and the IP camera. The messages (remote protocol calls), the responses and the parameters, are defined by WSDL files , which (when visualized nicely) look like this .

Python OnVif

In Python, the main bottleneck was in finding a decent open source SOAP library that would do the trick. Recently, things have got better with the arrival of Zeep .

Before Zeep existed, people used Suds , which has become a bit obsolete by now. A library called python-onvif was based on Suds.

That python-onvif module has since then been forked and modded to work with Zeep .

However, we don’t need any of that, since it’s a better idea to

use Zeep directly

with minimum extra code bloat on top of it.

So, use Zeep as your SOAP client, give it the WSDL file and that’s about it.

OnVif with Zeep

Rather than giving you an obscure OnVif client implementation, you’ll learn to do this by yourself using Zeep. Let’s begin with:

pip3 install zeep

You also need this table to get started:

Here is an example on how to create your own class for an OnVif device service, based on the class OnVif:

from valkka.onvif import OnVif, getWSDLPath
# (1) create your own class:
class DeviceManagement(OnVif):
    namespace = "http://www.onvif.org/ver10/device/wsdl"
    wsdl_file = getWSDLPath("devicemgmt.wsdl")
    sub_xaddr = "device_service"
    port      = "DeviceBinding"
# (2) instantiate your class:
device_service = DeviceManagement(
    ip          = "192.168.0.24",
    port        = 80,
    user        = "admin",
    password    = "12345"

(the implementation of the base class OnVif is only a few lines long)

The things you need for (1) subclassing an OnVif service are:

  • The remote control protocol is declared / visualized in the link at the first column. Go to http://www.onvif.org/ver10/device/wsdl to see the detailed specifications.
  • In that specification, we see that the WSDL “port” is DeviceBinding.
  • Each SOAP remote control protocol comes with a certain namespace. This is the same as that address in the first column, so we set namespace to http://www.onvif.org/ver10/device/wsdl.
  • We use a local modified version of the wsdl file. This can be found in the third column, i.e. set wsdl_file to devicemgmt.wsdl (these files come included in libValkka).
  • Camera’s local http subaddress sub_xaddr is device_service (the second column of the table)
  • When you (2) instantiate the class into the device_service object, you just give the camera’s local IP address and credentials

    Service classes

    You can create your own OnVif subclass as described above.

    However, we have done some of the work for you. Take a look at the column “Subclass” in the table, and you’ll find them:

    from valkka.onvif import Media
    media_service = Media(
        ip          = "192.168.0.24",
        port        = 80,
        user        = "admin",
        password    = "12345"
    

    Example call

    Let’s try a remote protocol call.

    If you look at that specification in http://www.onvif.org/ver10/device/wsdl, there is a remote protocol call name GetCapabilities. Let’s call it:

    cap = device_service.ws_client.GetCapabilities()
    print(cap)
    

    We can also pass a variable to that GetCapabilities call.

    Variables are nested objects, that must be constructed separately. Like this:

    factory = device_service.zeep_client.type_factory("http://www.onvif.org/ver10/schema")
    category = factory.CapabilityCategory("Device")
    cap = device_service.ws_client.GetCapabilities(category)
    print(cap)
    

    The namespace http://www.onvif.org/ver10/schema declares all basic variables used by the wsdl files. We also provide a short-cut command for that:

    category = device_service.getVariable("Device")
    

    That’s about it. Now you are able to remote control your camera.

    One extra bonus: to open the specifications directly with Firefox, try this

    device_service.openSpecs()
    

    Notes

    When specifying durations with Zeep, you must use the isodate module, like this:

    import isodate
    timeout = isodate.Duration(seconds = 2)
    

    Now that variable timeout can be used with OnVif calls

    Discovery

    In libValkka, cameras can be discovered like this:

    from valkka.discovery import runWSDiscovery, runARPScan
    ips = runWSDiscovery()
    ips2 = runARPScan(exclude_list = ips) # run this if you want to use arp-scan
    

    If you want arp-scan to work, you must permit normal users to run the executable, with:

    sudo apt-get install arp-scan
    sudo chmod u+s /usr/sbin/arp-scan