diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4833f..c06becd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * BREAKING: `retina::client::PacketItem::SenderReport` has been replaced with `retina::client::PacketItem::Rtcp`, to expose full RTCP compound packets. Likewise `retina::codec::CodecItem`. +* BREAKING: `retina::codec::Parameters` is now `retina::codec::ParametersRef`, + which references parameters stored within the `Stream` to reduce copying. * minimum Rust version is now 1.59. ## `v0.3.10` (2022-05-09) diff --git a/examples/client/mp4.rs b/examples/client/mp4.rs index b6f9067..27641dd 100644 --- a/examples/client/mp4.rs +++ b/examples/client/mp4.rs @@ -23,7 +23,7 @@ use futures::{Future, StreamExt}; use log::{debug, info, warn}; use retina::{ client::{SetupOptions, Transport}, - codec::{AudioParameters, CodecItem, Parameters, VideoParameters}, + codec::{AudioParameters, CodecItem, ParametersRef, VideoParameters}, }; use std::num::NonZeroU32; @@ -98,7 +98,7 @@ macro_rules! write_box { pub struct Mp4Writer { mdat_start: u32, mdat_pos: u32, - video_params: Vec>, + video_params: Vec, /// The most recently used 1-based index within `video_params`. cur_video_params_sample_description_index: Option, @@ -548,13 +548,13 @@ impl Mp4Writer { i } else { match stream.parameters() { - Some(Parameters::Video(params)) => { + Some(ParametersRef::Video(params)) => { log::info!("new video params: {:?}", params); - let pos = self.video_params.iter().position(|p| **p == params); + let pos = self.video_params.iter().position(|p| p == params); if let Some(pos) = pos { u32::try_from(pos + 1)? } else { - self.video_params.push(Box::new(params)); + self.video_params.push(params.clone()); u32::try_from(self.video_params.len())? } } @@ -767,7 +767,7 @@ pub async fn run(opts: Opts) -> Result<(), Error> { .find_map(|(i, s)| match s.parameters() { // Only consider audio streams that can produce a .mp4 sample // entry. - Some(retina::codec::Parameters::Audio(a)) if a.sample_entry().is_some() => { + Some(retina::codec::ParametersRef::Audio(a)) if a.sample_entry().is_some() => { log::info!("Using {} audio stream (rfc 6381 codec {})", s.encoding_name(), a.rfc6381_codec().unwrap()); Some((i, Box::new(a.clone()))) } diff --git a/examples/client/onvif.rs b/examples/client/onvif.rs index 4e716e8..2d49467 100644 --- a/examples/client/onvif.rs +++ b/examples/client/onvif.rs @@ -38,7 +38,12 @@ async fn run_inner(opts: Opts, session_group: Arc) -> Result<(), E let onvif_stream_i = session .streams() .iter() - .position(|s| matches!(s.parameters(), Some(retina::codec::Parameters::Message(..)))) + .position(|s| { + matches!( + s.parameters(), + Some(retina::codec::ParametersRef::Message(..)) + ) + }) .ok_or_else(|| anyhow!("couldn't find onvif stream"))?; session .setup(onvif_stream_i, SetupOptions::default()) diff --git a/src/client/mod.rs b/src/client/mod.rs index 70dce99..6ce484c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -814,7 +814,7 @@ impl Stream { /// When directly using [`Session`]'s packet-by-packet `futures::Stream` impl, codec /// depacketization logic is bypassed. The parameters returned by this function may be out of /// date. - pub fn parameters(&self) -> Option { + pub fn parameters(&self) -> Option { self.depacketizer.as_ref().ok().and_then(|d| d.parameters()) } diff --git a/src/client/parse.rs b/src/client/parse.rs index 1633289..9373cd0 100644 --- a/src/client/parse.rs +++ b/src/client/parse.rs @@ -669,7 +669,7 @@ mod tests { use bytes::Bytes; use url::Url; - use crate::{client::StreamStateInit, codec::Parameters}; + use crate::{client::StreamStateInit, codec::ParametersRef}; use crate::{StreamContext, StreamContextInner, TcpStreamContext}; use super::super::StreamState; @@ -744,7 +744,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.64001E"); assert_eq!(v.pixel_dimensions(), (704, 480)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -763,7 +763,7 @@ mod tests { assert_eq!(p.streams[1].rtp_payload_type, 97); assert_eq!(p.streams[1].clock_rate_hz, 48_000); match p.streams[1].parameters() { - Some(Parameters::Audio(_)) => {} + Some(ParametersRef::Audio(_)) => {} _ => panic!(), } @@ -778,7 +778,7 @@ mod tests { assert_eq!(p.streams[2].clock_rate_hz, 90_000); assert!(matches!( p.streams[2].parameters(), - Some(Parameters::Message(_)) + Some(ParametersRef::Message(_)) )); // SETUP. @@ -825,7 +825,7 @@ mod tests { assert_eq!(p.streams[1].encoding_name(), "pcma"); assert_eq!(p.streams[1].rtp_payload_type, 8); match p.streams[1].parameters().unwrap() { - Parameters::Audio(_) => {} + ParametersRef::Audio(_) => {} _ => panic!(), }; } @@ -856,7 +856,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.4D0029"); assert_eq!(v.pixel_dimensions(), (1920, 1080)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -876,7 +876,7 @@ mod tests { assert_eq!(p.streams[1].clock_rate_hz, 90_000); assert!(matches!( p.streams[1].parameters(), - Some(Parameters::Message(_)) + Some(ParametersRef::Message(_)) )); // SETUP. @@ -936,7 +936,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.640033"); assert_eq!(v.pixel_dimensions(), (2560, 1440)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -955,7 +955,7 @@ mod tests { assert_eq!(p.streams[1].rtp_payload_type, 97); assert_eq!(p.streams[1].clock_rate_hz, 16_000); match p.streams[1].parameters() { - Some(Parameters::Audio(_)) => {} + Some(ParametersRef::Audio(_)) => {} _ => panic!(), } @@ -1019,7 +1019,7 @@ mod tests { assert_eq!(p.streams[0].clock_rate_hz, 12_000); assert_eq!(p.streams[0].channels, NonZeroU16::new(2)); match p.streams[0].parameters() { - Some(Parameters::Audio(_)) => {} + Some(ParametersRef::Audio(_)) => {} _ => panic!(), } @@ -1033,7 +1033,7 @@ mod tests { assert_eq!(p.streams[1].rtp_payload_type, 97); assert_eq!(p.streams[1].clock_rate_hz, 90_000); match p.streams[1].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.42C01E"); assert_eq!(v.pixel_dimensions(), (240, 160)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -1118,7 +1118,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.4D001F"); assert_eq!(v.pixel_dimensions(), (1280, 720)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -1138,7 +1138,7 @@ mod tests { assert_eq!(p.streams[1].clock_rate_hz, 8_000); assert_eq!(p.streams[1].channels, NonZeroU16::new(1)); match p.streams[1].parameters().unwrap() { - Parameters::Audio(_) => {} + ParametersRef::Audio(_) => {} _ => panic!(), }; } @@ -1165,7 +1165,7 @@ mod tests { assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.4D002A"); assert_eq!(v.pixel_dimensions(), (1920, 1080)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -1185,7 +1185,7 @@ mod tests { assert_eq!(p.streams[1].clock_rate_hz, 8_000); assert_eq!(p.streams[1].channels, NonZeroU16::new(1)); match p.streams[1].parameters().unwrap() { - Parameters::Audio(_) => {} + ParametersRef::Audio(_) => {} _ => panic!(), }; } @@ -1211,7 +1211,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.4D002A"); assert_eq!(v.pixel_dimensions(), (1920, 1080)); assert_eq!(v.pixel_aspect_ratio(), None); @@ -1231,7 +1231,7 @@ mod tests { assert_eq!(p.streams[1].clock_rate_hz, 8_000); assert_eq!(p.streams[1].channels, NonZeroU16::new(1)); match p.streams[1].parameters().unwrap() { - Parameters::Audio(_) => {} + ParametersRef::Audio(_) => {} _ => panic!(), }; @@ -1308,7 +1308,7 @@ mod tests { assert_eq!(p.streams[0].rtp_payload_type, 96); assert_eq!(p.streams[0].clock_rate_hz, 90_000); match p.streams[0].parameters().unwrap() { - Parameters::Video(v) => { + ParametersRef::Video(v) => { assert_eq!(v.rfc6381_codec(), "avc1.4D001E"); assert_eq!(v.pixel_dimensions(), (720, 480)); assert_eq!(v.pixel_aspect_ratio(), None); diff --git a/src/codec/aac.rs b/src/codec/aac.rs index a6c8fcf..12c389e 100644 --- a/src/codec/aac.rs +++ b/src/codec/aac.rs @@ -23,7 +23,7 @@ use std::{ use crate::{error::ErrorInt, rtp::ReceivedPacket, ConnectionContext, Error, StreamContext}; -use super::CodecItem; +use super::{AudioParameters, CodecItem}; /// An AudioSpecificConfig as in ISO/IEC 14496-3 section 1.6.2.1. /// @@ -432,6 +432,7 @@ fn parse_format_specific_params( #[derive(Debug)] pub(crate) struct Depacketizer { config: AudioSpecificConfig, + parameters: AudioParameters, state: DepacketizerState, } @@ -527,14 +528,16 @@ impl Depacketizer { channels, config.channels )); } + let parameters = config.to_parameters(); Ok(Self { config, + parameters, state: DepacketizerState::default(), }) } - pub(super) fn parameters(&self) -> Option { - Some(super::Parameters::Audio(self.config.to_parameters())) + pub(super) fn parameters(&self) -> Option { + Some(super::ParametersRef::Audio(&self.parameters)) } pub(super) fn push(&mut self, pkt: ReceivedPacket) -> Result<(), String> { diff --git a/src/codec/g723.rs b/src/codec/g723.rs index 9d1c6df..486c7bd 100644 --- a/src/codec/g723.rs +++ b/src/codec/g723.rs @@ -7,11 +7,14 @@ use std::num::NonZeroU32; use bytes::Bytes; +use super::AudioParameters; + const FIXED_CLOCK_RATE: u32 = 8_000; #[derive(Debug)] pub(crate) struct Depacketizer { pending: Option, + parameters: AudioParameters, } impl Depacketizer { @@ -23,17 +26,20 @@ impl Depacketizer { FIXED_CLOCK_RATE, clock_rate )); } - Ok(Self { pending: None }) + Ok(Self { + pending: None, + parameters: AudioParameters { + rfc6381_codec: None, + frame_length: NonZeroU32::new(240), + clock_rate: FIXED_CLOCK_RATE, + extra_data: Bytes::new(), + sample_entry: None, + }, + }) } - pub(super) fn parameters(&self) -> Option { - Some(super::Parameters::Audio(super::AudioParameters { - rfc6381_codec: None, - frame_length: NonZeroU32::new(240), - clock_rate: FIXED_CLOCK_RATE, - extra_data: Bytes::new(), - sample_entry: None, - })) + pub(super) fn parameters(&self) -> Option { + Some(super::ParametersRef::Audio(&self.parameters)) } fn validate(pkt: &crate::rtp::ReceivedPacket) -> bool { diff --git a/src/codec/h264.rs b/src/codec/h264.rs index c5af1ed..994ed7a 100644 --- a/src/codec/h264.rs +++ b/src/codec/h264.rs @@ -127,10 +127,10 @@ impl Depacketizer { }) } - pub(super) fn parameters(&self) -> Option { + pub(super) fn parameters(&self) -> Option { self.parameters .as_ref() - .map(|p| super::Parameters::Video(p.generic_parameters.clone())) + .map(|p| super::ParametersRef::Video(&p.generic_parameters)) } pub(super) fn push(&mut self, pkt: ReceivedPacket) -> Result<(), String> { @@ -1360,7 +1360,7 @@ mod tests { fn depacketize_parameter_change() { let mut d = super::Depacketizer::new(90_000, Some("a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z01AHppkBYHv/lBgYGQAAA+gAAE4gBA=,aO48gA==")).unwrap(); match d.parameters() { - Some(crate::codec::Parameters::Video(v)) => { + Some(crate::codec::ParametersRef::Video(v)) => { assert_eq!(v.pixel_dimensions(), (704, 480)); } _ => unreachable!(), @@ -1426,7 +1426,7 @@ mod tests { // After pull, new_parameters and parameters() both reflect the change. assert!(frame.has_new_parameters); match d.parameters() { - Some(crate::codec::Parameters::Video(v)) => { + Some(crate::codec::ParametersRef::Video(v)) => { assert_eq!(v.pixel_dimensions(), (640, 480)); } _ => unreachable!(), diff --git a/src/codec/mod.rs b/src/codec/mod.rs index d33a3e6..9de37df 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -35,7 +35,7 @@ pub enum CodecItem { Rtcp(crate::rtcp::ReceivedCompoundPacket), } -/// Parameters which describe a stream. +/// Reference to parameters which describe a stream. /// /// Parameters are often, but not always, available immediately /// after `DESCRIBE` via [`crate::client::Stream::parameters`]. They should @@ -47,10 +47,10 @@ pub enum CodecItem { /// /// Currently audio and message streams' parameters never change mid-stream. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Parameters { - Video(VideoParameters), - Audio(AudioParameters), - Message(MessageParameters), +pub enum ParametersRef<'a> { + Video(&'a VideoParameters), + Audio(&'a AudioParameters), + Message(&'a MessageParameters), } /// Parameters which describe a video stream. @@ -514,7 +514,7 @@ impl Depacketizer { /// If the caller has called `push` more recently than `pull`, it's currently undefined /// whether the depacketizer returns parameters as of the most recently pulled or the upcoming /// frame. - pub fn parameters(&self) -> Option { + pub fn parameters(&self) -> Option { match &self.0 { DepacketizerInner::Aac(d) => d.parameters(), DepacketizerInner::G723(d) => d.parameters(), @@ -595,7 +595,6 @@ mod tests { "SenderReport", std::mem::size_of::(), ), - ("Parameters", std::mem::size_of::()), ("VideoParameters", std::mem::size_of::()), ("AudioParameters", std::mem::size_of::()), ( diff --git a/src/codec/onvif.rs b/src/codec/onvif.rs index b312dba..6e6a77f 100644 --- a/src/codec/onvif.rs +++ b/src/codec/onvif.rs @@ -10,7 +10,7 @@ use bytes::{Buf, BufMut, BytesMut}; -use super::CodecItem; +use super::{CodecItem, MessageParameters}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum CompressionType { @@ -22,7 +22,7 @@ pub enum CompressionType { #[derive(Debug)] pub(crate) struct Depacketizer { - compression_type: CompressionType, + parameters: MessageParameters, state: State, high_water_size: usize, } @@ -45,16 +45,14 @@ struct InProgress { impl Depacketizer { pub(super) fn new(compression_type: CompressionType) -> Self { Depacketizer { - compression_type, + parameters: MessageParameters(compression_type), state: State::Idle, high_water_size: 0, } } - pub(super) fn parameters(&self) -> Option { - Some(super::Parameters::Message(super::MessageParameters( - self.compression_type, - ))) + pub(super) fn parameters(&self) -> Option { + Some(super::ParametersRef::Message(&self.parameters)) } pub(super) fn push(&mut self, pkt: crate::rtp::ReceivedPacket) -> Result<(), String> { diff --git a/src/codec/simple_audio.rs b/src/codec/simple_audio.rs index 74ac405..1be2c29 100644 --- a/src/codec/simple_audio.rs +++ b/src/codec/simple_audio.rs @@ -8,11 +8,11 @@ use std::num::NonZeroU32; use bytes::Bytes; -use super::CodecItem; +use super::{AudioParameters, CodecItem}; #[derive(Debug)] pub(crate) struct Depacketizer { - clock_rate: u32, + parameters: AudioParameters, pending: Option, bits_per_sample: u32, } @@ -21,20 +21,20 @@ impl Depacketizer { /// Creates a new Depacketizer. pub(super) fn new(clock_rate: u32, bits_per_sample: u32) -> Self { Self { - clock_rate, + parameters: AudioParameters { + rfc6381_codec: None, + frame_length: None, // variable + clock_rate, + extra_data: Bytes::new(), + sample_entry: None, + }, bits_per_sample, pending: None, } } - pub(super) fn parameters(&self) -> Option { - Some(super::Parameters::Audio(super::AudioParameters { - rfc6381_codec: None, - frame_length: None, // variable - clock_rate: self.clock_rate, - extra_data: Bytes::new(), - sample_entry: None, - })) + pub(super) fn parameters(&self) -> Option { + Some(super::ParametersRef::Audio(&self.parameters)) } fn frame_length(&self, payload_len: usize) -> Option {