///|
/// A separate-package consumer for color-managed AV1 video. Every input in
/// fixtures.mbt is retained or explicitly derived from the saved fixtures; the example
/// itself performs no file I/O.
fn main {
let avif_input = metadata_fixture()
let native = @moonav1.avif_decode_native(avif_input).unwrap()
let metadata = @moonav1.avif_metadata(avif_input).unwrap()
let icc_bytes = metadata.icc.unwrap()
let profile_bytes = Array::makei(icc_bytes.length(), i => icc_bytes[i])
let profile = match @moonav1.IccProfile::parse(profile_bytes) {
Ok(value) => value
Err(_) => abort("ICC fixture was rejected")
}
let managed = profile.apply_rgba16(native.to_rgba16().unwrap()).unwrap()
println(
"ICC: RGBA16 \{managed.width}x\{managed.height}, profile=\{icc_bytes.length()} bytes, alpha=\{managed.data[3]}",
)
let hdr_frame = @moonav1.av1_decode_native(hdr_obu()).unwrap()
let sdr = hdr_frame
.to_sdr_rgba16(options={
peak_luminance_nits: 600.0,
reference_white_nits: 100.0,
exposure_stops: 0.0,
})
.unwrap()
println("HDR: explicit PQ tone map, SDR16 red=\{sdr.data[0]}")
let sample = metadata
.resample_rgba16(native.to_rgba16().unwrap(), 7, 6)
.unwrap()
println(
"Crop: 23/2 x 19/2 clap, transforms=\{metadata.transforms.length()}, RGBA16 \{sample.width}x\{sample.height}, red=\{sample.data[0]}",
)
container_summary("IVF", container_ivf())
container_summary("MP4", container_mp4())
container_summary("WebM", container_webm())
let compensated = profile
.apply_rgba16_with_options(
native.to_rgba16().unwrap(),
@moonav1.IccTransformOptions::new(@moonav1.RelativeColorimetric, true),
)
.unwrap()
println(
"ICC policy: relative+BPC, red=\{compensated.data[0]}, alpha=\{compensated.data[3]}",
)
stream_summary("IVF", container_ivf())
stream_summary("MP4", container_mp4())
stream_summary("WebM", container_webm())
let video = @moonav1.Av1ContainerDecoder::new(container_mp4()).unwrap()
let first = video.next_frame().unwrap().unwrap()
let requested = first.timestamp.unwrap()
let located = video.seek_timestamp(requested).unwrap()
let outcome = match located.outcome {
@moonav1.Exact => "exact"
@moonav1.Next => "next"
@moonav1.End => "end"
@moonav1.UnknownTimestamp => "unknown timestamp"
}
println(
"Seek: requested=\{requested}, outcome=\{outcome}, packet=\{located.frame.unwrap().packet_index}",
)
let timeline = video.presentation_timeline().unwrap()
let point = match timeline.map_movie_time(0L).unwrap() {
@moonav1.Empty => "empty edit"
@moonav1.End => "end"
@moonav1.Media(time) => "\{time.numerator}/\{time.denominator} media ticks"
}
println("Timeline: edits=\{timeline.edit_count()}, movie tick 0=\{point}")
}
///|
fn container_summary(label : String, data : Array[Byte]) -> Unit {
let decoder = match @moonav1.Av1ContainerDecoder::new(data) {
Ok(value) => value
Err(_) => abort("Container fixture was rejected")
}
let mut presentations = 0
while true {
match decoder.next_frame() {
Ok(Some(_)) => presentations += 1
Ok(None) => break
Err(_) => abort("Container decode failed")
}
}
println(
"Container \{label}: packets=\{decoder.packet_count()}, presentations=\{presentations}",
)
}
///|
/// A host supplies byte chunks; this example supplies them from saved data.
/// Consume each returned batch before feeding more compressed input.
fn stream_summary(label : String, data : Array[Byte]) -> Unit {
let stream = @moonav1.Av1ContainerStreamDecoder::new(limits={
..@moonav1.DecodeLimits::default(),
max_input_bytes: 8192,
}).unwrap()
let mut presentations = 0
let mut replayed = false
while true {
let mut at = 0
while at < data.length() {
let count = if data.length() - at < 37 { data.length() - at } else { 37 }
let chunk = Array::makei(count, i => data[at + i])
let batch = stream.feed(chunk).unwrap()
presentations += batch.frames.length()
at += count
}
let last = stream.finish().unwrap()
presentations += last.frames.length()
match last.status {
@moonav1.ReplayRequired => {
// Tail-moov MP4 retained its index. A seekable host rewinds the same
// file; we resend this immutable input from byte zero.
if replayed {
abort("Repeated replay request")
}
stream.replay().unwrap()
replayed = true
}
@moonav1.Finished => break
_ => abort("Container did not finish")
}
}
println(
"Stream \{label}: presentations=\{presentations}, peak=\{stream.peak_pending_bytes()}, replay=\{replayed}",
)
}