Sebastian Norling fb05c31d7d feat(event/stream): add Stream with pull-point lifecycle
Introduces Stream, the typed event consumer the package will eventually
present to callers, plus the caller seam needed to test it without
hitting a real camera.

Stream owns one ONVIF pull-point subscription end-to-end:
  * CreatePullPointSubscription on construction so authentication and
    reachability problems surface synchronously from NewStream rather
    than landing on the Errors channel after the goroutine starts.
  * Background pull loop calls PullMessages against the
    SubscriptionReference Address returned by Create. Each
    NotificationMessage is fed through Decode and pushed on the Events
    channel, with context cancellation honoured between every step so a
    Close cannot get stuck behind a long-server-side-wait pull.
  * Errors during a pull are surfaced on a separate Errors channel using
    a non-blocking send; a stalled consumer drops older errors instead
    of blocking the loop. The loop sleeps briefly (ctx-aware) and
    retries — automatic subscription recreation lands in the
    reconnect-on-error commit.
  * Close cancels the context, waits for the run goroutine to exit,
    Unsubscribes the pull point and closes Events/Errors. sync.Once
    keeps it idempotent.

Design seams
------------
* caller interface (CallMethod + SendSoap) abstracts *onvif.Device so
  tests can substitute fakeCaller without an HTTP server. deviceCaller
  is the production adapter; newStream takes the interface, NewStream
  takes the concrete *onvif.Device. The same shape lets a future commit
  add WithClassifier / WithClock / WithCaller options if the architect
  reviewer's pluggable-classifier note becomes urgent.
* now func() time.Time is a Stream field so a future clock-injecting
  test (renew timing, observed-at determinism) can swap it.
* unmarshalNode keys on the local XML name, sidestepping namespace
  matching since SOAP envelopes from different vendors prefix the
  PullMessagesResponse and CreatePullPointSubscriptionResponse with
  arbitrary tev:/tev1:/... bindings. This is the same trick the agent's
  getXMLNode used; lifting it here lets the agent eventually drop its
  copy.

Options and defaults
--------------------
PullTimeout 5s, MessageLimit 10, InitialTermination 60s, BufferSize 16
match what the existing agent code uses. TopicFilter defaults to empty
so AXIS cameras work out of the box — the verified topic table is
intentionally the routing layer, not a server-side filter, because the
agent will frequently want digital I/O and motion on the same stream.

Tests cover the create-then-pull-then-close happy path, that pulls
target the SubscriptionReference Address (not the device endpoint),
construction failure on CreatePullPoint error, context-cancel exits
the loop cleanly with channels closed, transient pull errors land on
Errors without stopping decode of subsequent good messages, idempotent
Close, and Options default values. -race clean.
2026-05-21 14:35:34 +02:00
2025-01-19 10:19:22 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
add
2023-12-25 21:31:14 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2025-01-19 09:58:53 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-25 21:26:55 +01:00
2023-12-18 20:16:21 +01:00
2021-01-31 16:01:46 +08:00
2023-12-25 21:26:55 +01:00
2023-12-18 20:16:21 +01:00

Onvif library

Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard.

Overview

This repository is forked from: use-go/onvif

Supported services

The following services are implemented:

  • Device
  • Media
  • PTZ
  • Event
  • Discovery

Using

General concept

  1. Connecting to the device
  2. Authentication (if necessary)
  3. Defining Data Types
  4. Carrying out the required method

Connecting to the device

If there is a device on the network at the address 192.168.13.42, and its ONVIF services use the 1234 port, then you can connect to the device in the following way:

dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234"})

The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. Usually this is 80 port.

Authentication

If any function of the ONVIF services requires authentication, you must use the Authenticate method.

device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})

Defining Data Types

Each ONVIF service in this library has its own package, in which all data types of this service are defined, and the package name is identical to the service name and begins with a capital letter. onvif defines the structures for each function of each ONVIF service supported by this library. Define the data type of the GetCapabilities function of the Device service. This is done as follows:

capabilities := device.GetCapabilities{Category:"All"}

Why does the GetCapabilities structure have the Category field and why is the value of this field All?

The figure below shows the documentation for the GetCapabilities. It can be seen that the function takes one Category parameter and its value should be one of the following: 'All', 'Analytics',' Device ',' Events', 'Imaging', 'Media' or 'PTZ'`.

Device GetCapabilities

An example of defining the data type of GetServiceCapabilities function in PTZ:

ptzCapabilities := ptz.GetServiceCapabilities{}

The figure below shows that GetServiceCapabilities does not accept any arguments.

PTZ GetServiceCapabilities

Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package.

An example of how to define the data type of the CreateUsers function in Devicemgmt:

createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}

The figure below shows that ,in this example, the CreateUsers structure field must be a User whose data type is the User structure containing the Username, Password, UserLevel, and optional Extension fields. The User structure is in the onvif package.

Device CreateUsers

Carrying out the required method

To perform any function of one of the ONVIF services whose structure has been defined, you must use the CallMethod of the device object.

createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
device.Authenticate("username", "password")
resp, err := dev.CallMethod(createUsers)

Development

See here

Description
pull-mirror of github.com/kerberos-io/onvif
Readme MIT 2.2 MiB
Languages
Go 99%
Python 0.9%