Skip to content

agsh/onvif

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,269 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ONVIF

Coverage Status

ONVIF client protocol implementation for Node.js.

Tip

This page describes the future 1.x version of the ONVIF library written in TypeScript. If you are looking for the README for the stable 0.x version, please see branch v0.x

The default npm installation still uses version 0.x. If you want to try this new version, install it with:

npm install onvif@alpha

This is a wrapper for the ONVIF protocol that allows you to:

  • get information about your NVT (Network Video Transmitter) device and its media sources
  • control PTZ (pan-tilt-zoom) movements
  • manage presets
  • detect devices on your network
  • control events
  • retrieve information about your NVR (Network Video Recorder) Profile G device
  • obtain a list of recordings

The library runs on Node.js and works server-side.

ONVIF

About

This is a new version of the ONVIF library. The previous version was written in JavaScript, while this version is written in TypeScript and includes interfaces describing ONVIF data structures.

At the moment, some of the methods from v0.8 have been implemented.

You can find the list of supported ONVIF commands here:

The library will continue to be updated, as other methods are currently under development.

The documentation for the new library was generated with TypeDoc and is available here:

Code that uses the old version of the library (0.8.x) should work with the compatibility class:

Warning

Compatibility mode is currently unsupported.

Thanks a lot for your interest!
I will be happy to answer any questions and hear your feedback.


Features


Connection

Before you can use most library methods, call connect() on your Onvif instance. This method performs the initial handshake with the device and fills internal state so later SOAP requests are authenticated and routed to the correct service endpoints.

connect() runs the following steps in order:

  1. Time synchronizationgetSystemDateAndTime() is called first. ONVIF WS-Security authentication includes a timestamp in the nonce digest, so the client must know the offset between its own clock and the device clock (timeShift). The library tries an unauthenticated request first, as the ONVIF spec allows, and retries with credentials when the device requires authentication (some Panasonic and Digital Barriers models behave this way).
  2. Service discovery — the library tries device.getServices(), the modern ONVIF approach introduced with Profile T. If that fails on older devices, it falls back to device.getCapabilities(). Both methods populate onvif.uri with the URLs for media, PTZ, events, replay, and other services that subsequent requests use.
  3. Media configurationmedia.getProfiles() and media.getVideoSources() run in parallel, then getActiveSources() matches each video source to a suitable media profile. This sets activeSource, defaultProfile, and defaultProfiles, including encoder settings and PTZ configuration, so you can start streaming or controlling the camera without extra setup.

On success, connect() emits a connect event and returns the Onvif instance. Pass autoConnect: true in the constructor to run this automatically after instantiation.

const onvif = new Onvif({ hostname: '192.168.1.13', port: 8000, username: 'admin', password: 'admin' });
await onvif.connect();
console.log(onvif.activeSource);

Interfaces

Interfaces are generated according to the latest version of the ONVIF specification.

All methods accept options defined by the ONVIF specification and return data from the corresponding <method_name>Response.

For example, the getCapabilities method accepts a single argument of type GetCapabilities and returns a result of type Capabilities.

Below is the internal structure of the GetCapabilitiesResponse type:

export interface GetCapabilitiesResponse {
  /** Capability information. */
  capabilities?: Capabilities;
}

class Device {
  // ...

  async getCapabilities(options?: GetCapabilities): Promise<Capabilities> {
    // ...
  }

  // ...
}

In general, the library tries to avoid returning objects that contain only a single property.

In some cases, where native JavaScript types are more convenient, interfaces are extended with additional fields.

For example:

  • SetSystemDateAndTime
  • SetSystemDateAndTimeExtended

The extended version adds a more convenient field:

dateTime?: Date;

xs:any Support

The ONVIF specifications include numerous extension points, which presents a challenge:

  • on one hand, we want simple and convenient interfaces
  • on the other hand, we need a unified mechanism for handling undocumented vendor-specific data

This data is usually provided through:

<xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>

This mechanism is important for:

  • backward compatibility
  • forward compatibility
  • XML ↔ JavaScript transformation in ONVIF get/set methods

Example

Suppose we have an ElementItem structure defined in:

Example schema:

<xs:element name="ElementItem" minOccurs="0" maxOccurs="unbounded">
    <xs:annotation>
        <xs:documentation>Complex value structure.</xs:documentation>
    </xs:annotation>

    <xs:complexType>
        <xs:sequence>
            <xs:any namespace="##any" processContents="lax">
                <xs:annotation>
                    <xs:documentation>
                        XML tree containing the element value as defined
                        in the corresponding description.
                    </xs:documentation>
                </xs:annotation>
            </xs:any>
        </xs:sequence>

        <xs:attribute name="Name" type="xs:string" use="required">
            <xs:annotation>
                <xs:documentation>Item name.</xs:documentation>
            </xs:annotation>
        </xs:attribute>
    </xs:complexType>
</xs:element>

This becomes the following autogenerated TypeScript interface:

export interface ElementItem {
  /** Item name. */
  name: string;

  /** XML tree containing the element value as defined in the corresponding description. */
  [key: string]: unknown;
}

Real-World XML Example

For example, in MetadataConfiguration:

<Parameters>
    <ElementItem>
        <Name>elementItem1</Name>

        <Param1>
            <Data>42</Data>
        </Param1>

        <Param2>param2</Param2>
    </ElementItem>
</Parameters>

After parsing, the object looks like this:

{
  elementItem : [{
    name    : 'elementItem1',

    param1  : {
      data : 42
    },

    param2  : 'param2',

    __any__ : {
      'Name'   : ['elementItem1'],

      'Param1' : [{
        'Data' : ['42']
      }],

      'Param2' : 'param2'
    }
  }]
}

This object contains:

  • the required name field
  • parsed xs:any fields (param1, param2)
  • the raw __any__ field

The __any__ field contains the original unprocessed object returned by:


Why Keep __any__?

A reasonable question is:

Why keep the raw XML structure?

The answer is simple.

When configuring ONVIF devices, extensions, or vendor-specific parameters, we often do not know how to serialize a clean JavaScript object back into the correct XML structure.

At the same time, we still want to work with the data in a convenient way.

So when modifying device configuration (for example using setMetadataConfiguration), follow two simple rules:

1. Modify known fields directly

elementItem[0].name = 'hello'

2. Modify unknown/vendor-specific fields inside __any__

elementItem[0].__any__.Param2 = 'hi'

This structure can then be automatically converted back into the appropriate SOAP XML.


Tests

All tests are written using Jest.

Run them with:

npm test

The tests use:

as a test device.

Thanks to HappyTimeSoft for providing the opportunity to test the full ONVIF specification.

Products are available here:

About

ONVIF node.js implementation

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors