Skip to content

Instantly share code, notes, and snippets.

@rvanlaar
Created April 8, 2023 21:05
Show Gist options
  • Save rvanlaar/4f69b87b0f0af8efef83157cd43b4121 to your computer and use it in GitHub Desktop.
Save rvanlaar/4f69b87b0f0af8efef83157cd43b4121 to your computer and use it in GitHub Desktop.

Revisions

  1. rvanlaar renamed this gist Apr 8, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. rvanlaar created this gist Apr 8, 2023.
    522 changes: 522 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,522 @@
    #!/usr/bin/env python3

    """MrCrowbar file for Quicktime movies
    Specifiation is from: QuickTime File Format Specification
    https://multimedia.cx/mirror/qtff-2007-09-04.pdf
    All links in this file with #//apple... point to the relevant part in this document.
    """
    import argparse
    from pathlib import Path

    from mrcrowbar import models as mrc
    from mrcrowbar.utils import from_uint32_be as FourCC
    from mrcrowbar.utils import to_uint32_be as FourCCB

    import struct


    class AppleFloatField(mrc.Field):
    def __init__(self, offset):
    default = []
    super().__init__(default=default)
    self.offset = offset

    def get_from_buffer(self, buffer, parent=None):
    offset = self.offset
    a = struct.unpack(">h", buffer[offset : offset + 2])[0]
    # TODO: is b signed or unsigned?
    b = struct.unpack(">H", buffer[offset + 2 : offset + 4])[0]
    return a + b / 65536


    class ContainerAtom(mrc.Block):
    atoms = mrc.ChunkField(
    mrc.Ref("CHUNK_MAP"),
    0x00,
    id_field=mrc.UInt32_BE,
    length_field=mrc.UInt32_BE,
    default_klass=mrc.Unknown,
    length_before_id=True,
    length_inclusive=True,
    )


    UNKNOWN_FOURCC = set()


    def print_unkown_fourccs():
    if UNKNOWN_FOURCC:
    print("Unknown FourCCs:")
    for el in UNKNOWN_FOURCC:
    print(el)


    def create_knowntype(key, base_class=mrc.Unknown):
    fourcc = make_fourcc(key)
    UNKNOWN_FOURCC.add(fourcc)
    return type(f"{fourcc}-Unknown", (base_class,), {})


    def make_fourcc(key):
    if not isinstance(key, int):
    return False
    try:
    return FourCCB(key).decode()
    except UnicodeDecodeError:
    return False


    class mrcdict(dict):
    def __getitem__(self, key):
    retval = super().__getitem__(key)
    return retval

    def __contains__(self, key):
    if make_fourcc(key):
    return True

    retval = super().__contains__(key)
    return retval

    def __missing__(self, key):
    return create_knowntype(key)


    # fmt: off
    class mvhdAtom(mrc.Block):
    """
    MoVieHeaDer
    Specifies characteristics of the entire QuickTime movie.
    #//apple_ref/doc/uid/TP40000939-CH204-BBCGFGJG
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)

    # time in seconds since midnight January 1, 1904
    creation_time = mrc.UInt32_BE(0x04)
    modification_time = mrc.UInt32_BE(0x08)
    # the number of time units that pass per second
    time_scale = mrc.UInt32_BE(0x0C)
    # duration of the movie in timescale units
    duration = mrc.UInt32_BE(0x10)
    # fixed point number, a value of 1.0 indicates normal rate
    preferred_rate = mrc.UInt32_BE(0x14)
    # fixed point number, a value of 1.0 indicates full volume
    preferred_volume = mrc.UInt16_BE(0x18)
    reserved = mrc.Bytes(0x1A, length=10)
    matrix_structure = mrc.Bytes(0x24, length=36)
    preview_time = mrc.UInt32_BE(0x48)
    preview_duration = mrc.UInt32_BE(0x4C)
    poster_time = mrc.UInt32_BE(0x50)
    selection_time = mrc.UInt32_BE(0x54)
    selection_duration = mrc.UInt32_BE(0x58)
    current_time = mrc.UInt32_BE(0x5C)
    next_track_id = mrc.UInt32_BE(0x60)


    class tkhdAtom(mrc.Block):
    """
    TracKHeaDer
    #//apple_ref/doc/uid/TP40000939-CH204-BBCEIDFA
    """
    version = mrc.UInt8(0x0)
    flags = mrc.Bytes(0x1, length=3)
    creation_time = mrc.UInt32_BE(0x4)
    modification_time = mrc.UInt32_BE(0x8)
    track_id = mrc.UInt32_BE(0xc)
    reserved = mrc.UInt32_BE(0x10)
    duration = mrc.UInt32_BE(0x14)
    reserved = mrc.Bytes(0x18, length=8)
    layer = mrc.UInt16_BE(0x20)
    alternate_group = mrc.UInt16_BE(0x22)
    volume = mrc.UInt16_BE(0x24)
    reserved = mrc.UInt16_BE(0x26)
    matrix_structure = mrc.Bytes(0x28, length=36)
    track_width = mrc.UInt32_BE(0x4c)
    track_height = mrc.UInt32_BE(0x50)


    class mdatAtom(mrc.Block):
    data = mrc.Bytes()


    class mdhdAtom(mrc.Block):
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    creation_time = mrc.UInt32_BE(0x04)
    modification_time = mrc.UInt32_BE(0x08)
    time_scale = mrc.UInt32_BE(0x0C)
    duration = mrc.UInt32_BE(0x10)
    language = mrc.UInt16_BE(0x14)
    quality = mrc.UInt16_BE(0x16)


    class hdlrAtom(mrc.Block):
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    component_type = mrc.UInt32_BE(0x04)
    component_subtype = mrc.UInt32_BE(0x08)
    component_manufacturer = mrc.UInt32_BE(0x0C)
    component_flags = mrc.UInt32_BE(0x10)
    component_flags_mask = mrc.UInt32_BE(0x14)
    component_name = mrc.Bytes(0x18)

    @property
    def repr(self):
    ct = FourCCB(self.component_type)
    cst = FourCCB(self.component_subtype)
    cm = FourCCB(self.component_manufacturer)
    cf = FourCCB(self.component_flags)
    cfm = FourCCB(self.component_flags_mask)
    # component name is a pascal string
    cn = self.component_name[1:].decode()
    return (f"Version={self.version}, flags={self.flags}, "
    f"Component Type={ct}, component SubType={cst}, "
    f"Component Manufacturer={cm}, Component Flags={cf}. "
    f"Component Flags Mask={cfm}, Component Name={cn}")

    class smhdAtom(mrc.Block):
    """
    SoundMedia HeaDer
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    balance = mrc.UInt16_BE(0x04)
    reserved = mrc.UInt16_BE(0x06)


    class vmhdAtom(mrc.Block):
    """
    VideoMedia HeaDer
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    graphics_mode = mrc.UInt16_BE(0x04)
    opcolor = mrc.Bytes(0x06, length=6)


    class EditListTableEntry(mrc.Block):
    track_duration = mrc.Int32_BE(0x00)
    media_time = mrc.Int32_BE(0x04)
    media_rate = mrc.Int32_BE(0x08)


    class elstAtom(mrc.Block):
    """
    EditLiST
    """

    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    edit_list_table = mrc.BlockField(EditListTableEntry, 0x08, count=mrc.Ref("number_of_entries"))


    class drefSubAtom(mrc.Block):
    version = mrc.UInt32_BE(0x00)
    flags = mrc.Bytes(0x04, length=3)
    data = mrc.Bytes(0x07)


    class drefAtom(mrc.Block):
    """
    Data REFerance Atom
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"alis"): drefSubAtom,
    FourCC(b"rsrc"): drefSubAtom,
    FourCC(b"url "): drefSubAtom
    }
    CHUNK_MAP.update(MAPPING)

    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    atoms = mrc.ChunkField(
    mrc.Ref("CHUNK_MAP"),
    0x08,
    id_field=mrc.UInt32_BE,
    length_field=mrc.UInt32_BE,
    default_klass=drefSubAtom,
    length_before_id=True,
    length_inclusive=True,
    )


    class stcoAtom(mrc.Block):
    """
    Chunk Offset
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    chunk_offset_table = mrc.Bytes(0x08)


    class stsdAtom(mrc.Block):
    """
    Sample description
    ## todo: check if data_format is in sample_description table and contains qtvr
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    sample_description_table = mrc.Bytes(0x08)

    class stscAtom(mrc.Block):
    """
    SampleTable Sample to Chunk
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    sample_to_chunk_table = mrc.Bytes(0x08)

    class sttsAtom(mrc.Block):
    """
    SampleTable Time to Sample
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    time_to_sample_table = mrc.Bytes(0x08)

    class stssAtom(mrc.Block):
    """
    SampleTable SyncSample
    Identifies the key frames
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    number_of_entries = mrc.UInt32_BE(0x04)
    sync_sample_table = mrc.Bytes(0x08)

    class stszAtom(mrc.Block):
    """
    SampleTable Sample siZe
    """
    version = mrc.UInt8(0x00)
    flags = mrc.Bytes(0x01, length=3)
    sample_size = mrc.UInt32_BE(0x04)
    number_of_entries = mrc.UInt32_BE(0x08)
    sample_size_table = mrc.Bytes(0x0C)

    class NAVGAtom(mrc.Block):
    version = mrc.UInt16_BE(0x00)
    columns = mrc.UInt16_BE(0x02)
    rows = mrc.UInt16_BE(0x04)
    reserved = mrc.UInt16_BE(0x06)
    loop_frames = mrc.UInt16_BE(0x08)
    loop_dur = mrc.UInt16_BE(0x0A)
    movietype = mrc.UInt16_BE(0x0C)
    loop_timescale = mrc.UInt16_BE(0x0E)
    fieldofview = AppleFloatField(0x10)
    startHPan = AppleFloatField(0x14)
    endHPan = AppleFloatField(0x18)
    endVTilt = AppleFloatField(0x1C)
    startVTilt = AppleFloatField(0x20)
    initialHPan = AppleFloatField(0x24)
    #initialVTiltMajor = mrc.UInt16_BE(0x28)
    #initialVPanComp = mrc.Int16_BE(0x2A)
    initialVTilt = AppleFloatField(0x28)
    reserved2 = mrc.UInt32_BE(0x2C)


    class gmhdAtom(mrc.Block):
    """
    base Media inforation HeaDer
    Indicates that this media information atom pertains to a base media
    """
    pass

    # fmt: on


    class dinfAtom(ContainerAtom):
    """
    DataINFormation
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"dref"): drefAtom,
    }
    CHUNK_MAP.update(MAPPING)


    class stblAtom(ContainerAtom):
    """
    Sample TaBLE
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"stco"): stcoAtom,
    FourCC(b"stsc"): stsdAtom,
    FourCC(b"stsd"): stsdAtom,
    FourCC(b"stts"): sttsAtom,
    FourCC(b"stsz"): stszAtom,
    FourCC(b"stss"): stssAtom,
    }

    CHUNK_MAP.update(MAPPING)


    class minfAtom(ContainerAtom):
    """
    MedINFormation
    Store handler-specific information for a track's media data.
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"hdlr"): hdlrAtom,
    FourCC(b"dinf"): dinfAtom,
    FourCC(b"stbl"): stblAtom,
    FourCC(b"smhd"): smhdAtom,
    FourCC(b"vmhd"): vmhdAtom,
    FourCC(b"gmhd"): gmhdAtom,
    }
    CHUNK_MAP.update(MAPPING)


    class edtsAtom(ContainerAtom):
    CHUNK_MAP = mrcdict()
    MAPPING = {FourCC(b"elst"): elstAtom}
    CHUNK_MAP.update(MAPPING)


    class mdiaAtom(ContainerAtom):
    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"mdhd"): mdhdAtom,
    FourCC(b"hdlr"): hdlrAtom,
    FourCC(b"minf"): minfAtom,
    }
    CHUNK_MAP.update(MAPPING)


    class trakAtom(ContainerAtom):
    """
    Track Atom
    Defines a single track of a movie.
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"mdia"): mdiaAtom,
    FourCC(b"tkhd"): tkhdAtom,
    FourCC(b"edts"): edtsAtom,
    }
    CHUNK_MAP.update(MAPPING)


    class ctypAtom(mrc.Block):
    """
    Controller TYPe
    """

    id = mrc.UInt32_BE(0x00)

    @property
    def repr(self):
    _id = FourCCB(self.id).decode()
    return f"id={_id}"


    class WLOCAtom(mrc.Block):
    """
    Window LOCation
    """

    x = mrc.UInt16_BE(0x00)
    y = mrc.UInt16_BE(0x02)


    class udtaAtom(mrc.Block):
    """
    User DaTA Atom
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"ctyp"): ctypAtom,
    FourCC(b"WLOC"): WLOCAtom,
    FourCC(b"NAVG"): NAVGAtom,
    }
    CHUNK_MAP.update(MAPPING)

    atoms = mrc.ChunkField(
    mrc.Ref("CHUNK_MAP"),
    0x00,
    id_field=mrc.UInt32_BE,
    length_field=mrc.UInt32_BE,
    default_klass=mrc.Unknown,
    length_before_id=True,
    length_inclusive=True,
    stream_end=b"\x00\x00\x00\x00",
    )
    # For historical reasons, the data list is optionally terminated by a 32-bit integer set to 0


    class moovAtom(ContainerAtom):
    """
    moov
    #//apple_ref/doc/uid/TP40000939-CH204-55911
    """

    CHUNK_MAP = mrcdict()
    MAPPING = {
    FourCC(b"mvhd"): mvhdAtom,
    FourCC(b"trak"): trakAtom,
    FourCC(b"udta"): udtaAtom,
    }
    CHUNK_MAP.update(MAPPING)


    class QuickTime(ContainerAtom):
    CHUNK_MAP = mrcdict()
    MAPPING = {FourCC(b"mdat"): mdatAtom, FourCC(b"moov"): moovAtom}
    CHUNK_MAP.update(MAPPING)


    def print_atom(atom, indent=0):
    indent_str = ">" * indent
    print(f"{indent_str}{atom}")
    if hasattr(atom, "atoms"):
    for el in atom.atoms:
    print_atom(el, indent + 1)
    if hasattr(atom, "obj") and hasattr(atom.obj, "atoms"):
    for el in atom.obj.atoms:
    print_atom(el, indent + 1)


    def parse_file(filename):
    f = open(filename, "rb").read()
    return QuickTime(f)


    def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("filename", metavar="FILE", type=Path, help="QT Movie filename")
    args = parser.parse_args()
    qt = parse_file(args.filename)
    print_atom(qt)
    print_unkown_fourccs()


    if __name__ == "__main__":
    main()