use crate::encoder::ParameterSets;
use crate::mux::build_avcc;
const FRAME_KEY: u8 = 1;
const FRAME_INTER: u8 = 2;
const CODEC_AVC: u8 = 7;
const AVC_SEQUENCE_HEADER: u8 = 0;
const AVC_NALU: u8 = 1;
pub fn avc_sequence_header(params: &ParameterSets) -> Vec<u8> {
let avcc = build_avcc(¶ms.sps, ¶ms.pps);
let mut body = Vec::with_capacity(5 + avcc.len());
body.push((FRAME_KEY << 4) | CODEC_AVC);
body.push(AVC_SEQUENCE_HEADER);
body.extend_from_slice(&[0, 0, 0]); body.extend_from_slice(&avcc);
body
}
pub fn avc_frame(annex_b: &[u8], is_keyframe: bool) -> Vec<u8> {
let avcc = annex_b_to_avcc(annex_b);
let frame_type = if is_keyframe { FRAME_KEY } else { FRAME_INTER };
let mut body = Vec::with_capacity(5 + avcc.len());
body.push((frame_type << 4) | CODEC_AVC);
body.push(AVC_NALU);
body.extend_from_slice(&[0, 0, 0]); body.extend_from_slice(&avcc);
body
}
pub fn annex_b_to_avcc(annex_b: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(annex_b.len() + 8);
for nal in iter_nals(annex_b) {
if nal.is_empty() {
continue;
}
let nal_type = nal[0] & 0x1f;
if matches!(nal_type, 7 | 8 | 9) {
continue;
}
out.extend_from_slice(&(nal.len() as u32).to_be_bytes());
out.extend_from_slice(nal);
}
out
}
fn iter_nals(data: &[u8]) -> impl Iterator<Item = &[u8]> {
let mut starts = Vec::new();
let mut i = 0;
while i + 3 <= data.len() {
if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
starts.push((i + 3, 3));
i += 3;
} else if i + 4 <= data.len()
&& data[i] == 0
&& data[i + 1] == 0
&& data[i + 2] == 0
&& data[i + 3] == 1
{
starts.push((i + 4, 4));
i += 4;
} else {
i += 1;
}
}
let mut ranges = Vec::with_capacity(starts.len());
for k in 0..starts.len() {
let body = starts[k].0;
let end = if k + 1 < starts.len() {
starts[k + 1].0 - starts[k + 1].1
} else {
data.len()
};
ranges.push((body, end));
}
ranges.into_iter().map(move |(s, e)| &data[s..e])
}
const AAC_AUDIO_HEADER: u8 = 0xAF;
const AAC_SEQUENCE_HEADER: u8 = 0;
const AAC_RAW: u8 = 1;
pub fn aac_sequence_header(asc: &[u8]) -> Vec<u8> {
let mut body = Vec::with_capacity(2 + asc.len());
body.push(AAC_AUDIO_HEADER);
body.push(AAC_SEQUENCE_HEADER);
body.extend_from_slice(asc);
body
}
pub fn aac_frame(aac: &[u8]) -> Vec<u8> {
let mut body = Vec::with_capacity(2 + aac.len());
body.push(AAC_AUDIO_HEADER);
body.push(AAC_RAW);
body.extend_from_slice(aac);
body
}
pub fn duration_to_flv_ms(d: std::time::Duration) -> u32 {
(d.as_millis() as u64 & 0xffff_ffff) as u32
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn annex_b_converts_to_length_prefixed_and_drops_param_sets() {
let stream = [
0, 0, 0, 1, 0x67, 0xAA, 0, 0, 1, 0x68, 0xBB, 0, 0, 0, 1, 0x65, 0x11, 0x22, ];
let avcc = annex_b_to_avcc(&stream);
assert_eq!(avcc, vec![0, 0, 0, 3, 0x65, 0x11, 0x22]);
}
#[test]
fn keyframe_tag_header_nibbles() {
let body = avc_frame(&[0, 0, 0, 1, 0x65, 0x01], true);
assert_eq!(body[0], (FRAME_KEY << 4) | CODEC_AVC); assert_eq!(body[1], AVC_NALU);
}
#[test]
fn interframe_tag_header_nibbles() {
let body = avc_frame(&[0, 0, 0, 1, 0x41, 0x01], false);
assert_eq!(body[0], (FRAME_INTER << 4) | CODEC_AVC); }
#[test]
fn sequence_header_has_avc_seqhdr_type() {
let params = ParameterSets {
sps: vec![0x67, 0x64, 0x00, 0x1f],
pps: vec![0x68, 0xCC],
};
let body = avc_sequence_header(¶ms);
assert_eq!(body[0], (FRAME_KEY << 4) | CODEC_AVC);
assert_eq!(body[1], AVC_SEQUENCE_HEADER);
}
#[test]
fn aac_seqhdr_and_frame_headers() {
let seq = aac_sequence_header(&[0x11, 0x90]);
assert_eq!(seq[0], 0xAF);
assert_eq!(seq[1], 0); assert_eq!(&seq[2..], &[0x11, 0x90]);
let frame = aac_frame(&[0xDE, 0xAD]);
assert_eq!(frame[0], 0xAF);
assert_eq!(frame[1], 1); assert_eq!(&frame[2..], &[0xDE, 0xAD]);
}
#[test]
fn flv_ms_truncates_and_wraps() {
assert_eq!(duration_to_flv_ms(Duration::from_millis(0)), 0);
assert_eq!(duration_to_flv_ms(Duration::from_micros(1500)), 1); assert_eq!(duration_to_flv_ms(Duration::from_millis(2500)), 2500);
}
}