use super::OnvifClient;
use crate::error::OnvifError;
use crate::soap::{find_response, parse_soap_body};
use crate::types::{
AudioEncoderConfiguration, AudioEncoderConfigurationOptions, AudioSource,
AudioSourceConfiguration, MediaProfile, OsdConfiguration, OsdOptions, SnapshotUri, StreamUri,
VideoEncoderConfiguration, VideoEncoderConfigurationOptions, VideoSource,
VideoSourceConfiguration, VideoSourceConfigurationOptions, xml_escape,
};
impl OnvifClient {
pub async fn get_profiles(&self, media_url: &str) -> Result<Vec<MediaProfile>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetProfiles";
const BODY: &str = "<trt:GetProfiles/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body = parse_soap_body(&xml)?;
let resp = find_response(&body, "GetProfilesResponse")?;
MediaProfile::vec_from_xml(resp)
}
pub async fn get_stream_uri(
&self,
media_url: &str,
profile_token: &str,
) -> Result<StreamUri, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetStreamUri";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:GetStreamUri>\
<trt:StreamSetup>\
<tt:Stream>RTP-Unicast</tt:Stream>\
<tt:Transport><tt:Protocol>RTSP</tt:Protocol></tt:Transport>\
</trt:StreamSetup>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:GetStreamUri>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetStreamUriResponse")?;
StreamUri::from_xml(resp)
}
pub async fn get_snapshot_uri(
&self,
media_url: &str,
profile_token: &str,
) -> Result<SnapshotUri, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetSnapshotUri";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:GetSnapshotUri>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:GetSnapshotUri>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetSnapshotUriResponse")?;
SnapshotUri::from_xml(resp)
}
pub async fn create_profile(
&self,
media_url: &str,
name: &str,
token: Option<&str>,
) -> Result<MediaProfile, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/CreateProfile";
let token_el = token
.map(|t| format!("<trt:Token>{}</trt:Token>", xml_escape(t)))
.unwrap_or_default();
let body = format!(
"<trt:CreateProfile>\
<trt:Name>{}</trt:Name>\
{token_el}\
</trt:CreateProfile>",
xml_escape(name)
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "CreateProfileResponse")?;
let p = resp
.child("Profile")
.ok_or_else(|| crate::soap::SoapError::missing("Profile"))?;
MediaProfile::from_xml(p)
}
pub async fn delete_profile(
&self,
media_url: &str,
profile_token: &str,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/DeleteProfile";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:DeleteProfile>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:DeleteProfile>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "DeleteProfileResponse")?;
Ok(())
}
pub async fn get_profile(
&self,
media_url: &str,
profile_token: &str,
) -> Result<MediaProfile, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetProfile";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:GetProfile>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:GetProfile>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetProfileResponse")?;
let p = resp
.child("Profile")
.ok_or_else(|| crate::soap::SoapError::missing("Profile"))?;
MediaProfile::from_xml(p)
}
pub async fn add_video_encoder_configuration(
&self,
media_url: &str,
profile_token: &str,
config_token: &str,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/AddVideoEncoderConfiguration";
let profile_token = xml_escape(profile_token);
let config_token = xml_escape(config_token);
let body = format!(
"<trt:AddVideoEncoderConfiguration>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
<trt:ConfigurationToken>{config_token}</trt:ConfigurationToken>\
</trt:AddVideoEncoderConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "AddVideoEncoderConfigurationResponse")?;
Ok(())
}
pub async fn remove_video_encoder_configuration(
&self,
media_url: &str,
profile_token: &str,
) -> Result<(), OnvifError> {
const ACTION: &str =
"http://www.onvif.org/ver10/media/wsdl/RemoveVideoEncoderConfiguration";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:RemoveVideoEncoderConfiguration>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:RemoveVideoEncoderConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "RemoveVideoEncoderConfigurationResponse")?;
Ok(())
}
pub async fn add_video_source_configuration(
&self,
media_url: &str,
profile_token: &str,
config_token: &str,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/AddVideoSourceConfiguration";
let profile_token = xml_escape(profile_token);
let config_token = xml_escape(config_token);
let body = format!(
"<trt:AddVideoSourceConfiguration>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
<trt:ConfigurationToken>{config_token}</trt:ConfigurationToken>\
</trt:AddVideoSourceConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "AddVideoSourceConfigurationResponse")?;
Ok(())
}
pub async fn remove_video_source_configuration(
&self,
media_url: &str,
profile_token: &str,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/RemoveVideoSourceConfiguration";
let profile_token = xml_escape(profile_token);
let body = format!(
"<trt:RemoveVideoSourceConfiguration>\
<trt:ProfileToken>{profile_token}</trt:ProfileToken>\
</trt:RemoveVideoSourceConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "RemoveVideoSourceConfigurationResponse")?;
Ok(())
}
pub async fn get_video_sources(&self, media_url: &str) -> Result<Vec<VideoSource>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetVideoSources";
const BODY: &str = "<trt:GetVideoSources/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoSourcesResponse")?;
VideoSource::vec_from_xml(resp)
}
pub async fn get_video_source_configurations(
&self,
media_url: &str,
) -> Result<Vec<VideoSourceConfiguration>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetVideoSourceConfigurations";
const BODY: &str = "<trt:GetVideoSourceConfigurations/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoSourceConfigurationsResponse")?;
VideoSourceConfiguration::vec_from_xml(resp)
}
pub async fn get_video_source_configuration(
&self,
media_url: &str,
token: &str,
) -> Result<VideoSourceConfiguration, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetVideoSourceConfiguration";
let token = xml_escape(token);
let body = format!(
"<trt:GetVideoSourceConfiguration>\
<trt:ConfigurationToken>{token}</trt:ConfigurationToken>\
</trt:GetVideoSourceConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoSourceConfigurationResponse")?;
let node = resp
.child("Configuration")
.ok_or_else(|| crate::soap::SoapError::missing("Configuration"))?;
VideoSourceConfiguration::from_xml(node)
}
pub async fn set_video_source_configuration(
&self,
media_url: &str,
config: &VideoSourceConfiguration,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/SetVideoSourceConfiguration";
let body = format!(
"<trt:SetVideoSourceConfiguration>\
{cfg}\
<trt:ForcePersistence>true</trt:ForcePersistence>\
</trt:SetVideoSourceConfiguration>",
cfg = config.to_xml_body()
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "SetVideoSourceConfigurationResponse")?;
Ok(())
}
pub async fn get_video_source_configuration_options(
&self,
media_url: &str,
config_token: Option<&str>,
) -> Result<VideoSourceConfigurationOptions, OnvifError> {
const ACTION: &str =
"http://www.onvif.org/ver10/media/wsdl/GetVideoSourceConfigurationOptions";
let inner = match config_token {
Some(tok) => format!(
"<trt:ConfigurationToken>{}</trt:ConfigurationToken>",
xml_escape(tok)
),
None => String::new(),
};
let body = format!(
"<trt:GetVideoSourceConfigurationOptions>{inner}</trt:GetVideoSourceConfigurationOptions>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoSourceConfigurationOptionsResponse")?;
VideoSourceConfigurationOptions::from_xml(resp)
}
pub async fn get_video_encoder_configurations(
&self,
media_url: &str,
) -> Result<Vec<VideoEncoderConfiguration>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetVideoEncoderConfigurations";
const BODY: &str = "<trt:GetVideoEncoderConfigurations/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoEncoderConfigurationsResponse")?;
VideoEncoderConfiguration::vec_from_xml(resp)
}
pub async fn get_video_encoder_configuration(
&self,
media_url: &str,
token: &str,
) -> Result<VideoEncoderConfiguration, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetVideoEncoderConfiguration";
let token = xml_escape(token);
let body = format!(
"<trt:GetVideoEncoderConfiguration>\
<trt:ConfigurationToken>{token}</trt:ConfigurationToken>\
</trt:GetVideoEncoderConfiguration>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoEncoderConfigurationResponse")?;
let node = resp
.child("Configuration")
.ok_or_else(|| crate::soap::SoapError::missing("Configuration"))?;
VideoEncoderConfiguration::from_xml(node)
}
pub async fn set_video_encoder_configuration(
&self,
media_url: &str,
config: &VideoEncoderConfiguration,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/SetVideoEncoderConfiguration";
let body = format!(
"<trt:SetVideoEncoderConfiguration>\
{cfg}\
<trt:ForcePersistence>true</trt:ForcePersistence>\
</trt:SetVideoEncoderConfiguration>",
cfg = config.to_xml_body()
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "SetVideoEncoderConfigurationResponse")?;
Ok(())
}
pub async fn get_video_encoder_configuration_options(
&self,
media_url: &str,
config_token: Option<&str>,
) -> Result<VideoEncoderConfigurationOptions, OnvifError> {
const ACTION: &str =
"http://www.onvif.org/ver10/media/wsdl/GetVideoEncoderConfigurationOptions";
let inner = match config_token {
Some(tok) => format!(
"<trt:ConfigurationToken>{}</trt:ConfigurationToken>",
xml_escape(tok)
),
None => String::new(),
};
let body = format!(
"<trt:GetVideoEncoderConfigurationOptions>{inner}</trt:GetVideoEncoderConfigurationOptions>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetVideoEncoderConfigurationOptionsResponse")?;
VideoEncoderConfigurationOptions::from_xml(resp)
}
pub async fn get_osds(
&self,
media_url: &str,
config_token: Option<&str>,
) -> Result<Vec<OsdConfiguration>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetOSDs";
let inner = config_token
.map(|t| format!("<trt:OSDToken>{}</trt:OSDToken>", xml_escape(t)))
.unwrap_or_default();
let body = format!("<trt:GetOSDs>{inner}</trt:GetOSDs>");
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetOSDsResponse")?;
OsdConfiguration::vec_from_xml(resp)
}
pub async fn get_osd(
&self,
media_url: &str,
osd_token: &str,
) -> Result<OsdConfiguration, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetOSD";
let osd_token = xml_escape(osd_token);
let body = format!("<trt:GetOSD><trt:OSDToken>{osd_token}</trt:OSDToken></trt:GetOSD>");
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetOSDResponse")?;
resp.child("OSDConfiguration")
.ok_or_else(|| crate::soap::SoapError::missing("OSDConfiguration").into())
.and_then(OsdConfiguration::from_xml)
}
pub async fn set_osd(&self, media_url: &str, osd: &OsdConfiguration) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/SetOSD";
let body = format!("<trt:SetOSD>{}</trt:SetOSD>", osd.to_xml_body());
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "SetOSDResponse")?;
Ok(())
}
pub async fn create_osd(
&self,
media_url: &str,
osd: &OsdConfiguration,
) -> Result<String, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/CreateOSD";
let body = format!("<trt:CreateOSD>{}</trt:CreateOSD>", osd.to_xml_body());
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "CreateOSDResponse")?;
resp.child("OSDToken")
.map(|n| n.text().to_string())
.ok_or_else(|| crate::soap::SoapError::missing("OSDToken").into())
}
pub async fn delete_osd(&self, media_url: &str, osd_token: &str) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/DeleteOSD";
let osd_token = xml_escape(osd_token);
let body =
format!("<trt:DeleteOSD><trt:OSDToken>{osd_token}</trt:OSDToken></trt:DeleteOSD>");
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "DeleteOSDResponse")?;
Ok(())
}
pub async fn get_osd_options(
&self,
media_url: &str,
config_token: &str,
) -> Result<OsdOptions, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetOSDOptions";
let config_token = xml_escape(config_token);
let body = format!(
"<trt:GetOSDOptions>\
<trt:ConfigurationToken>{config_token}</trt:ConfigurationToken>\
</trt:GetOSDOptions>"
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetOSDOptionsResponse")?;
OsdOptions::from_xml(resp)
}
pub async fn get_audio_sources(&self, media_url: &str) -> Result<Vec<AudioSource>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetAudioSources";
const BODY: &str = "<trt:GetAudioSources/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetAudioSourcesResponse")?;
AudioSource::vec_from_xml(resp)
}
pub async fn get_audio_source_configurations(
&self,
media_url: &str,
) -> Result<Vec<AudioSourceConfiguration>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetAudioSourceConfigurations";
const BODY: &str = "<trt:GetAudioSourceConfigurations/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetAudioSourceConfigurationsResponse")?;
AudioSourceConfiguration::vec_from_xml(resp)
}
pub async fn get_audio_encoder_configurations(
&self,
media_url: &str,
) -> Result<Vec<AudioEncoderConfiguration>, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetAudioEncoderConfigurations";
const BODY: &str = "<trt:GetAudioEncoderConfigurations/>";
let xml = self.call(media_url, ACTION, BODY).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetAudioEncoderConfigurationsResponse")?;
AudioEncoderConfiguration::vec_from_xml(resp)
}
pub async fn get_audio_encoder_configuration(
&self,
media_url: &str,
config_token: &str,
) -> Result<AudioEncoderConfiguration, OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/GetAudioEncoderConfiguration";
let body = format!(
"<trt:GetAudioEncoderConfiguration>\
<trt:ConfigurationToken>{}</trt:ConfigurationToken>\
</trt:GetAudioEncoderConfiguration>",
xml_escape(config_token)
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetAudioEncoderConfigurationResponse")?;
let node = resp
.child("Configuration")
.ok_or_else(|| crate::soap::SoapError::missing("Configuration"))?;
AudioEncoderConfiguration::from_xml(node)
}
pub async fn set_audio_encoder_configuration(
&self,
media_url: &str,
config: &AudioEncoderConfiguration,
) -> Result<(), OnvifError> {
const ACTION: &str = "http://www.onvif.org/ver10/media/wsdl/SetAudioEncoderConfiguration";
let body = format!(
"<trt:SetAudioEncoderConfiguration>\
{}\
<trt:ForcePersistence>true</trt:ForcePersistence>\
</trt:SetAudioEncoderConfiguration>",
config.to_xml_body()
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
find_response(&body_node, "SetAudioEncoderConfigurationResponse")?;
Ok(())
}
pub async fn get_audio_encoder_configuration_options(
&self,
media_url: &str,
config_token: &str,
) -> Result<AudioEncoderConfigurationOptions, OnvifError> {
const ACTION: &str =
"http://www.onvif.org/ver10/media/wsdl/GetAudioEncoderConfigurationOptions";
let body = format!(
"<trt:GetAudioEncoderConfigurationOptions>\
<trt:ConfigurationToken>{}</trt:ConfigurationToken>\
</trt:GetAudioEncoderConfigurationOptions>",
xml_escape(config_token)
);
let xml = self.call(media_url, ACTION, &body).await?;
let body_node = parse_soap_body(&xml)?;
let resp = find_response(&body_node, "GetAudioEncoderConfigurationOptionsResponse")?;
AudioEncoderConfigurationOptions::from_xml(resp)
}
}