2023-04-22 19:52:23 +00:00
|
|
|
use crate::error::Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
use crate::FlvReader;
|
2023-04-11 00:30:47 +00:00
|
|
|
use std::io::Seek;
|
2023-04-08 21:52:33 +00:00
|
|
|
|
2023-06-24 00:31:52 +00:00
|
|
|
#[repr(u8)]
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
2023-06-24 00:31:52 +00:00
|
|
|
pub enum SoundFormat {
|
|
|
|
LinearPCMPlatformEndian = 0,
|
|
|
|
Adpcm = 1,
|
|
|
|
MP3 = 2,
|
|
|
|
LinearPCMLittleEndian = 3,
|
|
|
|
Nellymoser16kHz = 4,
|
|
|
|
Nellymoser8kHz = 5,
|
|
|
|
Nellymoser = 6,
|
|
|
|
G711ALawPCM = 7,
|
|
|
|
G711MuLawPCM = 8,
|
|
|
|
Aac = 10,
|
|
|
|
Speex = 11,
|
|
|
|
MP38kHz = 14,
|
|
|
|
DeviceSpecific = 15,
|
|
|
|
}
|
|
|
|
|
2023-04-08 21:52:33 +00:00
|
|
|
impl TryFrom<u8> for SoundFormat {
|
2023-04-22 19:52:23 +00:00
|
|
|
type Error = Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
0 => Ok(Self::LinearPCMPlatformEndian),
|
|
|
|
1 => Ok(Self::Adpcm),
|
|
|
|
2 => Ok(Self::MP3),
|
|
|
|
3 => Ok(Self::LinearPCMLittleEndian),
|
|
|
|
4 => Ok(Self::Nellymoser16kHz),
|
|
|
|
5 => Ok(Self::Nellymoser8kHz),
|
|
|
|
6 => Ok(Self::Nellymoser),
|
|
|
|
7 => Ok(Self::G711ALawPCM),
|
|
|
|
8 => Ok(Self::G711MuLawPCM),
|
|
|
|
10 => Ok(Self::Aac),
|
|
|
|
11 => Ok(Self::Speex),
|
|
|
|
14 => Ok(Self::MP38kHz),
|
|
|
|
15 => Ok(Self::DeviceSpecific),
|
2023-04-22 19:52:23 +00:00
|
|
|
unk => Err(Error::UnknownAudioFormatType(unk)),
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-24 00:31:52 +00:00
|
|
|
#[repr(u8)]
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
2023-06-24 00:31:52 +00:00
|
|
|
pub enum SoundRate {
|
|
|
|
R5_500 = 0,
|
|
|
|
R11_000 = 1,
|
|
|
|
R22_000 = 2,
|
|
|
|
R44_000 = 3,
|
|
|
|
}
|
|
|
|
|
2023-04-08 21:52:33 +00:00
|
|
|
impl TryFrom<u8> for SoundRate {
|
2023-04-22 19:52:23 +00:00
|
|
|
type Error = Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
0 => Ok(Self::R5_500),
|
|
|
|
1 => Ok(Self::R11_000),
|
|
|
|
2 => Ok(Self::R22_000),
|
|
|
|
3 => Ok(Self::R44_000),
|
2023-04-22 19:52:23 +00:00
|
|
|
unk => Err(Error::UnknownAudioRate(unk)), //probably unreachable
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-24 00:31:52 +00:00
|
|
|
#[repr(u8)]
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
2023-06-24 00:31:52 +00:00
|
|
|
pub enum SoundSize {
|
|
|
|
Bits8 = 0,
|
|
|
|
Bits16 = 1,
|
|
|
|
}
|
|
|
|
|
2023-04-08 21:52:33 +00:00
|
|
|
impl TryFrom<u8> for SoundSize {
|
2023-04-22 19:52:23 +00:00
|
|
|
type Error = Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
0 => Ok(Self::Bits8),
|
|
|
|
1 => Ok(Self::Bits16),
|
2023-04-22 19:52:23 +00:00
|
|
|
unk => Err(Error::UnknownAudioSampleSize(unk)), //probably unreachable
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-24 00:31:52 +00:00
|
|
|
#[repr(u8)]
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
2023-06-24 00:31:52 +00:00
|
|
|
pub enum SoundType {
|
|
|
|
Mono = 0,
|
|
|
|
Stereo = 1,
|
|
|
|
}
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
impl TryFrom<u8> for SoundType {
|
2023-04-22 19:52:23 +00:00
|
|
|
type Error = Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
0 => Ok(Self::Mono),
|
|
|
|
1 => Ok(Self::Stereo),
|
2023-04-22 19:52:23 +00:00
|
|
|
unk => Err(Error::UnknownAudioChannelCount(unk)), //probably unreachable
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
2023-04-08 22:16:41 +00:00
|
|
|
pub enum AudioDataType<'a> {
|
|
|
|
Raw(&'a [u8]),
|
|
|
|
AacSequenceHeader(&'a [u8]),
|
|
|
|
AacRaw(&'a [u8]),
|
|
|
|
}
|
|
|
|
|
2023-04-12 23:15:05 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
2023-04-08 21:52:33 +00:00
|
|
|
pub struct AudioData<'a> {
|
2023-04-10 23:33:51 +00:00
|
|
|
pub format: SoundFormat,
|
|
|
|
pub rate: SoundRate,
|
|
|
|
pub size: SoundSize,
|
|
|
|
pub sound_type: SoundType,
|
|
|
|
pub data: AudioDataType<'a>,
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> AudioData<'a> {
|
|
|
|
/// Parse an audio data structure.
|
|
|
|
///
|
|
|
|
/// This does not parse the actual audio data itself, which is instead
|
|
|
|
/// returned as an array that must be provided to your audio decoder.
|
|
|
|
///
|
|
|
|
/// `data_size` is the size of the entire audio data structure, *including*
|
2023-04-22 19:52:23 +00:00
|
|
|
/// the header. Errors are yielded if the `data_size` is too small for the
|
|
|
|
/// audio data present in the tag. This should not be confused for
|
|
|
|
/// `EndOfData` which indicates that we've read past the end of the whole
|
|
|
|
/// data stream.
|
|
|
|
pub fn parse(reader: &mut FlvReader<'a>, data_size: u32) -> Result<Self, Error> {
|
2023-04-11 00:30:47 +00:00
|
|
|
let start = reader.stream_position().expect("current position") as usize;
|
2023-04-08 21:52:33 +00:00
|
|
|
let format_spec = reader.read_u8()?;
|
|
|
|
|
2023-04-22 19:52:23 +00:00
|
|
|
let format = SoundFormat::try_from(format_spec >> 4)?;
|
|
|
|
let rate = SoundRate::try_from((format_spec >> 2) & 0x03)?;
|
|
|
|
let size = SoundSize::try_from((format_spec >> 1) & 0x01)?;
|
|
|
|
let sound_type = SoundType::try_from(format_spec & 0x01)?;
|
2023-04-08 22:16:41 +00:00
|
|
|
|
2023-04-11 00:30:47 +00:00
|
|
|
let header_size = reader.stream_position().expect("current position") as usize - start;
|
2023-04-08 22:16:41 +00:00
|
|
|
if (data_size as usize) < header_size {
|
2023-04-22 19:52:23 +00:00
|
|
|
return Err(Error::ShortAudioBlock);
|
2023-04-08 22:16:41 +00:00
|
|
|
}
|
2023-04-08 21:52:33 +00:00
|
|
|
let data = reader.read(data_size as usize - header_size)?;
|
|
|
|
|
2023-04-08 22:16:41 +00:00
|
|
|
let data = match format {
|
|
|
|
SoundFormat::Aac => {
|
2023-04-22 19:52:23 +00:00
|
|
|
let aac_packet_type = data.first().ok_or(Error::ShortAudioBlock)?;
|
2023-04-08 22:16:41 +00:00
|
|
|
match aac_packet_type {
|
|
|
|
//TODO: The FLV spec says this is explained in ISO 14496-3.
|
|
|
|
0 => AudioDataType::AacSequenceHeader(&data[1..]),
|
|
|
|
1 => AudioDataType::AacRaw(&data[1..]),
|
2023-04-22 19:52:23 +00:00
|
|
|
unk => return Err(Error::UnknownAacPacketType(*unk)),
|
2023-04-08 22:16:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => AudioDataType::Raw(data),
|
|
|
|
};
|
|
|
|
|
2023-04-22 19:52:23 +00:00
|
|
|
Ok(AudioData {
|
2023-04-08 21:52:33 +00:00
|
|
|
format,
|
|
|
|
rate,
|
|
|
|
size,
|
|
|
|
sound_type,
|
|
|
|
data,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-04-22 19:52:23 +00:00
|
|
|
use crate::error::Error;
|
2023-04-08 21:52:33 +00:00
|
|
|
use crate::reader::FlvReader;
|
2023-04-08 22:16:41 +00:00
|
|
|
use crate::sound::{AudioData, AudioDataType, SoundFormat, SoundRate, SoundSize, SoundType};
|
2023-04-08 21:52:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn read_audiodata() {
|
2023-04-22 18:31:53 +00:00
|
|
|
let data = [0xBF, 0x12, 0x34, 0x56, 0x78];
|
2023-04-08 21:52:33 +00:00
|
|
|
let mut reader = FlvReader::from_source(&data);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
AudioData::parse(&mut reader, data.len() as u32),
|
2023-04-22 19:52:23 +00:00
|
|
|
Ok(AudioData {
|
2023-04-08 22:16:41 +00:00
|
|
|
format: SoundFormat::Speex,
|
2023-04-08 21:52:33 +00:00
|
|
|
rate: SoundRate::R44_000,
|
|
|
|
size: SoundSize::Bits16,
|
|
|
|
sound_type: SoundType::Stereo,
|
2023-04-08 22:16:41 +00:00
|
|
|
data: AudioDataType::Raw(&[0x12, 0x34, 0x56, 0x78])
|
2023-04-08 21:52:33 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn read_audiodata_invalid_len() {
|
2023-04-22 18:31:53 +00:00
|
|
|
let data = [0xBF, 0x12, 0x34, 0x56, 0x78];
|
2023-04-08 21:52:33 +00:00
|
|
|
let mut reader = FlvReader::from_source(&data);
|
|
|
|
|
2023-04-22 19:52:23 +00:00
|
|
|
assert_eq!(
|
|
|
|
AudioData::parse(&mut reader, 0),
|
|
|
|
Err(Error::ShortAudioBlock)
|
|
|
|
);
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn read_audiodata_short_len() {
|
2023-04-22 18:31:53 +00:00
|
|
|
let data = [0xBF, 0x12, 0x34, 0x56, 0x78];
|
2023-04-08 21:52:33 +00:00
|
|
|
let mut reader = FlvReader::from_source(&data);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
AudioData::parse(&mut reader, 2),
|
2023-04-22 19:52:23 +00:00
|
|
|
Ok(AudioData {
|
2023-04-08 22:16:41 +00:00
|
|
|
format: SoundFormat::Speex,
|
|
|
|
rate: SoundRate::R44_000,
|
|
|
|
size: SoundSize::Bits16,
|
|
|
|
sound_type: SoundType::Stereo,
|
|
|
|
data: AudioDataType::Raw(&[0x12])
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn read_audiodata_aac() {
|
2023-04-22 18:31:53 +00:00
|
|
|
let data = [0xAD, 0x01, 0x12, 0x34, 0x56, 0x78];
|
2023-04-08 22:16:41 +00:00
|
|
|
let mut reader = FlvReader::from_source(&data);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
AudioData::parse(&mut reader, data.len() as u32),
|
2023-04-22 19:52:23 +00:00
|
|
|
Ok(AudioData {
|
2023-04-08 21:52:33 +00:00
|
|
|
format: SoundFormat::Aac,
|
|
|
|
rate: SoundRate::R44_000,
|
2023-04-22 18:31:53 +00:00
|
|
|
size: SoundSize::Bits8,
|
2023-04-08 21:52:33 +00:00
|
|
|
sound_type: SoundType::Stereo,
|
2023-04-08 22:16:41 +00:00
|
|
|
data: AudioDataType::AacRaw(&[0x12, 0x34, 0x56, 0x78])
|
2023-04-08 21:52:33 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
2023-04-08 22:16:41 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn read_audiodata_aac_invalid() {
|
2023-04-22 18:31:53 +00:00
|
|
|
let data = [0xAD, 0x02, 0x12, 0x34, 0x56, 0x78];
|
2023-04-08 22:16:41 +00:00
|
|
|
let mut reader = FlvReader::from_source(&data);
|
|
|
|
|
2023-04-22 19:52:23 +00:00
|
|
|
assert_eq!(
|
|
|
|
AudioData::parse(&mut reader, data.len() as u32),
|
|
|
|
Err(Error::UnknownAacPacketType(2))
|
|
|
|
);
|
2023-04-08 22:16:41 +00:00
|
|
|
}
|
2023-04-08 21:52:33 +00:00
|
|
|
}
|