Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add h265 support - Part II: Onvif #448

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
54fc7b6
src: lib: logger: Filter out unwanted logs from 3rd party crates
joaoantoniocardoso Nov 7, 2024
c941df3
src: lib: stream: webrtc: Accept H265 on WebRTC
joaoantoniocardoso Nov 7, 2024
5d9a365
src: lib: stream: sink: Avoid negotiation loops when failing to negot…
joaoantoniocardoso Nov 7, 2024
f363ff5
src: lib: stream: sink: Fix SDP customization, fix logs
joaoantoniocardoso Nov 7, 2024
aef05bc
src: lib: stream: pipeline: Fix log wording
joaoantoniocardoso Nov 7, 2024
e54c15e
src: lib: stream: pipeline: Add log context to PipelineRunner task me…
joaoantoniocardoso Nov 7, 2024
9185981
src: lib: stream: webrtc: Fix log wording
joaoantoniocardoso Nov 7, 2024
bae724e
src: lib: video: Change VideoSourceAvailable to async
joaoantoniocardoso Nov 6, 2024
e4acba6
src: lib: adapt to the async VideoSourceAvailable
joaoantoniocardoso Nov 6, 2024
3718457
src: lib: video: extract formats into an async trait VideoSourceFormats
joaoantoniocardoso Nov 7, 2024
1e97357
src: lib: adapt to the async VideoSourceFormats
joaoantoniocardoso Nov 7, 2024
9a0d2a3
src: lib: stream: gst: Add get_encode_from_rtspsrc helper function
joaoantoniocardoso Nov 7, 2024
1368ef7
src: lib: controls: onvif: Refactor Onvif code
joaoantoniocardoso Nov 7, 2024
5ed9f07
src: lib: video: Add Onvif video source
joaoantoniocardoso Nov 7, 2024
83973e6
src: lib: server: Propagate Onvif video source
joaoantoniocardoso Nov 7, 2024
9a822a8
src: lib: video: Add Onvif Pipeline
joaoantoniocardoso Nov 7, 2024
edf8d35
src: Update onvif initialization to async
joaoantoniocardoso Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 76 additions & 38 deletions src/lib/controls/onvif/client.rs → src/lib/controls/onvif/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use onvif_schema::{devicemgmt::GetDeviceInformationResponse, transport};
use anyhow::{anyhow, Result};
use tracing::*;

pub struct Clients {
use crate::{stream::gst::utils::get_encode_from_rtspsrc, video::types::Format};

#[derive(Clone)]
pub struct OnvifCamera {
devicemgmt: soap::client::Client,
event: Option<soap::client::Client>,
deviceio: Option<soap::client::Client>,
Expand All @@ -13,22 +16,29 @@ pub struct Clients {
imaging: Option<soap::client::Client>,
ptz: Option<soap::client::Client>,
analytics: Option<soap::client::Client>,
pub streams_information: Option<Vec<OnvifStreamInformation>>,
}

pub struct Auth {
pub credentials: Option<soap::client::Credentials>,
pub url: Box<str>,
pub url: url::Url,
}

#[derive(Debug, Clone)]
pub struct OnvifStreamInformation {
pub stream_uri: url::Url,
pub format: Format,
}

impl Clients {
#[instrument(level = "debug", skip(auth))]
impl OnvifCamera {
#[instrument(level = "trace", skip(auth))]
pub async fn try_new(auth: &Auth) -> Result<Self> {
let creds = &auth.credentials;
let devicemgmt_uri = url::Url::parse(&auth.url)?;
let devicemgmt_uri = &auth.url;
let base_uri = &devicemgmt_uri.origin().ascii_serialization();

let mut this = Self {
devicemgmt: soap::client::ClientBuilder::new(&devicemgmt_uri)
devicemgmt: soap::client::ClientBuilder::new(devicemgmt_uri)
.credentials(creds.clone())
.build(),
imaging: None,
Expand All @@ -38,6 +48,7 @@ impl Clients {
media: None,
media2: None,
analytics: None,
streams_information: None,
};

let services =
Expand All @@ -57,7 +68,7 @@ impl Clients {
);
match service.namespace.as_str() {
"http://www.onvif.org/ver10/device/wsdl" => {
if service_url != devicemgmt_uri {
if &service_url != devicemgmt_uri {
warn!(
"advertised device mgmt uri {service_url} not expected {devicemgmt_uri}"
);
Expand All @@ -70,31 +81,38 @@ impl Clients {
"http://www.onvif.org/ver20/imaging/wsdl" => this.imaging = svc,
"http://www.onvif.org/ver20/ptz/wsdl" => this.ptz = svc,
"http://www.onvif.org/ver20/analytics/wsdl" => this.analytics = svc,
_ => debug!("unknown service: {:?}", service),
_ => trace!("unknown service: {:?}", service),
}
}

this.streams_information
.replace(this.get_streams_information().await?);

Ok(this)
}

#[instrument(level = "debug", skip(self))]
#[instrument(level = "trace", skip(self))]
pub async fn get_device_information(
&self,
) -> Result<GetDeviceInformationResponse, transport::Error> {
onvif_schema::devicemgmt::get_device_information(&self.devicemgmt, &Default::default())
.await
}

pub async fn get_stream_uris(&self) -> Result<Vec<url::Url>, transport::Error> {
let mut urls: Vec<url::Url> = vec![];
async fn get_streams_information(
&self,
) -> Result<Vec<OnvifStreamInformation>, transport::Error> {
let mut streams_information = vec![];

let media_client = self
.media
.as_ref()
.ok_or_else(|| transport::Error::Other("Client media is not available".into()))?;
let profiles = onvif_schema::media::get_profiles(media_client, &Default::default()).await?;
debug!("get_profiles response: {:#?}", &profiles);
let profiles = onvif_schema::media::get_profiles(media_client, &Default::default())
.await?
.profiles;
trace!("get_profiles response: {:#?}", &profiles);
let requests: Vec<_> = profiles
.profiles
.iter()
.map(
|p: &onvif_schema::onvif::Profile| onvif_schema::media::GetStreamUri {
Expand All @@ -116,34 +134,54 @@ impl Clients {
.map(|r| onvif_schema::media::get_stream_uri(media_client, r)),
)
.await?;
for (p, resp) in profiles.profiles.iter().zip(responses.iter()) {
debug!("token={} name={}", &p.token.0, &p.name.0);
debug!(" {}", &resp.media_uri.uri);
match url::Url::parse(&resp.media_uri.uri) {
Ok(address) => urls.push(address),
for (profile, stream_uri_response) in profiles.iter().zip(responses.iter()) {
trace!("token={} name={}", &profile.token.0, &profile.name.0);
trace!("\t{}", &stream_uri_response.media_uri.uri);

let stream_uri = match url::Url::parse(&stream_uri_response.media_uri.uri) {
Ok(stream_url) => stream_url,
Err(error) => {
error!(
"Failed to parse stream url: {}, reason: {error:?}",
&resp.media_uri.uri
)
}
}
if let Some(ref v) = p.video_encoder_configuration {
debug!(
" {:?}, {}x{}",
v.encoding, v.resolution.width, v.resolution.height
);
if let Some(ref r) = v.rate_control {
debug!(" {} fps, {} kbps", r.frame_rate_limit, r.bitrate_limit);
&stream_uri_response.media_uri.uri
);
continue;
}
}
if let Some(ref a) = p.audio_encoder_configuration {
debug!(
" audio: {:?}, {} kbps, {} kHz",
a.encoding, a.bitrate, a.sample_rate
);
}
};

let Some(video_encoder_configuration) = &profile.video_encoder_configuration else {
warn!("Skipping uri with no encoders");
continue;
};

let Some(encode) = get_encode_from_rtspsrc(&stream_uri).await else {
continue;
};

let video_rate = video_encoder_configuration
.rate_control
.as_ref()
.map(|rate_control| {
(rate_control.frame_rate_limit as f32
/ rate_control.encoding_interval.max(1) as f32) as u32
})
.unwrap_or_default();

let intervals = vec![crate::video::types::FrameInterval {
numerator: 1,
denominator: video_rate,
}];

let sizes = vec![crate::video::types::Size {
width: video_encoder_configuration.resolution.width.max(0) as u32,
height: video_encoder_configuration.resolution.height.max(0) as u32,
intervals,
}];

let format = Format { encode, sizes };

streams_information.push(OnvifStreamInformation { stream_uri, format });
}
Ok(urls)
Ok(streams_information)
}
}
Loading
Loading