use alloc::vec::Vec;
use crate::Error;
use crate::rtmp_timestamp::{RtmpTimestamp, RtmpTimestampDelta};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MediaFrame {
Audio(AudioFrame),
Video(VideoFrame),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AudioFrame {
pub timestamp: RtmpTimestamp,
pub format: AudioFormat,
pub sample_rate: AudioSampleRate,
pub is_8bit_sample: bool,
pub is_stereo: bool,
pub is_aac_sequence_header: bool,
pub data: Vec<u8>,
}
impl AudioFrame {
pub const AAC_SAMPLE_RATE: AudioSampleRate = AudioSampleRate::Khz44;
pub const AAC_STEREO: bool = true;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VideoFrame {
pub timestamp: RtmpTimestamp,
pub composition_timestamp_offset: RtmpTimestampDelta,
pub frame_type: VideoFrameType,
pub codec: VideoCodec,
pub avc_packet_type: Option<AvcPacketType>,
pub data: Vec<u8>,
}
impl VideoFrame {
pub fn is_keyframe(&self) -> bool {
self.frame_type == VideoFrameType::KeyFrame
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AvcPacketType {
SequenceHeader = 0,
NalUnit = 1,
EndOfSequence = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum VideoFrameType {
KeyFrame = 1,
InterFrame = 2,
DisposableInterFrame = 3,
GeneratedKeyFrame = 4,
VideoInfoOrCommandFrame = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum VideoCodec {
Jpeg = 1,
H263 = 2,
ScreenVideo = 3,
Vp6 = 4,
Vp6WithAlpha = 5,
ScreenVideoV2 = 6,
Avc = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AudioFormat {
Adpcm = 1,
Mp3 = 2,
LinearPcmLittleEndian = 3,
Nellymoser16khzMono = 4,
Nellymoser8KhzMono = 5,
Nellymoser = 6,
G711AlawLogarithmicPcm = 7,
G711MuLawLogarithmicPcm = 8,
Aac = 10,
Speex = 11,
Mp3_8khz = 14,
DeviceSpecificSound = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AudioSampleRate {
Khz5 = 0,
Khz11 = 1,
Khz22 = 2,
Khz44 = 3,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcSequenceHeader {
pub avc_profile_indication: u8,
pub profile_compatibility: u8,
pub avc_level_indication: u8,
pub length_size_minus_one: u8,
pub sps_list: Vec<Vec<u8>>,
pub pps_list: Vec<Vec<u8>>,
}
impl AvcSequenceHeader {
const CONFIGURATION_VERSION: u8 = 1;
const MAX_SPS_COUNT: usize = 31;
const MAX_PPS_COUNT: usize = 255;
const MAX_SPS_SIZE: usize = 4096;
const MAX_PPS_SIZE: usize = 4096;
pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
if data.len() < 7 {
return Err(Error::invalid_data(
"AVCDecoderConfigurationRecord too short (expected at least 7 bytes)",
));
}
let configuration_version = data[0];
if configuration_version != Self::CONFIGURATION_VERSION {
return Err(Error::unsupported(format!(
"unsupported AVC configuration version: {} (expected {})",
configuration_version,
Self::CONFIGURATION_VERSION
)));
}
let avc_profile_indication = data[1];
let profile_compatibility = data[2];
let avc_level_indication = data[3];
let length_size_minus_one = data[4] & 0x03;
let mut offset = 5;
let mut sps_list = Vec::new();
let mut pps_list = Vec::new();
if offset >= data.len() {
return Err(Error::invalid_data("incomplete SPS count field (offset 5)"));
}
let num_sps = (data[offset] & 0x1F) as usize;
if num_sps > Self::MAX_SPS_COUNT {
return Err(Error::invalid_data(format!(
"SPS count exceeds maximum ({} > {})",
num_sps,
Self::MAX_SPS_COUNT
)));
}
offset += 1;
if num_sps == 0 {
return Err(Error::invalid_data("SPS list must not be empty"));
}
for i in 0..num_sps {
if offset + 2 > data.len() {
return Err(Error::invalid_data(format!(
"incomplete SPS length field at index {}: need 2 bytes, have {}",
i,
data.len() - offset
)));
}
let sps_length = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
if sps_length > Self::MAX_SPS_SIZE {
return Err(Error::invalid_data(format!(
"SPS size exceeds maximum at index {}: {} > {}",
i,
sps_length,
Self::MAX_SPS_SIZE
)));
}
if offset + sps_length > data.len() {
return Err(Error::invalid_data(format!(
"incomplete SPS data at index {}: need {} bytes, have {}",
i,
sps_length,
data.len() - offset
)));
}
sps_list.push(data[offset..offset + sps_length].to_vec());
offset += sps_length;
}
if offset >= data.len() {
return Err(Error::invalid_data("incomplete PPS count field"));
}
let num_pps = data[offset] as usize;
if num_pps > Self::MAX_PPS_COUNT {
return Err(Error::invalid_data(format!(
"PPS count exceeds maximum ({} > {})",
num_pps,
Self::MAX_PPS_COUNT
)));
}
offset += 1;
if num_pps == 0 {
return Err(Error::invalid_data("PPS list must not be empty"));
}
for i in 0..num_pps {
if offset + 2 > data.len() {
return Err(Error::invalid_data(format!(
"incomplete PPS length field at index {}: need 2 bytes, have {}",
i,
data.len() - offset
)));
}
let pps_length = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
if pps_length > Self::MAX_PPS_SIZE {
return Err(Error::invalid_data(format!(
"PPS size exceeds maximum at index {}: {} > {}",
i,
pps_length,
Self::MAX_PPS_SIZE
)));
}
if offset + pps_length > data.len() {
return Err(Error::invalid_data(format!(
"incomplete PPS data at index {}: need {} bytes, have {}",
i,
pps_length,
data.len() - offset
)));
}
pps_list.push(data[offset..offset + pps_length].to_vec());
offset += pps_length;
}
Ok(Self {
avc_profile_indication,
profile_compatibility,
avc_level_indication,
length_size_minus_one,
sps_list,
pps_list,
})
}
pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
if self.sps_list.is_empty() {
return Err(Error::invalid_data("SPS list must not be empty"));
}
if self.pps_list.is_empty() {
return Err(Error::invalid_data("PPS list must not be empty"));
}
if self.sps_list.len() > Self::MAX_SPS_COUNT {
return Err(Error::invalid_data(format!(
"too many SPS entries: {} (max {})",
self.sps_list.len(),
Self::MAX_SPS_COUNT
)));
}
if self.pps_list.len() > Self::MAX_PPS_COUNT {
return Err(Error::invalid_data(format!(
"too many PPS entries: {} (max {})",
self.pps_list.len(),
Self::MAX_PPS_COUNT
)));
}
for (i, sps) in self.sps_list.iter().enumerate() {
if sps.len() > Self::MAX_SPS_SIZE {
return Err(Error::invalid_data(format!(
"SPS size exceeds maximum at index {}: {} > {}",
i,
sps.len(),
Self::MAX_SPS_SIZE
)));
}
}
for (i, pps) in self.pps_list.iter().enumerate() {
if pps.len() > Self::MAX_PPS_SIZE {
return Err(Error::invalid_data(format!(
"PPS size exceeds maximum at index {}: {} > {}",
i,
pps.len(),
Self::MAX_PPS_SIZE
)));
}
}
let mut result = vec![
Self::CONFIGURATION_VERSION,
self.avc_profile_indication,
self.profile_compatibility,
self.avc_level_indication,
0xFC | (self.length_size_minus_one & 0x03),
];
result.push(0xE0 | (self.sps_list.len() as u8));
for sps in &self.sps_list {
result.extend_from_slice(&(sps.len() as u16).to_be_bytes());
result.extend_from_slice(sps);
}
result.push(self.pps_list.len() as u8);
for pps in &self.pps_list {
result.extend_from_slice(&(pps.len() as u16).to_be_bytes());
result.extend_from_slice(pps);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_avc_sequence_header_roundtrip() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![vec![0x01, 0x02, 0x03]],
pps_list: vec![vec![0x04, 0x05]],
};
let bytes = header.to_bytes().expect("to_bytes failed");
let parsed = AvcSequenceHeader::from_bytes(&bytes).expect("from_bytes failed");
assert_eq!(header, parsed);
}
#[test]
fn test_avc_sequence_header_too_short() {
let short_data = vec![0x01, 0x02, 0x03];
assert!(AvcSequenceHeader::from_bytes(&short_data).is_err());
}
#[test]
fn test_avc_sequence_header_invalid_version() {
let data = vec![0x02, 0x42, 0xC0, 0x1F, 0xFC, 0xE0, 0x00];
let result = AvcSequenceHeader::from_bytes(&data);
assert!(result.is_err());
}
#[test]
fn test_avc_sequence_header_multiple_sps_pps() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![vec![1, 2, 3], vec![4, 5, 6]],
pps_list: vec![vec![7, 8], vec![9, 10]],
};
let bytes = header.to_bytes().expect("to_bytes failed");
let parsed = AvcSequenceHeader::from_bytes(&bytes).expect("from_bytes failed");
assert_eq!(header, parsed);
}
#[test]
fn test_avc_sequence_header_empty_sps() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![],
pps_list: vec![vec![0x04, 0x05]],
};
assert!(header.to_bytes().is_err());
}
#[test]
fn test_avc_sequence_header_empty_pps() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![vec![0x01, 0x02, 0x03]],
pps_list: vec![],
};
assert!(header.to_bytes().is_err());
}
#[test]
fn test_avc_sequence_header_sps_too_large() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![vec![0; 5000]], pps_list: vec![vec![0x04, 0x05]],
};
assert!(header.to_bytes().is_err());
}
#[test]
fn test_avc_sequence_header_pps_too_large() {
let header = AvcSequenceHeader {
avc_profile_indication: 0x42,
profile_compatibility: 0xC0,
avc_level_indication: 0x1F,
length_size_minus_one: 3,
sps_list: vec![vec![0x01, 0x02, 0x03]],
pps_list: vec![vec![0; 5000]], };
assert!(header.to_bytes().is_err());
}
#[test]
fn test_avc_sequence_header_empty_sps_from_bytes() {
let data = vec![0x01, 0x42, 0xC0, 0x1F, 0xFC, 0xE0, 0x00];
assert!(AvcSequenceHeader::from_bytes(&data).is_err());
}
#[test]
fn test_avc_sequence_header_empty_pps_from_bytes() {
let mut data = vec![0x01, 0x42, 0xC0, 0x1F, 0xFC, 0xE1]; data.extend_from_slice(&[0x00, 0x01]); data.extend_from_slice(&[0xFF]); data.push(0x00);
assert!(AvcSequenceHeader::from_bytes(&data).is_err());
}
#[test]
fn test_avc_sequence_header_sps_too_large_from_bytes() {
let mut data = vec![0x01, 0x42, 0xC0, 0x1F, 0xFC, 0xE1]; data.extend_from_slice(&[0x10, 0x01]);
assert!(AvcSequenceHeader::from_bytes(&data).is_err());
}
#[test]
fn test_avc_sequence_header_pps_too_large_from_bytes() {
let mut data = vec![0x01, 0x42, 0xC0, 0x1F, 0xFC, 0xE1]; data.extend_from_slice(&[0x00, 0x01]); data.extend_from_slice(&[0xFF]); data.extend_from_slice(&[0x01]); data.extend_from_slice(&[0x10, 0x01]);
assert!(AvcSequenceHeader::from_bytes(&data).is_err());
}
}