///|
/// Pure MoonBit ISO Base Media File Format demux for AV1 `av01` tracks.
///
/// The parser deliberately keeps the public result small: samples are returned
/// in decode order, with raw media timestamps in the track timescale. The
/// container parser owns every returned byte buffer, so callers can release
/// the input after this function returns.
let mp4_moov : Int = 0x6D6F6F76

///|
let mp4_mvhd : Int = 0x6D766864

///|
let mp4_trak : Int = 0x7472616B

///|
let mp4_tkhd_box : Int = 0x746B6864

///|
let mp4_mdia : Int = 0x6D646961

///|
let mp4_mdhd : Int = 0x6D646864

///|
let mp4_hdlr : Int = 0x68646C72

///|
let mp4_minf : Int = 0x6D696E66

///|
let mp4_dinf : Int = 0x64696E66

///|
let mp4_dref : Int = 0x64726566

///|
let mp4_stbl : Int = 0x7374626C

///|
let mp4_stsd : Int = 0x73747364

///|
let mp4_stts : Int = 0x73747473

///|
let mp4_ctts : Int = 0x63747473

///|
let mp4_stsc : Int = 0x73747363

///|
let mp4_stsz : Int = 0x7374737A

///|
let mp4_stz2 : Int = 0x73747A32

///|
let mp4_stco : Int = 0x7374636F

///|
let mp4_co64 : Int = 0x636F3634

///|
let mp4_stss : Int = 0x73747373

///|
let mp4_av01 : Int = 0x61763031

///|
let mp4_av1c : Int = 0x61763143

///|
let mp4_mdat : Int = 0x6D646174

///|
let mp4_edts : Int = 0x65647473

///|
let mp4_elst : Int = 0x656C7374

///|
let mp4_mvex : Int = 0x6D766578

///|
let mp4_trex : Int = 0x74726578

///|
let mp4_moof : Int = 0x6D6F6F66

///|
let mp4_traf : Int = 0x74726166

///|
let mp4_tfhd : Int = 0x74666864

///|
let mp4_tfdt_box : Int = 0x74666474

///|
let mp4_trun : Int = 0x7472756E

///|
let mp4_vide : Int = 0x76696465

///|
let mp4_url : Int = 0x75726C20

///|
let mp4_urn : Int = 0x75726E20

///|
let mp4_sinf : Int = 0x73696E66

///|
let mp4_i64_max : Int64 = 9223372036854775807L

///|
let mp4_i64_min : Int64 = -9223372036854775807L - 1L

///|
priv struct Mp4Box {
  typ : Int
  start : Int
  payload : Int
  end : Int
  parent : Int
}

///|
priv struct Mp4Mdat {
  start : Int
  end : Int
}

///|
priv struct Mp4StscEntry {
  first_chunk : Int64
  samples_per_chunk : Int64
  description_index : Int
}

///|
priv struct Mp4TimingEntry {
  count : Int64
  value : Int64
}

///|
priv struct Mp4Track {
  id : Int64
  width : Int
  height : Int
  timescale : Int64
  av1c : Array[Byte]
  stbl_box : Int
  has_sample_tables : Bool
  movie_timescale : Int64?
  edits : Array[Av1TrackEdit]
}

///|
priv struct Mp4Trex {
  track_id : Int64
  description_index : Int
  duration : Int64
  size : Int
  flags : Int
}

///|
priv struct Mp4Tfhd {
  track_id : Int64
  flags : Int
  base_offset : Int64?
  description_index : Int
  duration : Int64?
  size : Int?
  sample_flags : Int?
}

///|
priv struct Mp4FragmentState {
  mut next_decode_time : Int64
}

///|
priv struct Mp4Sample {
  offset : Int64
  size : Int
  dts : Int64
  pts : Int64
  duration : Int64
  sync : Bool?
}

///|
priv suberror Mp4Error {
  Diagnostic(DecodeError)
}

///|
fn mp4_error(
  kind : DecodeErrorKind,
  message : String,
  offset? : Int? = None,
) -> Mp4Error {
  Mp4Error::Diagnostic(decode_error(kind, "mp4", message, offset~))
}

///|
fn mp4_u8(data : Array[Byte], p : Int, end : Int) -> Int raise Mp4Error {
  if p < 0 || p >= end || p >= data.length() {
    raise mp4_error(TruncatedInput, "MP4 field is truncated", offset=Some(p))
  }
  data[p].to_int()
}

///|
fn mp4_u16(data : Array[Byte], p : Int, end : Int) -> Int raise Mp4Error {
  mp4_u8(data, p, end) * 256 + mp4_u8(data, p + 1, end)
}

///|
fn mp4_bits32(data : Array[Byte], p : Int, end : Int) -> Int raise Mp4Error {
  mp4_u8(data, p, end) * 16777216 +
  mp4_u8(data, p + 1, end) * 65536 +
  mp4_u8(data, p + 2, end) * 256 +
  mp4_u8(data, p + 3, end)
}

///|
fn mp4_u32_wide(data : Array[Byte], p : Int, end : Int) -> Int64 raise Mp4Error {
  let bits = mp4_bits32(data, p, end).to_int64()
  if bits < 0L {
    bits + 4294967296L
  } else {
    bits
  }
}

///|
fn mp4_u32(data : Array[Byte], p : Int, end : Int) -> Int raise Mp4Error {
  let value = mp4_bits32(data, p, end)
  if value < 0 {
    raise mp4_error(
      LimitExceeded,
      "MP4 size or count exceeds addressable Int",
      offset=Some(p),
    )
  }
  value
}

///|
fn mp4_i32(data : Array[Byte], p : Int, end : Int) -> Int64 raise Mp4Error {
  mp4_bits32(data, p, end).to_int64()
}

///|
fn mp4_i16(data : Array[Byte], p : Int, end : Int) -> Int raise Mp4Error {
  let n = mp4_u16(data, p, end)
  if n >= 32768 {
    n - 65536
  } else {
    n
  }
}

///|
fn mp4_i64(data : Array[Byte], p : Int, end : Int) -> Int64 raise Mp4Error {
  let hi = mp4_bits32(data, p, end).to_int64()
  (hi << 32) | mp4_u32_wide(data, p + 4, end)
}

///|
fn mp4_u64(data : Array[Byte], p : Int, end : Int) -> Int64 raise Mp4Error {
  let hi = mp4_u32_wide(data, p, end)
  let lo = mp4_u32_wide(data, p + 4, end)
  if hi > 2147483647L {
    raise mp4_error(
      Unsupported,
      "64-bit MP4 value exceeds supported range",
      offset=Some(p),
    )
  }
  (hi << 32) | lo
}

///|
fn mp4_add(a : Int64, b : Int64) -> Int64 raise Mp4Error {
  if (b > 0L && a > mp4_i64_max - b) || (b < 0L && a < mp4_i64_min - b) {
    raise mp4_error(InvalidData, "MP4 integer arithmetic overflows")
  }
  a + b
}

///|
fn mp4_copy(
  data : Array[Byte],
  from : Int,
  to : Int,
) -> Array[Byte] raise Mp4Error {
  if from < 0 || to < from || to > data.length() {
    raise mp4_error(
      TruncatedInput,
      "MP4 payload is outside the input",
      offset=Some(from),
    )
  }
  Array::makei(to - from, i => data[from + i])
}

///|
fn mp4_index(boxes : Array[Mp4Box], start : Int) -> Int {
  for i in 0.. Mp4Box? {
  for box in boxes {
    if box.parent == parent && box.typ == typ {
      return Some(box)
    }
  }
  None
}

///|
fn mp4_children(
  boxes : Array[Mp4Box],
  parent : Int,
  typ : Int,
) -> Array[Mp4Box] {
  let out : Array[Mp4Box] = []
  for box in boxes {
    if box.parent == parent && box.typ == typ {
      out.push(box)
    }
  }
  out
}

///|
fn mp4_is_container(typ : Int) -> Bool {
  typ == mp4_moov ||
  typ == mp4_trak ||
  typ == mp4_mdia ||
  typ == mp4_minf ||
  typ == mp4_dinf ||
  typ == mp4_stbl ||
  typ == mp4_mvex ||
  typ == mp4_moof ||
  typ == mp4_traf ||
  typ == mp4_edts ||
  typ == mp4_av01
}

///|
fn mp4_is_fullbox_children(typ : Int) -> Bool {
  typ == mp4_stsd || typ == mp4_dref
}

///|
fn mp4_walk(
  data : Array[Byte],
  from : Int,
  to : Int,
  parent : Int,
  boxes : Array[Mp4Box],
  depth : Int,
) -> Unit raise Mp4Error {
  if depth > 64 {
    raise mp4_error(
      LimitExceeded,
      "MP4 box nesting exceeds 64 levels",
      offset=Some(from),
    )
  }
  let mut p = from
  while p < to {
    if to - p < 8 {
      raise mp4_error(
        TruncatedInput,
        "Incomplete MP4 box header",
        offset=Some(p),
      )
    }
    let size32 = mp4_u32(data, p, to)
    let typ = mp4_bits32(data, p + 4, to)
    let mut header = 8
    let mut size = size32.to_int64()
    if size32 == 1 {
      header = 16
      size = mp4_u64(data, p + 8, to)
    } else if size32 == 0 {
      size = (to - p).to_int64()
    }
    if size < header.to_int64() || size > (to - p).to_int64() {
      raise mp4_error(
        InvalidData,
        "MP4 box size is outside its parent",
        offset=Some(p),
      )
    }
    let size_i = size.to_int()
    let end = p + size_i
    let payload = p + header
    if boxes.length() >= 65536 {
      raise mp4_error(LimitExceeded, "MP4 exceeds 65536 boxes", offset=Some(p))
    }
    let index = boxes.length()
    boxes.push({ typ, start: p, payload, end, parent, })
    if mp4_is_container(typ) || mp4_is_fullbox_children(typ) {
      let child_from = if mp4_is_fullbox_children(typ) {
        payload + 8
      } else if typ == mp4_av01 {
        p + 86
      } else {
        payload
      }
      if child_from > end {
        raise mp4_error(
          TruncatedInput,
          "MP4 container header is truncated",
          offset=Some(payload),
        )
      }
      mp4_walk(data, child_from, end, index, boxes, depth + 1)
    }
    p = end
  }
}

///|
fn mp4_boxes(data : Array[Byte]) -> Result[Array[Mp4Box], DecodeError] {
  try {
    let out : Array[Mp4Box] = []
    mp4_walk(data, 0, data.length(), -1, out, 0)
    Ok(out)
  } catch {
    Mp4Error::Diagnostic(error) => Err(error)
  }
}

///|
fn mp4_fullbox(data : Array[Byte], box : Mp4Box) -> (Int, Int) raise Mp4Error {
  let version = mp4_u8(data, box.payload, box.end)
  let flags = mp4_bits32(data, box.payload, box.end) & 0xFFFFFF
  (version, flags)
}

///|
fn mp4_validate_mdat(
  samples : Array[Mp4Sample],
  data : Array[Byte],
  mdats : Array[Mp4Mdat],
) -> Unit raise Mp4Error {
  for sample in samples {
    if sample.offset < 0 {
      raise mp4_error(InvalidData, "Negative MP4 sample offset")
    }
    let mut found = false
    for mdat in mdats {
      let start = mdat.start.to_int64()
      let end = mdat.end.to_int64()
      if sample.offset >= start &&
        sample.offset <= end &&
        sample.size.to_int64() <= end - sample.offset {
        found = true
        break
      }
    }
    if !found {
      raise mp4_error(InvalidData, "MP4 sample range is outside an mdat box")
    }
    if sample.offset > data.length().to_int64() - sample.size.to_int64() {
      raise mp4_error(TruncatedInput, "MP4 sample range exceeds the input")
    }
  }
}

///|
fn mp4_find_mdat(boxes : Array[Mp4Box]) -> Array[Mp4Mdat] {
  let out : Array[Mp4Mdat] = []
  for box in boxes {
    if box.parent == -1 && box.typ == mp4_mdat {
      out.push({ start: box.payload, end: box.end, })
    }
  }
  out
}

///|
fn mp4_parse_tkhd(
  data : Array[Byte],
  box : Mp4Box,
) -> (Int64, Int, Int) raise Mp4Error {
  let version = mp4_u8(data, box.payload, box.end)
  if version > 1 {
    raise mp4_error(Unsupported, "Unsupported tkhd version")
  }
  let id_at = if version == 0 { box.payload + 12 } else { box.payload + 20 }
  let geometry_at = if version == 0 {
    box.payload + 76
  } else {
    box.payload + 88
  }
  let id = mp4_u32_wide(data, id_at, box.end)
  let width_fixed = mp4_u32_wide(data, geometry_at, box.end)
  let height_fixed = mp4_u32_wide(data, geometry_at + 4, box.end)
  let width = (width_fixed / 65536L).to_int()
  let height = (height_fixed / 65536L).to_int()
  if id <= 0L {
    raise mp4_error(InvalidData, "MP4 track has invalid id")
  }
  (id, width, height)
}

///|
fn mp4_timescale(data : Array[Byte], box : Mp4Box) -> Int64 raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version > 1 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported mdhd version")
  }
  let at = if version == 0 { box.payload + 12 } else { box.payload + 20 }
  let value = mp4_u32_wide(data, at, box.end)
  if value <= 0L {
    raise mp4_error(InvalidData, "MP4 track has no positive timescale")
  }
  value
}

///|
fn mp4_handler_is_video(
  data : Array[Byte],
  box : Mp4Box,
) -> Bool raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported hdlr version")
  }
  mp4_u32(data, box.payload + 8, box.end) == mp4_vide
}

///|
fn mp4_check_data_reference(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  stbl : Int,
) -> Unit raise Mp4Error {
  let minf = boxes[stbl].parent
  let dinf = match mp4_child(boxes, minf, mp4_dinf) {
    Some(value) => value
    None => return
  }
  let di = mp4_index(boxes, dinf.start)
  let dref = match mp4_child(boxes, di, mp4_dref) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "dinf has no dref")
  }
  let (dref_version, dref_flags) = mp4_fullbox(data, dref)
  if dref_version != 0 || dref_flags != 0 {
    raise mp4_error(Unsupported, "Unsupported dref FullBox version or flags")
  }
  let count = mp4_u32(data, dref.payload + 4, dref.end)
  let entries = mp4_children(boxes, mp4_index(boxes, dref.start), mp4_url)
  let urns = mp4_children(boxes, mp4_index(boxes, dref.start), mp4_urn)
  if count != entries.length() + urns.length() || count != 1 {
    raise mp4_error(Unsupported, "Multiple or unsupported MP4 data references")
  }
  if entries.length() != 1 {
    raise mp4_error(Unsupported, "External MP4 data reference is unsupported")
  }
  let url = entries[0]
  let (version, flags) = mp4_fullbox(data, url)
  if version != 0 || flags != 1 {
    raise mp4_error(Unsupported, "External MP4 data reference is unsupported")
  }
}

///|
fn mp4_stsd_av1c(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  stsd : Mp4Box,
) -> (Int, Int, Array[Byte])? raise Mp4Error {
  let (stsd_version, stsd_flags) = mp4_fullbox(data, stsd)
  if stsd_version != 0 || stsd_flags != 0 {
    raise mp4_error(Unsupported, "Unsupported stsd FullBox version or flags")
  }
  let entry_count = mp4_u32(data, stsd.payload + 4, stsd.end)
  let all_entries : Array[Mp4Box] = []
  let stsd_index = mp4_index(boxes, stsd.start)
  for box in boxes {
    if box.parent == stsd_index {
      all_entries.push(box)
    }
  }
  if entry_count != all_entries.length() {
    raise mp4_error(InvalidData, "stsd entry count does not match its boxes")
  }
  if all_entries.length() == 0 {
    return None
  }
  let mut has_av1 = false
  for entry in all_entries {
    if entry.typ == mp4_av01 {
      has_av1 = true
    }
  }
  if !has_av1 {
    return None
  }
  if all_entries.length() != 1 {
    raise mp4_error(Unsupported, "MP4 sample-entry switches are unsupported")
  }
  let entry = all_entries[0]
  if entry.end - entry.start < 86 {
    raise mp4_error(TruncatedInput, "av01 sample entry is truncated")
  }
  let data_reference = mp4_u16(data, entry.start + 14, entry.end)
  if data_reference != 1 {
    raise mp4_error(
      Unsupported,
      "av01 sample entry uses an external data reference",
    )
  }
  let width = mp4_u16(data, entry.start + 32, entry.end)
  let height = mp4_u16(data, entry.start + 34, entry.end)
  if width <= 0 || height <= 0 {
    raise mp4_error(InvalidData, "av01 sample entry has invalid dimensions")
  }
  if mp4_child(boxes, mp4_index(boxes, entry.start), mp4_sinf) is Some(_) {
    raise mp4_error(Unsupported, "Encrypted MP4 sample entries are unsupported")
  }
  let av1c_children = mp4_children(
    boxes,
    mp4_index(boxes, entry.start),
    mp4_av1c,
  )
  if av1c_children.length() != 1 {
    raise mp4_error(
      InvalidData,
      "av01 sample entry must contain exactly one av1C",
    )
  }
  let av1c_box = av1c_children[0]
  let record = mp4_copy(data, av1c_box.payload, av1c_box.end)
  let initialization = match av1_demux_av1c(record) {
    Ok(value) => value
    Err(error) => raise Mp4Error::Diagnostic(error)
  }
  match av1_sequence_info(initialization) {
    Some(sequence) =>
      if width != sequence.width || height != sequence.height {
        raise mp4_error(
          InvalidData,
          "av01 dimensions exceed the av1C sequence header",
        )
      }
    None => ()
  }
  Some((width, height, initialization))
}

///|
fn mp4_parse_movie_timescale(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moov : Mp4Box,
) -> Int64? raise Mp4Error {
  let mi = mp4_index(boxes, moov.start)
  let mvhd = match mp4_child(boxes, mi, mp4_mvhd) {
    Some(value) => value
    None => return None
  }
  let (version, flags) = mp4_fullbox(data, mvhd)
  if version > 1 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported mvhd version")
  }
  let at = if version == 0 { mvhd.payload + 12 } else { mvhd.payload + 20 }
  let timescale = mp4_u32_wide(data, at, mvhd.end)
  if timescale <= 0L {
    raise mp4_error(InvalidData, "Movie has no positive timescale")
  }
  Some(timescale)
}

///|
fn mp4_parse_edits(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  trak_index : Int,
  movie_timescale : Int64?,
) -> (Int64?, Array[Av1TrackEdit]) raise Mp4Error {
  let out : Array[Av1TrackEdit] = []
  let edts = mp4_children(boxes, trak_index, mp4_edts)
  if edts.length() == 0 {
    return (movie_timescale, out)
  }
  if edts.length() != 1 {
    raise mp4_error(InvalidData, "Track contains multiple edit list containers")
  }
  let ei = mp4_index(boxes, edts[0].start)
  let elsts = mp4_children(boxes, ei, mp4_elst)
  if elsts.length() != 1 {
    raise mp4_error(
      InvalidData,
      "Track edit container does not contain one elst",
    )
  }
  let movie_scale = match movie_timescale {
    Some(value) => value
    None =>
      raise mp4_error(InvalidData, "Track edit list has no movie timescale")
  }
  let elst = elsts[0]
  let (version, flags) = mp4_fullbox(data, elst)
  if flags != 0 || version > 1 {
    raise mp4_error(Unsupported, "Unsupported elst version or flags")
  }
  let count = mp4_u32(data, elst.payload + 4, elst.end)
  let entry_width = if version == 0 { 12 } else { 20 }
  if count > (elst.end - elst.payload - 8) / entry_width {
    raise mp4_error(TruncatedInput, "elst entries are truncated")
  }
  let mut p = elst.payload + 8
  for _ in 0.. Mp4Track? raise Mp4Error {
  let ti = mp4_index(boxes, trak.start)
  let tkhd = match mp4_child(boxes, ti, mp4_tkhd_box) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "trak has no tkhd")
  }
  let (id, tk_width, tk_height) = mp4_parse_tkhd(data, tkhd)
  let mdia = match mp4_child(boxes, ti, mp4_mdia) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "trak has no mdia")
  }
  let mi = mp4_index(boxes, mdia.start)
  let hdlr = match mp4_child(boxes, mi, mp4_hdlr) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "mdia has no hdlr")
  }
  if !mp4_handler_is_video(data, hdlr) {
    return None
  }
  let mdhd = match mp4_child(boxes, mi, mp4_mdhd) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "mdia has no mdhd")
  }
  let timescale = mp4_timescale(data, mdhd)
  let minf = match mp4_child(boxes, mi, mp4_minf) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "mdia has no minf")
  }
  let mini = mp4_index(boxes, minf.start)
  let stbl = match mp4_child(boxes, mini, mp4_stbl) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "minf has no stbl")
  }
  let si = mp4_index(boxes, stbl.start)
  let stsd = match mp4_child(boxes, si, mp4_stsd) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "stbl has no stsd")
  }
  match mp4_stsd_av1c(data, boxes, stsd) {
    None => None
    Some((entry_width, entry_height, av1c)) => {
      mp4_check_data_reference(data, boxes, si)
      let (track_movie_timescale, edits) = match requested_id {
        Some(wanted) if wanted != id => (None, [])
        _ => mp4_parse_edits(data, boxes, ti, movie_timescale)
      }
      // tkhd is a presentation hint and may be zero in unusual files; the
      // coded sample entry dimensions are the dimensions returned to callers.
      let width = if entry_width > 0 { entry_width } else { tk_width }
      let height = if entry_height > 0 { entry_height } else { tk_height }
      let has_tables = mp4_child(boxes, si, mp4_stts) is Some(_) ||
        mp4_child(boxes, si, mp4_stsz) is Some(_) ||
        mp4_child(boxes, si, mp4_stz2) is Some(_)
      Some({
        id,
        width,
        height,
        timescale,
        av1c,
        stbl_box: si,
        has_sample_tables: has_tables,
        movie_timescale: track_movie_timescale,
        edits,
      })
    }
  }
}

///|
/// Parse only the metadata carried by one complete moov box. The returned
/// track owns all variable-size metadata; box indexes are scratch state and
/// are intentionally not retained by callers.
fn mp4_parse_moov_metadata(
  data : Array[Byte],
  requested_id : Int64?,
  limits : DecodeLimits,
) -> (Mp4Track, Mp4Trex?) raise Mp4Error {
  let boxes = match mp4_boxes(data) {
    Ok(value) => value
    Err(error) => raise Mp4Error::Diagnostic(error)
  }
  let moov = match mp4_child(boxes, -1, mp4_moov) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "MP4 metadata has no moov box")
  }
  let movie_timescale = mp4_parse_movie_timescale(data, boxes, moov)
  let moovi = mp4_index(boxes, moov.start)
  let tracks = mp4_children(boxes, moovi, mp4_trak)
  let mut selected : Mp4Track? = None
  for trak in tracks {
    let candidate = mp4_parse_track(
      data, boxes, trak, movie_timescale, requested_id,
    )
    match candidate {
      None => ()
      Some(track) =>
        match requested_id {
          Some(wanted) =>
            if track.id == wanted {
              selected = Some(track)
              break
            }
          None => {
            selected = Some(track)
            break
          }
        }
    }
  }
  let track = match selected {
    Some(value) => value
    None => raise mp4_error(Unsupported, "No selected AV1 video track")
  }
  if track.height <= 0 || track.width > limits.max_frame_pixels / track.height {
    raise mp4_error(LimitExceeded, "Track dimensions exceed max_frame_pixels")
  }
  let trex = mp4_find_trex(data, boxes, moov, track.id)
  (track, trex)
}

///|
fn mp4_parse_timing_table(
  data : Array[Byte],
  box : Mp4Box,
  signed : Bool,
) -> Array[Mp4TimingEntry] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if flags != 0 ||
    (signed && version != 0 && version != 1) ||
    (!signed && version != 0) {
    raise mp4_error(Unsupported, "Unsupported MP4 timing table version")
  }
  let count = mp4_u32(data, box.payload + 4, box.end)
  if count > (box.end - box.payload - 8) / 8 {
    raise mp4_error(
      TruncatedInput,
      "MP4 timing table count exceeds its payload",
    )
  }
  let entries : Array[Mp4TimingEntry] = []
  let mut p = box.payload + 8
  for _ in 0.. Array[Int] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported stsz version or flags")
  }
  let fixed = mp4_u32(data, box.payload + 4, box.end)
  let count = mp4_u32(data, box.payload + 8, box.end)
  if count > max_count {
    raise mp4_error(LimitExceeded, "MP4 sample count exceeds max_frames")
  }
  let sizes : Array[Int] = []
  if fixed != 0 {
    for _ in 0.. Array[Int] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported stz2 version or flags")
  }
  let field_size = mp4_u8(data, box.payload + 7, box.end)
  if field_size != 4 && field_size != 8 && field_size != 16 {
    raise mp4_error(Unsupported, "Unsupported stz2 field size")
  }
  let count = mp4_u32(data, box.payload + 8, box.end)
  if count > max_count {
    raise mp4_error(LimitExceeded, "MP4 sample count exceeds max_frames")
  }
  let sizes : Array[Int] = []
  let mut p = box.payload + 12
  if field_size == 4 {
    let mut i = 0
    while i < count {
      let packed = mp4_u8(data, p, box.end)
      sizes.push(packed >> 4)
      i += 1
      if i < count {
        sizes.push(packed & 15)
        i += 1
      }
      p += 1
    }
  } else if field_size == 8 {
    for _ in 0.. Array[Mp4TimingEntry] raise Mp4Error {
  let entries = mp4_parse_timing_table(data, box, false)
  let mut total = 0L
  for entry in entries {
    total = mp4_add(total, entry.count)
  }
  if total != sample_count.to_int64() {
    raise mp4_error(InvalidData, "stts sample count does not match stsz")
  }
  entries
}

///|
fn mp4_expand_ctts(
  data : Array[Byte],
  box : Mp4Box,
  sample_count : Int,
) -> Array[Mp4TimingEntry] raise Mp4Error {
  let entries = mp4_parse_timing_table(data, box, true)
  let mut total = 0L
  for entry in entries {
    total = mp4_add(total, entry.count)
  }
  if total != sample_count.to_int64() {
    raise mp4_error(InvalidData, "ctts sample count does not match stsz")
  }
  entries
}

///|
fn mp4_parse_stsc(
  data : Array[Byte],
  box : Mp4Box,
) -> Array[Mp4StscEntry] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported stsc version or flags")
  }
  let count = mp4_u32(data, box.payload + 4, box.end)
  if count > (box.end - box.payload - 8) / 12 {
    raise mp4_error(TruncatedInput, "stsc count exceeds its payload")
  }
  let out : Array[Mp4StscEntry] = []
  let mut p = box.payload + 8
  let mut previous = 0L
  for _ in 0.. 0 && out[0].first_chunk != 1L {
    raise mp4_error(InvalidData, "stsc does not start at chunk one")
  }
  out
}

///|
fn mp4_parse_offsets(
  data : Array[Byte],
  box : Mp4Box,
) -> Array[Int64] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(
      Unsupported,
      "Unsupported chunk offset table version or flags",
    )
  }
  let count = mp4_u32(data, box.payload + 4, box.end)
  let out : Array[Int64] = []
  let mut p = box.payload + 8
  let entry_width = if box.typ == mp4_co64 { 8 } else { 4 }
  if count > (box.end - p) / entry_width {
    raise mp4_error(TruncatedInput, "Chunk offset count exceeds its payload")
  }
  for _ in 0.. Array[Bool] raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 {
    raise mp4_error(Unsupported, "Unsupported stss version or flags")
  }
  let n = mp4_u32(data, box.payload + 4, box.end)
  if n > (box.end - box.payload - 8) / 4 {
    raise mp4_error(TruncatedInput, "stss count exceeds its payload")
  }
  let sync = Array::make(count, false)
  let mut p = box.payload + 8
  let mut previous = 0
  for _ in 0.. count {
      raise mp4_error(InvalidData, "Invalid stss sample number", offset=Some(p))
    }
    sync[sample - 1] = true
    previous = sample
    p += 4
  }
  if p != box.end {
    raise mp4_error(InvalidData, "stss has trailing bytes")
  }
  sync
}

///|
/// Expand an ordinary sample table into absolute sample descriptors. This
/// helper deliberately does not inspect mdat boxes: incremental demuxers can
/// retain the bounded table metadata while the media payload arrives later.
fn mp4_table_sample_descriptors(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  track : Mp4Track,
  limits : DecodeLimits,
) -> Array[Mp4Sample] raise Mp4Error {
  let si = track.stbl_box
  let stsz = mp4_child(boxes, si, mp4_stsz)
  let stz2 = mp4_child(boxes, si, mp4_stz2)
  let sizes = match (stsz, stz2) {
    (Some(_), Some(_)) =>
      raise mp4_error(InvalidData, "stsz and stz2 both present")
    (Some(box), None) => mp4_expand_sizes(data, box, limits.max_frames)
    (None, Some(box)) => mp4_expand_stz2(data, box, limits.max_frames)
    (None, None) => return []
  }
  let count = sizes.length()
  if count == 0 {
    return []
  }
  let stts = match mp4_child(boxes, si, mp4_stts) {
    Some(value) => value
    None => raise mp4_error(InvalidData, "Non-empty stsz has no stts")
  }
  let stts_entries = mp4_expand_stts(data, stts, count)
  let ctts_entries = match mp4_child(boxes, si, mp4_ctts) {
    Some(value) => mp4_expand_ctts(data, value, count)
    None => []
  }
  let stsc = match mp4_child(boxes, si, mp4_stsc) {
    Some(value) => mp4_parse_stsc(data, value)
    None => raise mp4_error(InvalidData, "Non-empty stsz has no stsc")
  }
  let offset_box = match
    (mp4_child(boxes, si, mp4_stco), mp4_child(boxes, si, mp4_co64)) {
    (Some(_), Some(_)) =>
      raise mp4_error(InvalidData, "stco and co64 both present")
    (Some(value), None) => value
    (None, Some(value)) => value
    (None, None) =>
      raise mp4_error(InvalidData, "Non-empty stsz has no chunk offsets")
  }
  let offsets = mp4_parse_offsets(data, offset_box)
  if stsc.length() == 0 || offsets.length() == 0 {
    raise mp4_error(InvalidData, "MP4 sample table has no chunks")
  }
  let sample_offsets : Array[Int64] = []
  let mut sample_index = 0
  let mut entry_index = 0
  for chunk_zero in 0..= stsc[entry_index + 1].first_chunk {
      entry_index += 1
    }
    let entry = stsc[entry_index]
    if entry.samples_per_chunk > (count - sample_index).to_int64() {
      raise mp4_error(InvalidData, "stsc expands beyond stsz")
    }
    if entry.description_index != 1 {
      raise mp4_error(
        Unsupported,
        "MP4 sample-description switches are unsupported",
      )
    }
    let mut off = offsets[chunk_zero]
    for _ in 0..= count {
        raise mp4_error(InvalidData, "stsc expands beyond stsz")
      }
      sample_offsets.push(off)
      off = mp4_add(off, sizes[sample_index].to_int64())
      sample_index += 1
    }
  }
  if sample_index != count {
    raise mp4_error(InvalidData, "stsc does not cover all stsz samples")
  }
  let sync = match mp4_child(boxes, si, mp4_stss) {
    Some(value) => mp4_parse_sync(data, value, count)
    None => Array::make(count, true)
  }
  let samples : Array[Mp4Sample] = []
  let mut dts = 0L
  let mut timing_index = 0
  let mut timing_left = if stts_entries.length() == 0 {
    0L
  } else {
    stts_entries[0].count
  }
  let mut ctts_index = 0
  let mut ctts_left = if ctts_entries.length() == 0 {
    0L
  } else {
    ctts_entries[0].count
  }
  for i in 0.. Array[Mp4Sample] raise Mp4Error {
  let samples = mp4_table_sample_descriptors(data, boxes, track, limits)
  mp4_validate_mdat(samples, data, mdats)
  samples
}

///|
fn mp4_parse_trex(data : Array[Byte], box : Mp4Box) -> Mp4Trex raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || flags != 0 || box.end - box.payload != 24 {
    raise mp4_error(InvalidData, "Invalid trex box")
  }
  {
    track_id: mp4_u32_wide(data, box.payload + 4, box.end),
    description_index: mp4_u32(data, box.payload + 8, box.end),
    duration: mp4_u32_wide(data, box.payload + 12, box.end),
    size: mp4_u32(data, box.payload + 16, box.end),
    flags: mp4_u32(data, box.payload + 20, box.end),
  }
}

///|
fn mp4_parse_tfhd(data : Array[Byte], box : Mp4Box) -> Mp4Tfhd raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version != 0 || (flags & 0xFCFFC4) != 0 {
    raise mp4_error(Unsupported, "Unsupported tfhd flags")
  }
  if (flags & 0x010000) != 0 {
    raise mp4_error(Unsupported, "tfhd duration-is-empty is unsupported")
  }
  let mut p = box.payload + 4
  let track_id = mp4_u32_wide(data, p, box.end)
  p += 4
  let base = if (flags & 1) != 0 {
    let x = mp4_u64(data, p, box.end)
    p += 8
    Some(x)
  } else {
    None
  }
  let desc = if (flags & 2) != 0 {
    let x = mp4_u32(data, p, box.end)
    p += 4
    x
  } else {
    1
  }
  let duration = if (flags & 8) != 0 {
    let x = mp4_u32_wide(data, p, box.end)
    p += 4
    Some(x)
  } else {
    None
  }
  let size = if (flags & 16) != 0 {
    let x = mp4_u32(data, p, box.end)
    p += 4
    Some(x)
  } else {
    None
  }
  let sample_flags = if (flags & 32) != 0 {
    let x = mp4_u32(data, p, box.end)
    p += 4
    Some(x)
  } else {
    None
  }
  if p != box.end || desc <= 0 {
    raise mp4_error(InvalidData, "Invalid tfhd payload")
  }
  {
    track_id,
    flags,
    base_offset: base,
    description_index: desc,
    duration,
    size,
    sample_flags,
  }
}

///|
fn mp4_parse_tfdt(data : Array[Byte], box : Mp4Box) -> Int64 raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  let expected = if version == 0 { 8 } else if version == 1 { 12 } else { -1 }
  if flags != 0 || box.end - box.payload != expected {
    raise mp4_error(InvalidData, "Invalid tfdt box")
  }
  if version == 0 {
    mp4_u32_wide(data, box.payload + 4, box.end)
  } else {
    mp4_u64(data, box.payload + 4, box.end)
  }
}

///|
fn mp4_sample_flags_sync(flags : Int) -> Bool {
  (flags & 0x10000) == 0
}

///|
fn mp4_parse_trun(
  data : Array[Byte],
  box : Mp4Box,
  tfhd : Mp4Tfhd,
  trex : Mp4Trex?,
  state : Mp4FragmentState,
  base : Int64,
  next_data_offset : Int64?,
  max_frames : Int,
  out : Array[Mp4Sample],
) -> Int64 raise Mp4Error {
  let (version, flags) = mp4_fullbox(data, box)
  if version > 1 || (flags & 0xFFF0FA) != 0 {
    raise mp4_error(Unsupported, "Unsupported trun flags")
  }
  let count = mp4_u32(data, box.payload + 4, box.end)
  if count > max_frames - out.length() {
    raise mp4_error(LimitExceeded, "MP4 sample count exceeds max_frames")
  }
  if count > data.length() {
    raise mp4_error(
      LimitExceeded,
      "MP4 fragment sample count exceeds the input bound",
    )
  }
  let mut p = box.payload + 8
  let data_offset = if (flags & 1) != 0 {
    let value = mp4_i32(data, p, box.end)
    p += 4
    Some(value)
  } else {
    None
  }
  let first_flags = if (flags & 4) != 0 {
    let value = mp4_u32(data, p, box.end)
    p += 4
    Some(value)
  } else {
    None
  }
  let mut sample_offset = match data_offset {
    Some(value) => mp4_add(base, value)
    None =>
      match next_data_offset {
        Some(value) => value
        None => base
      }
  }
  let mut dts = state.next_decode_time
  for i in 0.. value
        (None, Some(value)) => value.duration
        _ => 0L
      }
    }
    let size = if (flags & 0x200) != 0 {
      let value = mp4_u32(data, p, box.end)
      p += 4
      value
    } else {
      match (tfhd.size, trex) {
        (Some(value), _) => value
        (None, Some(value)) => value.size
        _ => 0
      }
    }
    let sample_flags = if (flags & 0x400) != 0 {
      let value = mp4_u32(data, p, box.end)
      p += 4
      Some(value)
    } else if i == 0 && first_flags is Some(_) {
      first_flags
    } else {
      match (tfhd.sample_flags, trex) {
        (Some(value), _) => Some(value)
        (None, Some(value)) => Some(value.flags)
        _ => None
      }
    }
    let composition = if (flags & 0x800) != 0 {
      if version == 1 {
        mp4_i32(data, p, box.end)
      } else {
        mp4_u32_wide(data, p, box.end)
      }
    } else {
      0L
    }
    if (flags & 0x800) != 0 {
      p += 4
    }
    if duration <= 0L {
      raise mp4_error(InvalidData, "trun sample has no positive duration")
    }
    let pts = mp4_add(dts, composition)
    if size <= 0 || sample_offset < 0L {
      raise mp4_error(InvalidData, "trun sample value overflows")
    }
    out.push({
      offset: sample_offset,
      size,
      dts,
      pts,
      duration,
      sync: sample_flags.map(mp4_sample_flags_sync),
    })
    sample_offset = mp4_add(sample_offset, size.to_int64())
    dts = mp4_add(dts, duration)
  }
  if p != box.end {
    raise mp4_error(InvalidData, "trun has trailing bytes")
  }
  state.next_decode_time = dts
  sample_offset
}

///|
/// Expand one moof into absolute sample descriptors. `state` is shared by the
/// caller across moofs so omitted tfdt boxes continue the decode timeline.
/// `moof_file_offset` is absolute even when `data` is a scratch moof buffer.
fn mp4_fragment_moof_samples(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moof : Mp4Box,
  track : Mp4Track,
  trex : Mp4Trex?,
  state : Mp4FragmentState,
  moof_file_offset : Int64,
  limits : DecodeLimits,
  out : Array[Mp4Sample],
) -> Unit raise Mp4Error {
  let mi = mp4_index(boxes, moof.start)
  let trafs = mp4_children(boxes, mi, mp4_traf)
  let mut previous_traf_end : Int64? = None
  let mut prior_traf_seen = false
  for traf in trafs {
    let ti = mp4_index(boxes, traf.start)
    let tfhd_box = match mp4_child(boxes, ti, mp4_tfhd) {
      Some(value) => value
      None => raise mp4_error(InvalidData, "traf has no tfhd")
    }
    let tfhd = mp4_parse_tfhd(data, tfhd_box)
    let had_prior_traf = prior_traf_seen
    prior_traf_seen = true
    if tfhd.track_id != track.id {
      previous_traf_end = None
      continue
    }
    if tfhd.description_index != 1 {
      raise mp4_error(Unsupported, "Fragment switches sample descriptions")
    }
    let implicit_base = tfhd.base_offset is None && (tfhd.flags & 0x020000) == 0
    if implicit_base && had_prior_traf && previous_traf_end is None {
      raise mp4_error(
        Unsupported,
        "Implicit fragment base after an unselected traf is unsupported",
      )
    }
    let tfdt_box = mp4_child(boxes, ti, mp4_tfdt_box)
    match tfdt_box {
      Some(value) => state.next_decode_time = mp4_parse_tfdt(data, value)
      None => ()
    }
    let truns = mp4_children(boxes, ti, mp4_trun)
    if truns.length() == 0 {
      raise mp4_error(InvalidData, "traf has no trun")
    }
    // Explicit tfhd bases are already absolute file offsets. The
    // default-base-is-moof flag is absolute too; only the legacy implicit
    // base chains from the selected traf's preceding run.
    let base = match tfhd.base_offset {
      Some(value) => value
      None =>
        if (tfhd.flags & 0x020000) != 0 {
          moof_file_offset
        } else {
          match previous_traf_end {
            Some(value) => value
            None => moof_file_offset
          }
        }
    }
    let mut next_data_offset : Int64? = None
    for trun in truns {
      next_data_offset = Some(
        mp4_parse_trun(
          data,
          trun,
          tfhd,
          trex,
          state,
          base,
          next_data_offset,
          limits.max_frames,
          out,
        ),
      )
    }
    // next_data_offset is deliberately reset for every traf, while the
    // decode-time state above persists over all selected trafs/moofs.
    previous_traf_end = next_data_offset
  }
}

///|
fn mp4_fragment_samples(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moofs : Array[Mp4Box],
  track : Mp4Track,
  trex : Mp4Trex?,
  limits : DecodeLimits,
  mdats : Array[Mp4Mdat],
) -> Array[Mp4Sample] raise Mp4Error {
  let out : Array[Mp4Sample] = []
  let state = { next_decode_time: 0L, }
  for moof in moofs {
    mp4_fragment_moof_samples(
      data,
      boxes,
      moof,
      track,
      trex,
      state,
      moof.start.to_int64(),
      limits,
      out,
    )
  }
  if out.length() == 0 {
    raise mp4_error(InvalidData, "Selected fragmented track has no samples")
  }
  mp4_validate_mdat(out, data, mdats)
  out
}

///|
fn mp4_find_trex(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moov : Mp4Box,
  track_id : Int64,
) -> Mp4Trex? raise Mp4Error {
  let mi = mp4_index(boxes, moov.start)
  let mvex = match mp4_child(boxes, mi, mp4_mvex) {
    Some(value) => value
    None => return None
  }
  let vi = mp4_index(boxes, mvex.start)
  for box in mp4_children(boxes, vi, mp4_trex) {
    let trex = mp4_parse_trex(data, box)
    if trex.track_id == track_id {
      if trex.description_index != 1 {
        raise mp4_error(
          Unsupported,
          "Fragment trex switches sample descriptions",
        )
      }
      return Some(trex)
    }
  }
  None
}

///|
fn mp4_has_fragment_track(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moofs : Array[Mp4Box],
  track_id : Int64,
) -> Bool raise Mp4Error {
  for moof in moofs {
    let mi = mp4_index(boxes, moof.start)
    for traf in mp4_children(boxes, mi, mp4_traf) {
      let ti = mp4_index(boxes, traf.start)
      let tfhd_box = match mp4_child(boxes, ti, mp4_tfhd) {
        Some(value) => value
        None => continue
      }
      let tfhd = mp4_parse_tfhd(data, tfhd_box)
      if tfhd.track_id == track_id {
        return true
      }
    }
  }
  false
}

///|
fn mp4_track_samples(
  data : Array[Byte],
  boxes : Array[Mp4Box],
  moov : Mp4Box,
  track : Mp4Track,
  limits : DecodeLimits,
  mdats : Array[Mp4Mdat],
) -> Array[Mp4Sample] raise Mp4Error {
  let moofs : Array[Mp4Box] = []
  for box in boxes {
    if box.parent == -1 && box.typ == mp4_moof {
      moofs.push(box)
    }
  }
  let has_fragment = mp4_has_fragment_track(data, boxes, moofs, track.id)
  if has_fragment {
    if track.has_sample_tables {
      let table_samples = mp4_table_samples(data, boxes, track, limits, mdats)
      if table_samples.length() > 0 {
        raise mp4_error(
          Unsupported,
          "Mixed ordinary and fragmented samples are unsupported",
        )
      }
    }
    let trex = mp4_find_trex(data, boxes, moov, track.id)
    mp4_fragment_samples(data, boxes, moofs, track, trex, limits, mdats)
  } else {
    mp4_table_samples(data, boxes, track, limits, mdats)
  }
}

///|
/// Demux an AV1 `av01` track from an ISO BMFF/MP4 byte buffer.
///
/// Samples are returned in decode order. `presentation_timestamp` and
/// `decode_timestamp` are raw media timestamps in `timescale` units; no
/// movie-level edit-list offset is applied. Selected-track edits are exposed
/// in the result; encryption, external data references, or sample-entry
/// switches return `Unsupported`.
pub fn av1_demux_mp4(
  data : Array[Byte],
  track_id? : Int64? = None,
  limits? : DecodeLimits = DecodeLimits::default(),
) -> Result[Av1DemuxedTrack, DecodeError] {
  match av1_demux_validate_input(data, limits) {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let boxes = match mp4_boxes(data) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let moov = match mp4_child(boxes, -1, mp4_moov) {
    Some(value) => value
    None => return Err(decode_error(InvalidData, "mp4", "MP4 has no moov box"))
  }
  let mdats = mp4_find_mdat(boxes)
  if mdats.length() == 0 {
    return Err(decode_error(InvalidData, "mp4", "MP4 has no mdat box"))
  }
  let movie_timescale = mp4_parse_movie_timescale(data, boxes, moov) catch {
    Mp4Error::Diagnostic(error) => return Err(error)
  }
  let moovi = mp4_index(boxes, moov.start)
  let tracks = mp4_children(boxes, moovi, mp4_trak)
  let mut selected : Mp4Track? = None
  for trak in tracks {
    let candidate = mp4_parse_track(
      data, boxes, trak, movie_timescale, track_id,
    ) catch {
      Mp4Error::Diagnostic(error) => return Err(error)
    }
    match candidate {
      None => ()
      Some(track) =>
        match track_id {
          Some(wanted) =>
            if track.id == wanted {
              selected = Some(track)
              break
            }
          None => {
            selected = Some(track)
            break
          }
        }
    }
  }
  let track = match selected {
    Some(value) => value
    None =>
      return Err(
        decode_error(Unsupported, "mp4", "No selected AV1 video track"),
      )
  }
  if track.width > limits.max_frame_pixels / track.height {
    return Err(
      decode_error(
        LimitExceeded,
        "mp4",
        "Track dimensions exceed max_frame_pixels",
      ),
    )
  }
  let samples = mp4_track_samples(data, boxes, moov, track, limits, mdats) catch {
    Mp4Error::Diagnostic(error) => return Err(error)
  }
  if samples.length() > limits.max_frames {
    return Err(
      decode_error(LimitExceeded, "mp4", "MP4 sample count exceeds max_frames"),
    )
  }
  let mut sample_bytes = 0
  for sample in samples {
    if sample.size > limits.max_input_bytes - sample_bytes {
      return Err(
        decode_error(
          LimitExceeded,
          "mp4",
          "Copied sample payloads exceed max_input_bytes",
        ),
      )
    }
    sample_bytes += sample.size
  }
  let packets : Array[Av1Packet] = []
  for sample in samples {
    let start = sample.offset.to_int()
    packets.push({
      data: Array::makei(sample.size, i => data[start + i]),
      presentation_timestamp: Some(sample.pts),
      decode_timestamp: Some(sample.dts),
      duration: Some(sample.duration),
      sync_sample: sample.sync,
    })
  }
  Ok({
    format: Mp4,
    track_id: track.id,
    width: track.width,
    height: track.height,
    timescale: track.timescale,
    movie_timescale: track.movie_timescale,
    edits: track.edits,
    codec_initialization: Array::makei(track.av1c.length(), i => track.av1c[i]),
    packets,
  })
}