no Bytes
in AudioParameters
interface either
For #47. Along the way, I simplified `aac::Depacketizer` impl a bit, eliminating a clone and some redundancy.
This commit is contained in:
parent
b1e1fa9031
commit
a33ba9b8af
@ -30,13 +30,9 @@ use super::{AudioParameters, CodecItem};
|
|||||||
/// Currently stores the raw form and a few fields of interest.
|
/// Currently stores the raw form and a few fields of interest.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct AudioSpecificConfig {
|
struct AudioSpecificConfig {
|
||||||
raw: Bytes,
|
parameters: AudioParameters,
|
||||||
sample_entry: Bytes,
|
|
||||||
|
|
||||||
/// See ISO/IEC 14496-3 Table 1.3.
|
|
||||||
audio_object_type: u8,
|
|
||||||
frame_length: NonZeroU16,
|
frame_length: NonZeroU16,
|
||||||
sampling_frequency: u32,
|
|
||||||
channels: &'static ChannelConfig,
|
channels: &'static ChannelConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,28 +155,22 @@ impl AudioSpecificConfig {
|
|||||||
(_, true) => NonZeroU16::new(960).expect("non-zero"),
|
(_, true) => NonZeroU16::new(960).expect("non-zero"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc6381#section-3.3
|
||||||
|
let rfc6381_codec = Some(format!("mp4a.40.{}", audio_object_type));
|
||||||
|
|
||||||
Ok(AudioSpecificConfig {
|
Ok(AudioSpecificConfig {
|
||||||
raw: Bytes::copy_from_slice(raw),
|
parameters: AudioParameters {
|
||||||
sample_entry: make_sample_entry(channels, sampling_frequency, raw)?,
|
// See also TODO asking if clock_rate and sampling_frequency must match.
|
||||||
audio_object_type,
|
clock_rate: sampling_frequency,
|
||||||
|
rfc6381_codec,
|
||||||
|
frame_length: Some(NonZeroU32::from(frame_length)),
|
||||||
|
extra_data: raw.to_owned(),
|
||||||
|
sample_entry: Some(make_sample_entry(channels, sampling_frequency, raw)?),
|
||||||
|
},
|
||||||
frame_length,
|
frame_length,
|
||||||
sampling_frequency,
|
|
||||||
channels,
|
channels,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_parameters(&self) -> super::AudioParameters {
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc6381#section-3.3
|
|
||||||
let rfc6381_codec = Some(format!("mp4a.40.{}", self.audio_object_type));
|
|
||||||
super::AudioParameters {
|
|
||||||
// See also TODO asking if clock_rate and sampling_frequency must match.
|
|
||||||
clock_rate: self.sampling_frequency,
|
|
||||||
rfc6381_codec,
|
|
||||||
frame_length: Some(NonZeroU32::from(self.frame_length)),
|
|
||||||
extra_data: self.raw.clone(),
|
|
||||||
sample_entry: Some(self.sample_entry.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrites a buffer with a varint length, returning the length of the length.
|
/// Overwrites a buffer with a varint length, returning the length of the length.
|
||||||
@ -220,7 +210,7 @@ macro_rules! write_box {
|
|||||||
// is expected to reference `$buf` via the original name.
|
// is expected to reference `$buf` via the original name.
|
||||||
#[allow(clippy::unnecessary_mut_passed)]
|
#[allow(clippy::unnecessary_mut_passed)]
|
||||||
{
|
{
|
||||||
let _: &mut BytesMut = $buf; // type-check.
|
let _: &mut Vec<u8> = $buf; // type-check.
|
||||||
|
|
||||||
let pos_start = $buf.len();
|
let pos_start = $buf.len();
|
||||||
let fourcc: &[u8; 4] = $fourcc;
|
let fourcc: &[u8; 4] = $fourcc;
|
||||||
@ -244,7 +234,7 @@ macro_rules! write_box {
|
|||||||
/// scope. See ISO/IEC 14496-1 Table 1 for the `tag`.
|
/// scope. See ISO/IEC 14496-1 Table 1 for the `tag`.
|
||||||
macro_rules! write_descriptor {
|
macro_rules! write_descriptor {
|
||||||
($buf:expr, $tag:expr, $b:block) => {{
|
($buf:expr, $tag:expr, $b:block) => {{
|
||||||
let _: &mut BytesMut = $buf; // type-check.
|
let _: &mut Vec<u8> = $buf; // type-check.
|
||||||
let _: u8 = $tag;
|
let _: u8 = $tag;
|
||||||
let pos_start = $buf.len();
|
let pos_start = $buf.len();
|
||||||
|
|
||||||
@ -274,8 +264,8 @@ fn make_sample_entry(
|
|||||||
channels: &ChannelConfig,
|
channels: &ChannelConfig,
|
||||||
sampling_frequency: u32,
|
sampling_frequency: u32,
|
||||||
config: &[u8],
|
config: &[u8],
|
||||||
) -> Result<Bytes, String> {
|
) -> Result<Vec<u8>, String> {
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
// Write an MP4AudioSampleEntry (`mp4a`), as in ISO/IEC 14496-14 section 5.6.1.
|
// Write an MP4AudioSampleEntry (`mp4a`), as in ISO/IEC 14496-14 section 5.6.1.
|
||||||
// It's based on AudioSampleEntry, ISO/IEC 14496-12 section 12.2.3.2,
|
// It's based on AudioSampleEntry, ISO/IEC 14496-12 section 12.2.3.2,
|
||||||
@ -354,7 +344,7 @@ fn make_sample_entry(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Ok(buf.freeze())
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses metadata from the `format-specific-params` of a SDP `fmtp` media attribute.
|
/// Parses metadata from the `format-specific-params` of a SDP `fmtp` media attribute.
|
||||||
@ -418,11 +408,12 @@ fn parse_format_specific_params(
|
|||||||
|
|
||||||
let config = AudioSpecificConfig::parse(&config[..])?;
|
let config = AudioSpecificConfig::parse(&config[..])?;
|
||||||
|
|
||||||
// TODO: is this a requirement? I might have read somewhere one can be a multiple of the other.
|
// TODO: is this a requirement? I might have read somewhere that the RTP clock rate can be
|
||||||
if clock_rate != config.sampling_frequency {
|
// a multiple of the AudioSpecificConfig sampling_frequency or vice versa.
|
||||||
|
if clock_rate != config.parameters.clock_rate {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Expected RTP clock rate {} and AAC sampling frequency {} to match",
|
"Expected RTP clock rate {} and AAC sampling frequency {} to match",
|
||||||
clock_rate, config.sampling_frequency
|
clock_rate, config.parameters.clock_rate,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +423,6 @@ fn parse_format_specific_params(
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Depacketizer {
|
pub(crate) struct Depacketizer {
|
||||||
config: AudioSpecificConfig,
|
config: AudioSpecificConfig,
|
||||||
parameters: AudioParameters,
|
|
||||||
state: DepacketizerState,
|
state: DepacketizerState,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,16 +518,14 @@ impl Depacketizer {
|
|||||||
channels, config.channels
|
channels, config.channels
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let parameters = config.to_parameters();
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
config,
|
config,
|
||||||
parameters,
|
|
||||||
state: DepacketizerState::default(),
|
state: DepacketizerState::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parameters(&self) -> Option<super::ParametersRef> {
|
pub(super) fn parameters(&self) -> Option<super::ParametersRef> {
|
||||||
Some(super::ParametersRef::Audio(&self.parameters))
|
Some(super::ParametersRef::Audio(&self.config.parameters))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn push(&mut self, pkt: ReceivedPacket) -> Result<(), String> {
|
pub(super) fn push(&mut self, pkt: ReceivedPacket) -> Result<(), String> {
|
||||||
@ -797,15 +785,15 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_audio_specific_config() {
|
fn parse_audio_specific_config() {
|
||||||
let dahua = AudioSpecificConfig::parse(&[0x11, 0x88]).unwrap();
|
let dahua = AudioSpecificConfig::parse(&[0x11, 0x88]).unwrap();
|
||||||
assert_eq!(dahua.sampling_frequency, 48_000);
|
assert_eq!(dahua.parameters.clock_rate, 48_000);
|
||||||
assert_eq!(dahua.channels.name, "mono");
|
assert_eq!(dahua.channels.name, "mono");
|
||||||
|
|
||||||
let bunny = AudioSpecificConfig::parse(&[0x14, 0x90]).unwrap();
|
let bunny = AudioSpecificConfig::parse(&[0x14, 0x90]).unwrap();
|
||||||
assert_eq!(bunny.sampling_frequency, 12_000);
|
assert_eq!(bunny.parameters.clock_rate, 12_000);
|
||||||
assert_eq!(bunny.channels.name, "stereo");
|
assert_eq!(bunny.channels.name, "stereo");
|
||||||
|
|
||||||
let rfc3640 = AudioSpecificConfig::parse(&[0x11, 0xB0]).unwrap();
|
let rfc3640 = AudioSpecificConfig::parse(&[0x11, 0xB0]).unwrap();
|
||||||
assert_eq!(rfc3640.sampling_frequency, 48_000);
|
assert_eq!(rfc3640.parameters.clock_rate, 48_000);
|
||||||
assert_eq!(rfc3640.channels.name, "5.1");
|
assert_eq!(rfc3640.channels.name, "5.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use bytes::Bytes;
|
|
||||||
|
|
||||||
use super::AudioParameters;
|
use super::AudioParameters;
|
||||||
|
|
||||||
const FIXED_CLOCK_RATE: u32 = 8_000;
|
const FIXED_CLOCK_RATE: u32 = 8_000;
|
||||||
@ -32,7 +30,7 @@ impl Depacketizer {
|
|||||||
rfc6381_codec: None,
|
rfc6381_codec: None,
|
||||||
frame_length: NonZeroU32::new(240),
|
frame_length: NonZeroU32::new(240),
|
||||||
clock_rate: FIXED_CLOCK_RATE,
|
clock_rate: FIXED_CLOCK_RATE,
|
||||||
extra_data: Bytes::new(),
|
extra_data: Vec::new(),
|
||||||
sample_entry: None,
|
sample_entry: None,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -139,8 +139,8 @@ pub struct AudioParameters {
|
|||||||
rfc6381_codec: Option<String>,
|
rfc6381_codec: Option<String>,
|
||||||
frame_length: Option<NonZeroU32>,
|
frame_length: Option<NonZeroU32>,
|
||||||
clock_rate: u32,
|
clock_rate: u32,
|
||||||
extra_data: Bytes,
|
extra_data: Vec<u8>,
|
||||||
sample_entry: Option<Bytes>,
|
sample_entry: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for AudioParameters {
|
impl std::fmt::Debug for AudioParameters {
|
||||||
@ -172,7 +172,7 @@ impl AudioParameters {
|
|||||||
|
|
||||||
/// The codec-specific "extra data" to feed to eg ffmpeg to decode the audio.
|
/// The codec-specific "extra data" to feed to eg ffmpeg to decode the audio.
|
||||||
/// * AAC: a serialized `AudioSpecificConfig`.
|
/// * AAC: a serialized `AudioSpecificConfig`.
|
||||||
pub fn extra_data(&self) -> &Bytes {
|
pub fn extra_data(&self) -> &[u8] {
|
||||||
&self.extra_data
|
&self.extra_data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,8 +180,8 @@ impl AudioParameters {
|
|||||||
///
|
///
|
||||||
/// Not all codecs can be placed into a `.mp4` file, and even for supported codecs there
|
/// Not all codecs can be placed into a `.mp4` file, and even for supported codecs there
|
||||||
/// may be unsupported edge cases.
|
/// may be unsupported edge cases.
|
||||||
pub fn sample_entry(&self) -> Option<&Bytes> {
|
pub fn sample_entry(&self) -> Option<&[u8]> {
|
||||||
self.sample_entry.as_ref()
|
self.sample_entry.as_deref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use bytes::Bytes;
|
|
||||||
|
|
||||||
use super::{AudioParameters, CodecItem};
|
use super::{AudioParameters, CodecItem};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -25,7 +23,7 @@ impl Depacketizer {
|
|||||||
rfc6381_codec: None,
|
rfc6381_codec: None,
|
||||||
frame_length: None, // variable
|
frame_length: None, // variable
|
||||||
clock_rate,
|
clock_rate,
|
||||||
extra_data: Bytes::new(),
|
extra_data: Vec::new(),
|
||||||
sample_entry: None,
|
sample_entry: None,
|
||||||
},
|
},
|
||||||
bits_per_sample,
|
bits_per_sample,
|
||||||
|
Loading…
Reference in New Issue
Block a user