mod riff;
mod opendml;
mod streaming;
#[cfg(test)]
mod tests;
pub use streaming::AviStreamingDemuxer;
pub(crate) use streaming::demux_avi_streaming_init;
use anyhow::{Context, Result, bail};
use codec::frame::{ColorSpace, PixelFormat, StreamInfo};
use crate::demux::DemuxResult;
use opendml::read_dmlh_total_frames;
use riff::{ascii, collect_movi_samples, find_video_stream, fourcc_to_codec,
scan_top_level_records};
pub(crate) fn demux_avi(data: &[u8]) -> Result<DemuxResult> {
if data.len() < 12 || &data[..4] != b"RIFF" || &data[8..12] != b"AVI " {
bail!("not a RIFF/AVI file");
}
let mut hdrl: Option<(usize, usize)> = None;
let mut movi_lists: Vec<(usize, usize)> = Vec::new();
scan_top_level_records(data, &mut hdrl, &mut movi_lists);
let (hdrl_start, hdrl_end) = hdrl.context("AVI: missing hdrl LIST")?;
if movi_lists.is_empty() {
bail!("AVI: missing movi LIST");
}
let video = find_video_stream(&data[hdrl_start..hdrl_end])
.context("AVI: no video stream found in hdrl")?;
let codec = fourcc_to_codec(&video.handler)
.or_else(|| fourcc_to_codec(&video.compression))
.with_context(|| {
format!(
"AVI: unsupported video fourcc {:?}/{:?}",
ascii(&video.handler),
ascii(&video.compression)
)
})?;
let stream_idx = video.stream_index;
let prefix = format!("{:02}", stream_idx);
let mut samples: Vec<Vec<u8>> = Vec::new();
for &(movi_start, movi_end) in &movi_lists {
collect_movi_samples(&data[movi_start..movi_end], &prefix, &mut samples)?;
}
if samples.is_empty() {
bail!(
"AVI: movi LIST contained no video samples for stream {:02}",
stream_idx
);
}
let total_frames =
read_dmlh_total_frames(&data[hdrl_start..hdrl_end]).unwrap_or(samples.len() as u64);
let duration = if video.frame_rate > 0.0 {
total_frames as f64 / video.frame_rate
} else {
0.0
};
let info = StreamInfo {
codec: codec.clone(),
width: video.width,
height: video.height,
frame_rate: video.frame_rate,
duration,
pixel_format: PixelFormat::Yuv420p,
color_space: ColorSpace::Bt709,
color_metadata: Default::default(),
total_frames,
bitrate: 0,
};
let detected_pf = codec::pixel_format::detect(&codec, &samples);
let info = StreamInfo {
pixel_format: detected_pf,
..info
};
Ok(DemuxResult {
codec,
info,
samples,
audio: None,
})
}