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