use codec::frame::ColorMetadata;
use super::boxes::{BoxBuilder, write_unity_matrix, parse_seq_header_params};
use super::sample_table::{build_stsc, build_stsz, build_stco, build_co64};
pub(super) fn build_video_trak(
width: u32,
height: u32,
mdhd_timescale: u32,
duration_in_movie_ts: u64,
duration_in_mdhd_ts: u64,
frame_duration: u32,
sample_sizes: &[u32],
keyframe_indices: &[u32],
config_obus: &[u8],
chunk_offsets: &[u64],
samples_per_chunk: u32,
use_co64: bool,
color_metadata: &ColorMetadata,
) -> Vec<u8> {
let tkhd = build_video_tkhd(width, height, duration_in_movie_ts);
let mdia = build_video_mdia(
width,
height,
mdhd_timescale,
duration_in_mdhd_ts,
frame_duration,
sample_sizes,
keyframe_indices,
config_obus,
chunk_offsets,
samples_per_chunk,
use_co64,
color_metadata,
);
let mut b = BoxBuilder::new(b"trak");
b.extend(&tkhd);
b.extend(&mdia);
b.finish()
}
fn build_video_tkhd(width: u32, height: u32, duration: u64) -> Vec<u8> {
let mut b = BoxBuilder::new(b"tkhd");
b.u8(0); b.extend(&[0, 0, 0x03]); b.u32(0); b.u32(0); b.u32(1); b.u32(0); b.u32(duration as u32);
b.u32(0); b.u32(0);
b.u16(0); b.u16(0); b.u16(0); b.u16(0); write_unity_matrix(&mut b);
b.u32(width << 16); b.u32(height << 16);
b.finish()
}
fn build_video_mdia(
width: u32,
height: u32,
timescale: u32,
duration: u64,
frame_duration: u32,
sample_sizes: &[u32],
keyframe_indices: &[u32],
config_obus: &[u8],
chunk_offsets: &[u64],
samples_per_chunk: u32,
use_co64: bool,
color_metadata: &ColorMetadata,
) -> Vec<u8> {
let mdhd = build_mdhd(timescale, duration);
let hdlr = build_video_hdlr();
let minf = build_minf(
width,
height,
frame_duration,
sample_sizes,
keyframe_indices,
config_obus,
chunk_offsets,
samples_per_chunk,
use_co64,
color_metadata,
);
let mut b = BoxBuilder::new(b"mdia");
b.extend(&mdhd);
b.extend(&hdlr);
b.extend(&minf);
b.finish()
}
pub(super) fn build_mdhd(timescale: u32, duration: u64) -> Vec<u8> {
let mut b = BoxBuilder::new(b"mdhd");
b.u8(0); b.extend(&[0, 0, 0]); b.u32(0); b.u32(0); b.u32(timescale);
b.u32(duration as u32);
b.u16(0x55c4); b.u16(0); b.finish()
}
fn build_video_hdlr() -> Vec<u8> {
let mut b = BoxBuilder::new(b"hdlr");
b.u8(0); b.extend(&[0, 0, 0]); b.u32(0); b.extend(b"vide"); b.u32(0); b.u32(0); b.u32(0); b.extend(b"VideoHandler\0");
b.finish()
}
pub(super) fn build_dinf() -> Vec<u8> {
let mut dref = BoxBuilder::new(b"dref");
dref.u8(0);
dref.extend(&[0, 0, 0]);
dref.u32(1); let mut url = BoxBuilder::new(b"url ");
url.u8(0);
url.extend(&[0, 0, 0x01]); dref.extend(&url.finish());
let mut b = BoxBuilder::new(b"dinf");
b.extend(&dref.finish());
b.finish()
}
fn build_minf(
width: u32,
height: u32,
frame_duration: u32,
sample_sizes: &[u32],
keyframe_indices: &[u32],
config_obus: &[u8],
chunk_offsets: &[u64],
samples_per_chunk: u32,
use_co64: bool,
color_metadata: &ColorMetadata,
) -> Vec<u8> {
let vmhd = build_vmhd();
let dinf = build_dinf();
let stbl = build_stbl(
width,
height,
frame_duration,
sample_sizes,
keyframe_indices,
config_obus,
chunk_offsets,
samples_per_chunk,
use_co64,
color_metadata,
);
let mut b = BoxBuilder::new(b"minf");
b.extend(&vmhd);
b.extend(&dinf);
b.extend(&stbl);
b.finish()
}
fn build_vmhd() -> Vec<u8> {
let mut b = BoxBuilder::new(b"vmhd");
b.u8(0);
b.extend(&[0, 0, 0x01]); b.u16(0); b.u16(0);
b.u16(0);
b.u16(0); b.finish()
}
fn build_stbl(
width: u32,
height: u32,
frame_duration: u32,
sample_sizes: &[u32],
keyframe_indices: &[u32],
config_obus: &[u8],
chunk_offsets: &[u64],
samples_per_chunk: u32,
use_co64: bool,
color_metadata: &ColorMetadata,
) -> Vec<u8> {
let stsd = build_stsd(width, height, config_obus, color_metadata);
let stts = build_stts(sample_sizes.len() as u32, frame_duration);
let stsc = build_stsc(sample_sizes.len() as u32, samples_per_chunk);
let stsz = build_stsz(sample_sizes);
let chunk_offset_box = if use_co64 {
build_co64(chunk_offsets)
} else {
build_stco(chunk_offsets)
};
let stss_box = if !keyframe_indices.is_empty() && keyframe_indices.len() < sample_sizes.len() {
Some(build_stss(keyframe_indices))
} else {
None
};
let mut b = BoxBuilder::new(b"stbl");
b.extend(&stsd);
b.extend(&stts);
if let Some(ss) = &stss_box {
b.extend(ss);
}
b.extend(&stsc);
b.extend(&stsz);
b.extend(&chunk_offset_box);
b.finish()
}
fn build_stsd(
_width: u32,
_height: u32,
video_sample_entry: &[u8],
_color_metadata: &ColorMetadata,
) -> Vec<u8> {
let mut b = BoxBuilder::new(b"stsd");
b.u8(0);
b.extend(&[0, 0, 0]); b.u32(1); b.extend(video_sample_entry);
b.finish()
}
fn build_stts(sample_count: u32, frame_duration: u32) -> Vec<u8> {
let mut b = BoxBuilder::new(b"stts");
b.u8(0);
b.extend(&[0, 0, 0]);
b.u32(1); b.u32(sample_count);
b.u32(frame_duration);
b.finish()
}
fn build_stss(keyframes: &[u32]) -> Vec<u8> {
let mut b = BoxBuilder::new(b"stss");
b.u8(0);
b.extend(&[0, 0, 0]);
b.u32(keyframes.len() as u32);
for &k in keyframes {
b.u32(k);
}
b.finish()
}
pub(crate) fn build_av01(
width: u32,
height: u32,
config_obus: &[u8],
color_metadata: &ColorMetadata,
) -> Vec<u8> {
let av1c = build_av1c(config_obus);
let colr = build_colr_nclx(color_metadata);
let mdcv = color_metadata.mastering_display.as_ref().map(build_mdcv);
let clli = color_metadata.content_light_level.as_ref().map(build_clli);
let mut b = BoxBuilder::new(b"av01");
for _ in 0..6 {
b.u8(0);
} b.u16(1); b.u16(0); b.u16(0); for _ in 0..3 {
b.u32(0);
} b.u16(width as u16);
b.u16(height as u16);
b.u32(0x00480000); b.u32(0x00480000); b.u32(0); b.u16(1); b.u8(0);
for _ in 0..31 {
b.u8(0);
}
b.u16(0x0018); b.u16(0xFFFF); b.extend(&av1c);
b.extend(&colr);
if let Some(mdcv) = &mdcv {
b.extend(mdcv);
}
if let Some(clli) = &clli {
b.extend(clli);
}
b.finish()
}
fn push_visual_sample_entry_header(b: &mut BoxBuilder, width: u32, height: u32) {
for _ in 0..6 {
b.u8(0);
} b.u16(1); b.u16(0); b.u16(0); for _ in 0..3 {
b.u32(0);
} b.u16(width as u16);
b.u16(height as u16);
b.u32(0x00480000); b.u32(0x00480000); b.u32(0); b.u16(1); b.u8(0);
for _ in 0..31 {
b.u8(0);
} b.u16(0x0018); b.u16(0xFFFF); }
fn push_color_boxes(b: &mut BoxBuilder, color_metadata: &ColorMetadata) {
b.extend(&build_colr_nclx(color_metadata));
if let Some(md) = color_metadata.mastering_display.as_ref() {
b.extend(&build_mdcv(md));
}
if let Some(cll) = color_metadata.content_light_level.as_ref() {
b.extend(&build_clli(cll));
}
}
fn strip_emulation(data: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len());
let n = data.len();
let mut i = 0;
while i < n {
if i + 2 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 3 {
out.push(0);
out.push(0);
i += 3; } else {
out.push(data[i]);
i += 1;
}
}
out
}
pub(crate) fn build_avc1(
width: u32,
height: u32,
avcc: &[u8],
color_metadata: &ColorMetadata,
fourcc: &[u8; 4],
) -> Vec<u8> {
let mut b = BoxBuilder::new(fourcc);
push_visual_sample_entry_header(&mut b, width, height);
b.extend(avcc);
push_color_boxes(&mut b, color_metadata);
b.finish()
}
pub(crate) fn build_hvc1(
width: u32,
height: u32,
hvcc: &[u8],
color_metadata: &ColorMetadata,
fourcc: &[u8; 4],
) -> Vec<u8> {
let mut b = BoxBuilder::new(fourcc);
push_visual_sample_entry_header(&mut b, width, height);
b.extend(hvcc);
push_color_boxes(&mut b, color_metadata);
b.finish()
}
pub(crate) fn build_avcc(sps: &[Vec<u8>], pps: &[Vec<u8>]) -> Vec<u8> {
let first = sps.first().map(|s| s.as_slice()).unwrap_or(&[]);
let (profile, compat, level) = if first.len() >= 4 {
(first[1], first[2], first[3])
} else {
(0x64, 0x00, 0x1f) };
let mut body = Vec::new();
body.push(1); body.push(profile);
body.push(compat);
body.push(level);
body.push(0xFF); body.push(0xE0 | (sps.len() as u8 & 0x1F)); for s in sps {
body.extend_from_slice(&(s.len() as u16).to_be_bytes());
body.extend_from_slice(s);
}
body.push(pps.len() as u8); for p in pps {
body.extend_from_slice(&(p.len() as u16).to_be_bytes());
body.extend_from_slice(p);
}
let mut b = BoxBuilder::new(b"avcC");
b.extend(&body);
b.finish()
}
pub(crate) fn build_hvcc(vps: &[Vec<u8>], sps: &[Vec<u8>], pps: &[Vec<u8>]) -> Vec<u8> {
let mut ptl = [0u8; 12];
let (mut bit_depth_luma_m8, mut bit_depth_chroma_m8, mut chroma_format) = (0u8, 0u8, 1u8);
if let Some(s) = sps.first() {
let rbsp = strip_emulation(s);
if rbsp.len() >= 15 {
ptl.copy_from_slice(&rbsp[3..15]);
} else {
ptl[0] = 0x01; ptl[11] = 123; }
let mut annexb = vec![0u8, 0, 0, 1];
annexb.extend_from_slice(s);
if let Some(info) = codec::pixel_format::parse_hevc_sps(&annexb) {
bit_depth_luma_m8 = info.bit_depth_luma.saturating_sub(8);
bit_depth_chroma_m8 = info.bit_depth_chroma.saturating_sub(8);
chroma_format = info.chroma_format_idc;
}
}
let mut body = Vec::new();
body.push(1); body.extend_from_slice(&ptl); body.extend_from_slice(&[0xF0, 0x00]); body.push(0xFC); body.push(0xFC | (chroma_format & 0x03)); body.push(0xF8 | (bit_depth_luma_m8 & 0x07)); body.push(0xF8 | (bit_depth_chroma_m8 & 0x07)); body.extend_from_slice(&[0, 0]); body.push(0x0F); let arrays: [(u8, &[Vec<u8>]); 3] = [(32, vps), (33, sps), (34, pps)];
let present: Vec<&(u8, &[Vec<u8>])> = arrays.iter().filter(|(_, v)| !v.is_empty()).collect();
body.push(present.len() as u8); for (nal_type, set) in present {
body.push(0x80 | nal_type); body.extend_from_slice(&(set.len() as u16).to_be_bytes());
for nal in *set {
body.extend_from_slice(&(nal.len() as u16).to_be_bytes());
body.extend_from_slice(nal);
}
}
let mut b = BoxBuilder::new(b"hvcC");
b.extend(&body);
b.finish()
}
pub(super) fn transfer_to_h273(transfer: codec::frame::TransferFn) -> u8 {
use codec::frame::TransferFn;
match transfer {
TransferFn::Bt709 => 1,
TransferFn::Bt470Bg => 4,
TransferFn::Linear => 8,
TransferFn::St2084 => 16,
TransferFn::AribStdB67 => 18,
TransferFn::Unspecified => 2,
}
}
pub(super) fn build_colr_nclx(color_metadata: &ColorMetadata) -> Vec<u8> {
let mut b = BoxBuilder::new(b"colr");
b.extend(b"nclx");
b.u16(color_metadata.colour_primaries as u16);
b.u16(transfer_to_h273(color_metadata.transfer) as u16);
b.u16(color_metadata.matrix_coefficients as u16);
let full_range_byte: u8 = if color_metadata.full_range {
0x80
} else {
0x00
};
b.u8(full_range_byte);
b.finish()
}
pub(super) fn build_mdcv(md: &codec::frame::MasteringDisplay) -> Vec<u8> {
let mut b = BoxBuilder::new(b"mdcv");
b.u16(md.primaries_r_x);
b.u16(md.primaries_r_y);
b.u16(md.primaries_g_x);
b.u16(md.primaries_g_y);
b.u16(md.primaries_b_x);
b.u16(md.primaries_b_y);
b.u16(md.white_point_x);
b.u16(md.white_point_y);
b.u32(md.max_luminance);
b.u32(md.min_luminance);
b.finish()
}
pub(super) fn build_clli(cll: &codec::frame::ContentLightLevel) -> Vec<u8> {
let mut b = BoxBuilder::new(b"clli");
b.u16(cll.max_cll);
b.u16(cll.max_fall);
b.finish()
}
fn build_av1c(config_obus: &[u8]) -> Vec<u8> {
let mut b = BoxBuilder::new(b"av1C");
b.u8(0x81);
let (
seq_profile,
seq_level_idx_0,
seq_tier_0,
high_bitdepth,
twelve_bit,
monochrome,
chroma_sub_x,
chroma_sub_y,
chroma_sample_position,
) = parse_seq_header_params(config_obus);
b.u8(((seq_profile & 0x7) << 5) | (seq_level_idx_0 & 0x1F));
let byte3 = ((seq_tier_0 & 0x1) << 7)
| ((high_bitdepth as u8 & 0x1) << 6)
| ((twelve_bit as u8 & 0x1) << 5)
| ((monochrome as u8 & 0x1) << 4)
| ((chroma_sub_x & 0x1) << 3)
| ((chroma_sub_y & 0x1) << 2)
| (chroma_sample_position & 0x3);
b.u8(byte3);
b.u8(0);
b.extend(config_obus);
b.finish()
}