Table of Contents

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.