use super::Error;
type Result<T> = std::result::Result<T, Error>;
pub type Sps<'buf> = &'buf [u8];
pub type Pps<'buf> = Vec<&'buf [u8]>;
pub fn extract_parameter_sets_h264<'buf>(
extradata_bytes: &'buf [u8],
) -> Result<(Sps<'buf>, Pps<'buf>)> {
if extradata_bytes.len() > 0 {
match extradata_bytes[0] {
0x00 => extract_parameter_sets_from_extradata_h264_avc_annexb(extradata_bytes),
0x01 => extract_parameter_sets_from_extradata_h264_avcc(extradata_bytes),
_ => Err(Error::InvalidExtraData),
}
} else {
Err(Error::InvalidExtraData)
}
}
fn extract_parameter_sets_from_extradata_h264_avcc<'buf>(
bytes: &'buf [u8],
) -> Result<(Sps<'buf>, Pps<'buf>)> {
if bytes.len() > 8 {
let mut ppss = Vec::new();
let sps_size = u16::from_be_bytes([bytes[6], bytes[7]]);
let sps = &bytes[8_usize..(8 + sps_size) as usize];
let pps_array = &bytes[(8 + sps_size) as usize..];
if pps_array.len() > 1 {
let pps_num = pps_array[0];
let pps_array = &pps_array[1..];
let mut pps_p = 0;
for _ in 0..pps_num {
if pps_array[pps_p..].len() < 2 {
return Err(Error::InvalidExtraData);
}
let pps_size = u16::from_be_bytes([
pps_array[pps_p],
pps_array[pps_p + 1]]);
if pps_array[pps_p + 2..].len() < pps_size as usize {
return Err(Error::InvalidExtraData);
}
let pps = &pps_array[pps_p + 2..pps_p + 2 + pps_size as usize];
ppss.push(pps);
pps_p += 2 + pps_size as usize;
}
Ok((sps, ppss))
} else {
Err(Error::InvalidExtraData)
}
} else {
Err(Error::InvalidExtraData)
}
}
fn extract_parameter_sets_from_extradata_h264_avc_annexb<'buf>(
bytes: &'buf [u8],
) -> Result<(Sps<'buf>, Pps<'buf>)> {
let mut index_current = find_avc_start_code(bytes, 0)
.map(|(_, index_next)| index_next);
let mut sps: Option<Sps<'buf>> = None;
let mut pps: Pps<'buf> = Vec::new();
while let Some(index) = index_current {
let (end, index_next) = match find_avc_start_code(bytes, index) {
Some((end, index_next))
=> (end, Some(index_next)),
None
=> (bytes.len(), None)
};
let nal = &bytes[index..end];
let nal_type = nal[0] & 0x1f;
match nal_type {
0x07 => sps = Some(nal),
0x08 => pps.push(nal),
_ => {}
};
index_current = index_next;
}
if let Some(sps) = sps {
Ok((sps, pps))
} else {
Err(Error::InvalidExtraData)
}
}
fn find_avc_start_code(
bytes: &[u8],
offset: usize,
) -> Option<(usize, usize)> {
let part = &bytes[offset..];
if part.len() >= 3 {
for i in 0..(part.len() - 3) {
if part[i..i + 3] == [0x00, 0x00, 0x01] {
return Some((offset + i, offset + i + 3))
} else if i + 4 <= part.len() {
if part[i..i + 4] == [0x00, 0x00, 0x00, 0x01] {
return Some((offset + i, offset + i + 4))
}
}
}
None
} else {
None
}
}