codec::Parameters -> codec::ParametersRef

This reduces copying. Part of #47.
This commit is contained in:
Scott Lamb 2022-05-11 11:28:40 -07:00
parent a36b01b715
commit b1e1fa9031
11 changed files with 80 additions and 67 deletions

View File

@ -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)

View File

@ -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<W: AsyncWrite + AsyncSeek + Send + Unpin> {
mdat_start: u32,
mdat_pos: u32,
video_params: Vec<Box<VideoParameters>>,
video_params: Vec<VideoParameters>,
/// The most recently used 1-based index within `video_params`.
cur_video_params_sample_description_index: Option<u32>,
@ -548,13 +548,13 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
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())))
}

View File

@ -38,7 +38,12 @@ async fn run_inner(opts: Opts, session_group: Arc<SessionGroup>) -> 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())

View File

@ -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<crate::codec::Parameters> {
pub fn parameters(&self) -> Option<crate::codec::ParametersRef> {
self.depacketizer.as_ref().ok().and_then(|d| d.parameters())
}

View File

@ -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);

View File

@ -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<super::Parameters> {
Some(super::Parameters::Audio(self.config.to_parameters()))
pub(super) fn parameters(&self) -> Option<super::ParametersRef> {
Some(super::ParametersRef::Audio(&self.parameters))
}
pub(super) fn push(&mut self, pkt: ReceivedPacket) -> Result<(), String> {

View File

@ -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<super::AudioFrame>,
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<super::Parameters> {
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<super::ParametersRef> {
Some(super::ParametersRef::Audio(&self.parameters))
}
fn validate(pkt: &crate::rtp::ReceivedPacket) -> bool {

View File

@ -127,10 +127,10 @@ impl Depacketizer {
})
}
pub(super) fn parameters(&self) -> Option<super::Parameters> {
pub(super) fn parameters(&self) -> Option<super::ParametersRef> {
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!(),

View File

@ -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<Parameters> {
pub fn parameters(&self) -> Option<ParametersRef> {
match &self.0 {
DepacketizerInner::Aac(d) => d.parameters(),
DepacketizerInner::G723(d) => d.parameters(),
@ -595,7 +595,6 @@ mod tests {
"SenderReport",
std::mem::size_of::<crate::client::rtp::SenderReport>(),
),
("Parameters", std::mem::size_of::<Parameters>()),
("VideoParameters", std::mem::size_of::<VideoParameters>()),
("AudioParameters", std::mem::size_of::<AudioParameters>()),
(

View File

@ -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<super::Parameters> {
Some(super::Parameters::Message(super::MessageParameters(
self.compression_type,
)))
pub(super) fn parameters(&self) -> Option<super::ParametersRef> {
Some(super::ParametersRef::Message(&self.parameters))
}
pub(super) fn push(&mut self, pkt: crate::rtp::ReceivedPacket) -> Result<(), String> {

View File

@ -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<super::AudioFrame>,
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<super::Parameters> {
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<super::ParametersRef> {
Some(super::ParametersRef::Audio(&self.parameters))
}
fn frame_length(&self, payload_len: usize) -> Option<NonZeroU32> {