DEV Community

Anguo
Anguo

Posted on

The ONVIF Media2 trap: GetConfiguration is a type, not an element

While building the v0.2 capability inventory for onvif-scan, I froze a design decision that turned out to be wrong — and the interesting part is how it got caught.

The mistake

For Media2 devices, GetVideoEncoderConfigurationOptions takes a configuration token. I specified the request body as an operation element wrapping a GetConfiguration element, which would contain the ConfigurationToken.

That shape does not exist. The official Media2 WSDL says:

<xs:element name="GetVideoEncoderConfigurationOptions" type="tr2:GetConfiguration"/>
Enter fullscreen mode Exit fullscreen mode

GetConfiguration is the type name of the operation element. Its sequence directly contains ConfigurationToken (and ProfileToken). There is no wrapper child element with that name. The correct request is simply:

<tr2:GetVideoEncoderConfigurationOptions>
  <tr2:ConfigurationToken>vec_1</tr2:ConfigurationToken>
</tr2:GetVideoEncoderConfigurationOptions>
Enter fullscreen mode Exit fullscreen mode

The implementation agent I work with refused to code against my frozen spec: it checked the WSDL, stopped that one item, documented the conflict with evidence, and finished everything else. The Media2 profiles were reported as unqueried with the reason visible — not silently skipped, and not "verified" by response-parsing tests alone. That is the behaviour I want from tooling that touches other people's networks: when the spec and the instructions disagree, stop and show the evidence.

D1 (as we called it) is now resolved in favour of the WSDL, with request-shape assertions in the simulator to keep it resolved.

What v0.2.0 inventories

Beyond the current configuration a profile already reports, v0.2.0 collects what devices declare:

  • Full service declarations. GetServices is part of the standard pass: every declared namespace, XAddr and version is kept — unknown services, duplicates and missing fields included. A failed query yields null, never a fabricated empty list.
  • Encoder options per profile. Codec sets, per-codec resolutions, frame-rate ranges on Media1, and declared fractional frame-rate lists on Media2 (29.97 stays 29.97 — not rounded to 30). Only what the device declares; the report explicitly does not claim arbitrary codec×resolution×frame-rate combinations work.
  • PTZ declarations. Node capacity (MaximumNumberOfPresets), home/tour support declarations, and actual preset counts and names per profile. Coordinates are never exported, and counts are never summed across profiles that share a node — capacity and reality stay in separate fields.
  • A hardened credential gate. After any rejection — including a bare HTTP 401 with no SOAP fault — the scanner makes zero further credentialed calls to that device. Optional queries report explicit states (ok / unsupported / auth_blocked / failed / unqueried) instead of quietly degrading the whole device.

It also fixes a semantic bug worth admitting: channel_count used to deduplicate configuration tokens, not physical sources. One source with two configurations counted as two channels. It now deduplicates actual source tokens and records which basis it used.

Everything is read-only Get* queries, one attempt per user-supplied credential, passwords never written to reports. Still zero-dependency Python.

Honest status

This is verified against simulated devices (three personas: standard, legacy-firmware style, strict Media2) — 86 tests, including full WS-Discovery end-to-end runs on a LAN host. It is not yet verified against real cameras; that gap is stated in the release notes, not buried. If you run it on real hardware, a sanitised device report through the repo's issue template helps more than a star.

Repo:

https://github.com/anguoyang/onvif-scan

Release:

https://github.com/anguoyang/onvif-scan/releases/tag/v0.2.0

Top comments (0)