onvif_server/service/
imaging.rs1use async_trait::async_trait;
2use bytes::Bytes;
3use quick_xml::events::Event;
4use quick_xml::NsReader;
5use soap_server::{SoapFault, SoapHandler};
6use std::sync::Arc;
7
8use crate::error::OnvifError;
9use crate::service::xml_util::extract_text_ns;
10use crate::traits::ImagingService;
11
12const ONVIF_IMAGING_NS: &[u8] = b"http://www.onvif.org/ver20/imaging/wsdl";
14const ONVIF_SCHEMA_NS: &[u8] = b"http://www.onvif.org/ver10/schema";
15
16pub struct ImagingServiceHandler {
17 pub(crate) svc: Arc<dyn ImagingService>,
18}
19
20impl ImagingServiceHandler {
21 pub fn new(svc: Arc<dyn ImagingService>) -> Self {
22 Self { svc }
23 }
24}
25
26#[async_trait]
27impl SoapHandler for ImagingServiceHandler {
28 async fn handle(&self, body: Bytes) -> Result<Bytes, SoapFault> {
29 let op = extract_local_name(&body)?;
30 match op.as_str() {
31 "GetImagingSettings" => self.handle_get_imaging_settings(&body).await,
32 _ => Err(OnvifError::ActionNotSupported.into_soap_fault()),
33 }
34 }
35}
36
37fn extract_local_name(body: &Bytes) -> Result<String, SoapFault> {
38 let mut reader = NsReader::from_reader(body.as_ref());
39 reader.config_mut().trim_text(true);
40 loop {
41 match reader
42 .read_resolved_event()
43 .map_err(|e| SoapFault::sender(format!("{e}")))?
44 {
45 (_, Event::Start(e)) | (_, Event::Empty(e)) => {
46 let local = std::str::from_utf8(e.local_name().as_ref())
47 .map_err(|e| SoapFault::sender(format!("{e}")))?
48 .to_string();
49 return Ok(local);
50 }
51 (_, Event::Eof) => return Err(SoapFault::sender("Empty body".to_string())),
52 _ => {}
53 }
54 }
55}
56
57fn extract_text_element(body: &Bytes, element_name: &str) -> Result<String, SoapFault> {
58 extract_text_ns(body, element_name, &[ONVIF_IMAGING_NS, ONVIF_SCHEMA_NS])
59}
60
61impl ImagingServiceHandler {
62 async fn handle_get_imaging_settings(&self, body: &Bytes) -> Result<Bytes, SoapFault> {
63 let token = extract_text_element(body, "VideoSourceToken")?;
64 let settings = self
65 .svc
66 .get_imaging_settings(token)
67 .await
68 .map_err(|e| e.into_soap_fault())?;
69
70 let mut inner = String::new();
71 if let Some(v) = settings.brightness {
72 inner.push_str(&format!("<tt:Brightness>{}</tt:Brightness>", v as i32));
73 }
74 if let Some(v) = settings.contrast {
75 inner.push_str(&format!("<tt:Contrast>{}</tt:Contrast>", v as i32));
76 }
77 if let Some(v) = settings.sharpness {
78 inner.push_str(&format!("<tt:Sharpness>{}</tt:Sharpness>", v as i32));
79 }
80 if let Some(v) = settings.color_saturation {
81 inner.push_str(&format!(
82 "<tt:ColorSaturation>{}</tt:ColorSaturation>",
83 v as i32
84 ));
85 }
86 if settings.white_balance_cr_gain.is_some() || settings.white_balance_cb_gain.is_some() {
90 let cr = settings
91 .white_balance_cr_gain
92 .map(|v| format!("<tt:CrGain>{v}</tt:CrGain>"))
93 .unwrap_or_default();
94 let cb = settings
95 .white_balance_cb_gain
96 .map(|v| format!("<tt:CbGain>{v}</tt:CbGain>"))
97 .unwrap_or_default();
98 inner.push_str(&format!(
99 "<tt:WhiteBalance><tt:Mode>MANUAL</tt:Mode>{cr}{cb}</tt:WhiteBalance>"
100 ));
101 }
102
103 let xml = format!(
104 r#"<timg:GetImagingSettingsResponse xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"><timg:ImagingSettings>{inner}</timg:ImagingSettings></timg:GetImagingSettingsResponse>"#,
105 inner = inner,
106 );
107 Ok(Bytes::from(xml))
108 }
109}