ISO BMFF boxes
MP4, QuickTime, HEIF, HEIC and AVIF files are made of boxes (ISO/IEC 14496-12). Egami reads AVIF and HEIC itself; for other
work on these files, such as reading an MP4's tracks or a tag Egami does not keep, IsoBoxReader walks the boxes for you. It
reads one level at a time from bytes in memory, allocates nothing, and checks every size: a box that runs past its parent, or
one too small for its header, raises a CorruptDataException that names the format you gave and the box's offset.
// List an AVIF, HEIC or MP4 file's top-level boxes and the boxes inside meta.
IsoBoxReader top = new(avif, "AVIF");
while (top.Next(out IsoBox box))
{
found.Add($"{box.Type} {box.Size}");
if (box.Type == FourCC.Parse("meta"))
{
// meta is a full box: its version and flags (4 bytes) come before its children.
IsoBoxReader children = top.Children(box, skip: 4);
while (children.Next(out IsoBox child))
{
found.Add($" {child.Type} at {child.Start}");
}
}
}
IsoBox gives each box's type, where it starts, where its contents start and where it ends. Box types are FourCC values:
FourCC.Parse("moov") makes one, and ToString prints it, with bytes outside printable ASCII escaped (\xa9nam).
Children(box, skip) walks the boxes inside one; skip passes the fields a box has before its children, such as a full box's
version and flags (4 bytes) or a sample description's entry count (8 bytes). Content(box) gives a box's bytes. Large sizes,
size 0 (to the end of the parent) and uuid boxes, whose extended type is IsoBox.UserType, are read too.
Large files
A file too large to read into memory is walked from a stream: IsoBoxReader.ReadHeader reads a box header from its first
IsoBoxReader.MaxHeaderSize bytes, so you can skip over media data and load only the boxes you need. A reader made with an
offset reports file offsets for the boxes inside the part you loaded.
// Walk a large file's top-level boxes without reading it into memory, then load only moov.
using FileStream stream = File.OpenRead("duck.avif");
byte[] header = new byte[IsoBoxReader.MaxHeaderSize];
for (long at = 0; at < stream.Length;)
{
stream.Position = at;
int read = stream.ReadAtLeast(header, (int)Math.Min(header.Length, stream.Length - at), throwOnEndOfStream: false);
IsoBox box = IsoBoxReader.ReadHeader(header.AsSpan(0, read), at, stream.Length, "MP4");
types.Add(box.Type);
if (box.Type == FourCC.Parse("moov"))
{
byte[] moov = new byte[box.ContentLength];
stream.Position = box.ContentStart;
stream.ReadExactly(moov);
IsoBoxReader inside = new(moov, "MP4", offset: box.ContentStart); // the boxes inside report file offsets
while (inside.Next(out IsoBox track))
{
types.Add(track.Type);
}
}
at = box.End;
}
Egami.Video reads MP4 and QuickTime files this way.