oxvif/lib.rs
1//! # oxvif
2//!
3//! An async Rust client library for the [ONVIF] IP camera protocol.
4//!
5//! ONVIF (Open Network Video Interface Forum) is the industry standard for
6//! interoperability between IP-based security cameras. This library provides
7//! a complete async client covering device management, media streaming,
8//! PTZ control, imaging, on-screen display, events, recording, search, and
9//! replay — all over SOAP/HTTP(S) with WS-Security and HTTP Digest
10//! authentication.
11//!
12//! ## ONVIF Profile coverage
13//!
14//! | Profile | Description | Coverage | Notes |
15//! |---------|-------------|----------|-------|
16//! | **Profile S** | Video streaming | ~95% | All core operations implemented |
17//! | **Profile T** | Advanced streaming (H.265, focus, OSD, audio) | ~95% | HTTP Digest Auth, Media2 audio/metadata/analytics config, PTZ compat; Analytics rules and DeviceIO not yet implemented |
18//! | **Profile G** | Recording & playback | ~85% | Read/search/replay + full recording/job write management; live-source job binding not yet implemented |
19//!
20//! ## Supported services
21//!
22//! - **Device** — capabilities, scopes, device info, hostname, NTP, reboot,
23//! user management, network interfaces/protocols/DNS/gateway, relay outputs,
24//! storage configurations, system log/URIs, factory default, discovery mode,
25//! auxiliary commands (wiper/IR lamp)
26//! - **Media1 / Media2** — profiles, RTSP/snapshot URIs, video + audio config, OSD,
27//! metadata config, audio decoder/output config, video source modes,
28//! unified AddConfiguration/RemoveConfiguration
29//! - **PTZ** — absolute/relative/continuous move, presets, home position, status,
30//! configurations, nodes, compatible configurations
31//! - **Imaging** — brightness/contrast/exposure settings, focus move/stop/status
32//! - **Events** — pull-point subscriptions, event polling, renew, unsubscribe,
33//! continuous `event_stream`, synchronization point
34//! - **Recording** — list stored recordings; create/delete recordings, tracks, and recording jobs
35//! - **Search** — find recordings by scope, collect results, end search
36//! - **Replay** — get RTSP playback URI for a stored recording
37//! - **WS-Discovery** — UDP multicast probe to find cameras on the local network
38//!
39//! ## Architecture
40//!
41//! ```text
42//! ┌──────────────────────────────────────────────────────┐
43//! │ OnvifSession │
44//! │ caches service URLs — delegates every call │
45//! ├──────────────────────────────────────────────────────┤
46//! │ OnvifClient │
47//! │ stateless — you supply service URLs per call │
48//! ├──────────────────────────────────────────────────────┤
49//! │ soap::SoapEnvelope │ soap::WsSecurityToken │ ← SOAP layer
50//! ├──────────────────────────────────────────────────────┤
51//! │ Transport trait │ ← HTTP abstraction
52//! │ (HttpTransport / mock in tests) │
53//! └──────────────────────────────────────────────────────┘
54//! ```
55//!
56//! ## Quick start
57//!
58//! Two ways to use oxvif — pick whichever suits your workflow.
59//!
60//! ### `OnvifSession` — URL caching handled for you
61//!
62//! [`OnvifSession`] calls `GetCapabilities` once at construction and caches all
63//! service URLs. No URL parameters needed for individual methods.
64//!
65//! ```no_run
66//! use oxvif::{OnvifSession, OnvifError};
67//!
68//! async fn run() -> Result<(), OnvifError> {
69//! let session = OnvifSession::builder("http://192.168.1.100/onvif/device_service")
70//! .with_credentials("admin", "password")
71//! .with_clock_sync() // syncs WS-Security timestamp with device clock
72//! .build()
73//! .await?;
74//!
75//! let profiles = session.get_profiles().await?;
76//! let uri = session.get_stream_uri(&profiles[0].token).await?;
77//! println!("RTSP stream: {}", uri.uri);
78//!
79//! let status = session.ptz_get_status(&profiles[0].token).await?;
80//! println!("Pan: {:?} Tilt: {:?}", status.pan, status.tilt);
81//! Ok(())
82//! }
83//! ```
84//!
85//! ### `OnvifClient` — direct control, you manage service URLs
86//!
87//! [`OnvifClient`] is stateless and gives direct control over every call.
88//! You fetch and forward service URLs yourself for full routing control.
89//!
90//! ```no_run
91//! use oxvif::{OnvifClient, OnvifError};
92//!
93//! async fn run() -> Result<(), OnvifError> {
94//! let client = OnvifClient::new("http://192.168.1.100/onvif/device_service")
95//! .with_credentials("admin", "password");
96//!
97//! let dt = client.get_system_date_and_time().await?;
98//! let client = client.with_utc_offset(dt.utc_offset_secs());
99//!
100//! let caps = client.get_capabilities().await?;
101//! let media_url = caps.media.url.as_deref().unwrap();
102//!
103//! let profiles = client.get_profiles(media_url).await?;
104//! let uri = client.get_stream_uri(media_url, &profiles[0].token).await?;
105//! println!("RTSP stream: {}", uri.uri);
106//! Ok(())
107//! }
108//! ```
109//!
110//! ## Testing without a real camera
111//!
112//! Implement [`transport::Transport`] to inject any XML fixture:
113//!
114//! ```no_run
115//! use oxvif::transport::{Transport, TransportError};
116//! use async_trait::async_trait;
117//! use std::sync::Arc;
118//!
119//! struct MockTransport { xml: String }
120//!
121//! #[async_trait]
122//! impl Transport for MockTransport {
123//! async fn soap_post(&self, _url: &str, _action: &str, _body: String)
124//! -> Result<String, TransportError>
125//! {
126//! Ok(self.xml.clone())
127//! }
128//! }
129//!
130//! # async fn example() {
131//! let client = oxvif::OnvifClient::new("http://ignored")
132//! .with_transport(Arc::new(MockTransport { xml: "<s:Envelope/>".into() }));
133//! # }
134//! ```
135//!
136//! [ONVIF]: https://www.onvif.org
137
138pub mod client;
139pub mod discovery;
140pub mod error;
141pub mod session;
142pub mod soap;
143pub mod transport;
144pub mod types;
145
146pub use client::{OnvifClient, notification_listener};
147pub use discovery::{DiscoveredDevice, DiscoveryEvent, probe_unicast};
148pub use error::OnvifError;
149pub use session::{OnvifSession, OnvifSessionBuilder};
150pub use types::{
151 AnalyticsCapabilities, AudioDecoderConfiguration, AudioEncoderConfiguration,
152 AudioEncoderConfigurationOptions, AudioEncoderOptions, AudioEncoding, AudioOutputConfiguration,
153 AudioSource, AudioSourceConfiguration, BoundsRange, Capabilities, DeviceCapabilities,
154 DeviceInfo, DeviceIoCapabilities, DnsInformation, EncoderInstanceInfo, EventProperties,
155 EventsCapabilities, FindRecordingResults, FloatRange, FocusMove, H264Configuration,
156 H264Options, H265Configuration, H265Options, Hostname, ImagingCapabilities, ImagingMoveOptions,
157 ImagingOptions, ImagingSettings, ImagingStatus, IntRange, IoCapabilities, JpegOptions,
158 Media2Capabilities, MediaCapabilities, MediaProfile, MediaProfile2, MetadataConfiguration,
159 MetadataConfigurationOptions, MulticastConfiguration, NetworkCapabilities, NetworkGateway,
160 NetworkInterface, NetworkProtocol, NotificationMessage, NtpInfo, OnvifService, OsdColor,
161 OsdConfiguration, OsdOptions, OsdPosition, OsdTextString, PtzCapabilities, PtzConfiguration,
162 PtzConfigurationOptions, PtzNode, PtzPreset, PtzSpaceRange, PtzSpeed, PtzStatus,
163 PullPointSubscription, PushSubscription, RecordingCapabilities, RecordingConfiguration,
164 RecordingInformation, RecordingItem, RecordingJob, RecordingJobConfiguration,
165 RecordingJobState, RecordingSourceInformation, RecordingTrack, RelayOutput, ReplayCapabilities,
166 Resolution, SearchCapabilities, SecurityCapabilities, SetDateTimeRequest, SnapshotUri,
167 SourceBounds, StorageConfiguration, StreamUri, StreamingCapabilities, SystemCapabilities,
168 SystemDateTime, SystemLog, SystemUris, User, UtcDateTime, VideoEncoderConfiguration,
169 VideoEncoderConfiguration2, VideoEncoderConfigurationOptions,
170 VideoEncoderConfigurationOptions2, VideoEncoderInstances, VideoEncoderOptions2, VideoEncoding,
171 VideoRateControl, VideoRateControl2, VideoSource, VideoSourceConfiguration,
172 VideoSourceConfigurationOptions, VideoSourceMode,
173};