#![forbid(unsafe_code)]
pub(super) fn build_av1c_from_extradata(data: &[u8]) -> Option<Vec<u8>> {
if data.len() < 2 {
return None;
}
if data.len() >= 4 && data[0] == 0x81 {
return Some(data[..4].to_vec());
}
parse_av1c_from_obus(data)
}
fn parse_av1c_from_obus(data: &[u8]) -> Option<Vec<u8>> {
let mut pos = 0usize;
while pos < data.len() {
let obu_header = data[pos];
pos += 1;
let obu_type = (obu_header >> 3) & 0x0F;
let has_size_field = (obu_header & 0x02) != 0;
let extension_flag = (obu_header & 0x04) != 0;
if extension_flag {
if pos >= data.len() {
break;
}
pos += 1; }
let obu_payload_size = if has_size_field {
let (sz, consumed) = read_leb128(&data[pos..])?;
pos += consumed;
sz
} else {
data.len() - pos
};
let payload_end = pos + obu_payload_size;
if payload_end > data.len() {
break;
}
if obu_type == 1 && obu_payload_size >= 1 {
let sh_data = &data[pos..payload_end];
return extract_av1c_from_seq_header(sh_data);
}
pos = payload_end;
}
None
}
fn read_leb128(buf: &[u8]) -> Option<(usize, usize)> {
let mut value: usize = 0;
let mut shift = 0usize;
for (i, &byte) in buf.iter().enumerate() {
value |= ((byte & 0x7F) as usize) << shift;
shift += 7;
if (byte & 0x80) == 0 {
return Some((value, i + 1));
}
if shift >= 35 {
return None; }
}
None
}
#[allow(clippy::too_many_lines)]
fn extract_av1c_from_seq_header(data: &[u8]) -> Option<Vec<u8>> {
if data.is_empty() {
return None;
}
let mut bit_pos = 0usize;
let read_bit = |bp: usize| -> Option<u8> {
let byte_idx = bp / 8;
let bit_idx = 7 - (bp % 8);
data.get(byte_idx).map(|b| (b >> bit_idx) & 1)
};
let read_bits = |start: usize, count: usize| -> Option<u8> {
let mut val = 0u8;
for i in 0..count {
let b = read_bit(start + i)?;
val = (val << 1) | b;
}
Some(val)
};
let seq_profile = read_bits(bit_pos, 3)?;
bit_pos += 3;
let still_picture = read_bit(bit_pos)?;
bit_pos += 1;
let reduced = read_bit(bit_pos)?;
bit_pos += 1;
let (seq_level_idx_0, seq_tier_0);
if reduced == 1 {
seq_level_idx_0 = read_bits(bit_pos, 5)?;
bit_pos += 5;
seq_tier_0 = 0u8;
} else {
let timing_present = read_bit(bit_pos)?;
bit_pos += 1;
if timing_present == 1 {
bit_pos += 64; let decoder_model_present = read_bit(bit_pos)?;
bit_pos += 1;
if decoder_model_present == 1 {
bit_pos += 47; }
}
bit_pos += 1;
let op_count = read_bits(bit_pos, 5)? as usize + 1;
bit_pos += 5;
let mut first_level = 0u8;
let mut first_tier = 0u8;
for op_i in 0..op_count {
bit_pos += 12; let level = read_bits(bit_pos, 5)?;
bit_pos += 5;
let tier = if level > 7 {
let t = read_bit(bit_pos)?;
bit_pos += 1;
t
} else {
0
};
if op_i == 0 {
first_level = level;
first_tier = tier;
}
}
seq_level_idx_0 = first_level;
seq_tier_0 = first_tier;
}
let fw_bits = read_bits(bit_pos, 4)? as usize + 1;
bit_pos += 4;
let fh_bits = read_bits(bit_pos, 4)? as usize + 1;
bit_pos += 4;
bit_pos += fw_bits + fh_bits;
if reduced == 0 {
let fid = read_bit(bit_pos)?;
bit_pos += 1;
if fid == 1 {
bit_pos += 7; }
bit_pos += 7; let order_hint = read_bit(bit_pos)?;
bit_pos += 1;
if order_hint == 1 {
bit_pos += 2; }
let sc = read_bit(bit_pos)?;
bit_pos += 1;
if sc == 0 {
bit_pos += 1;
}
let siv = read_bit(bit_pos)?;
bit_pos += 1;
if siv == 0 {
bit_pos += 1;
}
if order_hint == 1 {
bit_pos += 3; }
bit_pos += 3; }
let high_bitdepth = read_bit(bit_pos)?;
bit_pos += 1;
let twelve_bit = if seq_profile == 2 && high_bitdepth == 1 {
let tb = read_bit(bit_pos)?;
bit_pos += 1;
tb
} else {
0
};
let monochrome = if seq_profile == 1 || (still_picture == 1 && reduced == 0) {
0u8
} else {
let m = read_bit(bit_pos)?;
bit_pos += 1;
m
};
let color_description_present = read_bit(bit_pos)?;
bit_pos += 1;
if color_description_present == 1 {
bit_pos += 24; }
let (subsampling_x, subsampling_y, chroma_sample_pos);
if monochrome == 1 {
bit_pos += 1; subsampling_x = 1u8;
subsampling_y = 1u8;
chroma_sample_pos = 0u8;
} else if seq_profile == 0 {
subsampling_x = 1u8;
subsampling_y = 1u8;
bit_pos += 1; chroma_sample_pos = {
let csp = read_bits(bit_pos, 2).unwrap_or(0);
bit_pos += 2;
csp
};
} else if seq_profile == 1 {
subsampling_x = 0u8;
subsampling_y = 0u8;
bit_pos += 1; chroma_sample_pos = 0;
} else {
bit_pos += 1; let bit_depth_val: u8 = if seq_profile == 2 && twelve_bit == 1 {
12
} else if high_bitdepth == 1 {
10
} else {
8
};
if bit_depth_val == 12 {
subsampling_x = read_bit(bit_pos).unwrap_or(1);
bit_pos += 1;
subsampling_y = if subsampling_x == 1 {
let sy = read_bit(bit_pos).unwrap_or(1);
bit_pos += 1;
sy
} else {
0
};
} else {
subsampling_x = 1u8;
subsampling_y = 0u8;
}
chroma_sample_pos = if subsampling_x == 1 && subsampling_y == 1 {
let csp = read_bits(bit_pos, 2).unwrap_or(0);
bit_pos += 2;
csp
} else {
0
};
}
let _ = bit_pos;
let byte0: u8 = 0x81; let byte1: u8 = ((seq_profile & 0x07) << 5) | (seq_level_idx_0 & 0x1F);
let byte2: u8 = (seq_tier_0 << 7)
| (high_bitdepth << 6)
| (twelve_bit << 5)
| (monochrome << 4)
| (subsampling_x << 3)
| (subsampling_y << 2)
| (chroma_sample_pos & 0x03);
let byte3: u8 = 0x00;
Some(vec![byte0, byte1, byte2, byte3])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prebuilt_av1c_passthrough() {
let input = vec![0x81u8, 0x08, 0x0C, 0x00];
let result = build_av1c_from_extradata(&input).expect("should succeed");
assert_eq!(result[0], 0x81);
assert_eq!(result, input);
}
#[test]
fn test_empty_returns_none() {
assert!(build_av1c_from_extradata(&[]).is_none());
assert!(build_av1c_from_extradata(&[0x00]).is_none());
}
#[test]
fn test_short_data_returns_none() {
assert!(build_av1c_from_extradata(&[0x00, 0x00]).is_none());
}
#[test]
fn test_leb128_single_byte() {
assert_eq!(read_leb128(&[0x05]), Some((5, 1)));
assert_eq!(read_leb128(&[0x00]), Some((0, 1)));
assert_eq!(read_leb128(&[0x7F]), Some((127, 1)));
}
#[test]
fn test_leb128_multi_byte() {
assert_eq!(read_leb128(&[0x80, 0x01]), Some((128, 2)));
assert_eq!(read_leb128(&[0xAC, 0x02]), Some((300, 2)));
}
#[test]
fn test_leb128_empty_returns_none() {
assert!(read_leb128(&[]).is_none());
}
}