.mov file format of QuickTime, MP4 ISO 14496-14 media: Python parsing library

Application

QuickTime, MP4 ISO 14496-14 media

File extension

mov

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of .mov file format of QuickTime, MP4 ISO 14496-14 media using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for Python generated by Kaitai Struct depends on the Python runtime library. You have to install it before you can parse data.

The Python runtime library can be installed from PyPI:

python3 -m pip install kaitaistruct

Code

Parse a local file and get structure in memory:

data = QuicktimeMov.from_file("path/to/local/file.mov")

Or parse structure from a bytes:

from kaitaistruct import KaitaiStream, BytesIO

raw = b"\x00\x01\x02..."
data = QuicktimeMov(KaitaiStream(BytesIO(raw)))

After that, one can get various attributes from the structure by invoking getter methods like:

data.atoms # => get atoms

Python source code to parse .mov file format of QuickTime, MP4 ISO 14496-14 media

quicktime_mov.py

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
from enum import Enum


if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 9):
    raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))

class QuicktimeMov(KaitaiStruct):
    """
    .. seealso::
       Source - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-BBCGDDDF
    """

    class AtomType(Enum):
        xtra = 1484026465
        dinf = 1684631142
        dref = 1685218662
        edts = 1701082227
        elst = 1701606260
        free = 1718773093
        ftyp = 1718909296
        hdlr = 1751411826
        iods = 1768907891
        mdat = 1835295092
        mdhd = 1835296868
        mdia = 1835297121
        meta = 1835365473
        minf = 1835626086
        moof = 1836019558
        moov = 1836019574
        mvhd = 1836476516
        smhd = 1936549988
        stbl = 1937007212
        stco = 1937007471
        stsc = 1937011555
        stsd = 1937011556
        stsz = 1937011578
        stts = 1937011827
        tkhd = 1953196132
        traf = 1953653094
        trak = 1953653099
        tref = 1953654118
        udta = 1969517665
        vmhd = 1986881636

    class Brand(Enum):
        x_3g2a = 862401121
        x_3ge6 = 862414134
        x_3ge7 = 862414135
        x_3ge9 = 862414137
        x_3gf9 = 862414393
        x_3gg6 = 862414646
        x_3gg9 = 862414649
        x_3gh9 = 862414905
        x_3gm9 = 862416185
        x_3gma = 862416193
        x_3gp4 = 862416948
        x_3gp5 = 862416949
        x_3gp6 = 862416950
        x_3gp7 = 862416951
        x_3gp8 = 862416952
        x_3gp9 = 862416953
        x_3gr6 = 862417462
        x_3gr9 = 862417465
        x_3gs6 = 862417718
        x_3gs9 = 862417721
        x_3gt8 = 862417976
        x_3gt9 = 862417977
        x_3gtv = 862418038
        x_3gvr = 862418546
        x_3vra = 863400545
        x_3vrb = 863400546
        x_3vrm = 863400557
        arri = 1095914057
        caep = 1128351056
        cdes = 1128555891
        j2p0 = 1244811312
        j2p1 = 1244811313
        lcag = 1279476039
        m4a = 1295270176
        m4b = 1295270432
        m4p = 1295274016
        m4v = 1295275552
        ma1a = 1296118081
        ma1b = 1296118082
        mfsm = 1296454477
        mgsv = 1296520022
        mppi = 1297109065
        msnv = 1297305174
        miab = 1298743618
        miac = 1298743619
        mian = 1298743662
        mibu = 1298743925
        micm = 1298744173
        miha = 1298745409
        mihb = 1298745410
        mihe = 1298745413
        mipr = 1298747506
        ross = 1380930387
        seau = 1397047637
        sebk = 1397047883
        xavc = 1480676931
        adti = 1633973353
        aid3 = 1634296883
        av01 = 1635135537
        avc1 = 1635148593
        avci = 1635148649
        avcs = 1635148659
        avde = 1635148901
        avif = 1635150182
        avio = 1635150191
        avis = 1635150195
        bbxm = 1650620525
        ca4m = 1667314797
        ca4s = 1667314803
        caaa = 1667326305
        caac = 1667326307
        cabl = 1667326572
        cama = 1667329377
        camc = 1667329379
        caqv = 1667330422
        casu = 1667330933
        ccea = 1667458401
        ccff = 1667458662
        cdm1 = 1667525937
        cdm4 = 1667525940
        ceac = 1667588451
        cfhd = 1667655780
        cfsd = 1667658596
        chd1 = 1667785777
        chd2 = 1667785778
        chdf = 1667785830
        chev = 1667786102
        chh1 = 1667786801
        chhd = 1667786852
        cint = 1667853940
        clg1 = 1668048689
        clg2 = 1668048690
        cmf2 = 1668113970
        cmfc = 1668114019
        cmff = 1668114022
        cmfl = 1668114028
        cmfs = 1668114035
        cmhm = 1668114541
        cmhs = 1668114547
        comp = 1668246896
        csh1 = 1668507697
        cud1 = 1668637745
        cud2 = 1668637746
        cud8 = 1668637752
        cud9 = 1668637753
        cuvd = 1668642404
        cvid = 1668704612
        cvvc = 1668707939
        cwvt = 1668773492
        da0a = 1684090977
        da0b = 1684090978
        da1a = 1684091233
        da1b = 1684091234
        da2a = 1684091489
        da2b = 1684091490
        da3a = 1684091745
        da3b = 1684091746
        dash = 1684108136
        dby1 = 1684175153
        dmb1 = 1684890161
        dsms = 1685286259
        dts1 = 1685353265
        dts2 = 1685353266
        dts3 = 1685353267
        dv1a = 1685467489
        dv1b = 1685467490
        dv2a = 1685467745
        dv2b = 1685467746
        dv3a = 1685468001
        dv3b = 1685468002
        dvr1 = 1685484081
        dvt1 = 1685484593
        dxo = 1685614368
        emsg = 1701671783
        heic = 1751476579
        heim = 1751476589
        heis = 1751476595
        heix = 1751476600
        heoi = 1751478121
        hevc = 1751479907
        hevd = 1751479908
        hevi = 1751479913
        hevm = 1751479917
        hevs = 1751479923
        hevx = 1751479928
        hvce = 1752589157
        hvci = 1752589161
        hvcx = 1752589176
        hvti = 1752593513
        ifaa = 1768317281
        ifah = 1768317288
        ifai = 1768317289
        ifas = 1768317299
        ifau = 1768317301
        ifav = 1768317302
        ifhd = 1768319076
        ifhh = 1768319080
        ifhr = 1768319090
        ifhs = 1768319091
        ifhu = 1768319093
        ifhx = 1768319096
        ifrm = 1768321645
        ifsd = 1768321892
        im1i = 1768763753
        im1t = 1768763764
        im2i = 1768764009
        im2t = 1768764020
        isc2 = 1769169714
        iso2 = 1769172786
        iso3 = 1769172787
        iso4 = 1769172788
        iso5 = 1769172789
        iso6 = 1769172790
        iso7 = 1769172791
        iso8 = 1769172792
        iso9 = 1769172793
        isoa = 1769172833
        isob = 1769172834
        isoc = 1769172835
        isom = 1769172845
        j2is = 1781688691
        j2ki = 1781689193
        j2ks = 1781689203
        jp2 = 1785737760
        jpeg = 1785750887
        jpgs = 1785751411
        jpm = 1785752864
        jpoi = 1785753449
        jpsi = 1785754473
        jpx = 1785755680
        jpxb = 1785755746
        jxl = 1786276896
        jxs = 1786278688
        jxsc = 1786278755
        jxsi = 1786278761
        jxss = 1786278771
        lhte = 1818784869
        lhti = 1818784873
        lmsg = 1819112295
        miaf = 1835622758
        mif1 = 1835623985
        mif2 = 1835623986
        mj2s = 1835676275
        mjp2 = 1835692082
        mp21 = 1836069425
        mp41 = 1836069937
        mp42 = 1836069938
        mp71 = 1836070705
        mpuf = 1836086630
        msdh = 1836278888
        msf1 = 1836279345
        msix = 1836280184
        niko = 1852402543
        nlsl = 1852601196
        nras = 1852989811
        oa2d = 1868640868
        oabl = 1868653164
        odcf = 1868850022
        ompp = 1869443184
        opf2 = 1869637170
        opx2 = 1869641778
        ovdp = 1870029936
        ovly = 1870031993
        paff = 1885431398
        pana = 1885433441
        piff = 1885955686
        pmff = 1886217830
        pnvi = 1886287465
        pred = 1886545252
        qt = 1903435808
        relo = 1919249519
        risx = 1919513464
        sdv = 1935963680
        senv = 1936027254
        sims = 1936289139
        sisx = 1936290680
        siti = 1936290921
        sitv = 1936290934
        slh1 = 1936484401
        slh2 = 1936484402
        slh3 = 1936484403
        ssss = 1936946035
        ttml = 1953787244
        ttwv = 1953789814
        uhvi = 1969780329
        unif = 1970170214
        uvvu = 1970697845
        v3mp = 1983081840
        v3mt = 1983081844
        v3nt = 1983082100
        v3st = 1983083380
        vvci = 1987470185
        vvoi = 1987473257
        vwpt = 1987539060
        yt4 = 2037658656
    def __init__(self, _io, _parent=None, _root=None):
        self._io = _io
        self._parent = _parent
        self._root = _root if _root else self
        self._read()

    def _read(self):
        self.atoms = QuicktimeMov.AtomList(self._io, self, self._root)

    class MvhdBody(KaitaiStruct):
        """
        .. seealso::
           Source - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-BBCGFGJG
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.version = self._io.read_u1()
            self.flags = self._io.read_bytes(3)
            self.creation_time = self._io.read_u4be()
            self.modification_time = self._io.read_u4be()
            self.time_scale = self._io.read_u4be()
            self.duration = self._io.read_u4be()
            self.preferred_rate = QuicktimeMov.Fixed32(self._io, self, self._root)
            self.preferred_volume = QuicktimeMov.Fixed16(self._io, self, self._root)
            self.reserved1 = self._io.read_bytes(10)
            self.matrix = self._io.read_bytes(36)
            self.preview_time = self._io.read_u4be()
            self.preview_duration = self._io.read_u4be()
            self.poster_time = self._io.read_u4be()
            self.selection_time = self._io.read_u4be()
            self.selection_duration = self._io.read_u4be()
            self.current_time = self._io.read_u4be()
            self.next_track_id = self._io.read_u4be()


    class FtypBody(KaitaiStruct):
        """
        .. seealso::
           Source - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-CJBCBIFF
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.major_brand = KaitaiStream.resolve_enum(QuicktimeMov.Brand, self._io.read_u4be())
            self.minor_version = self._io.read_bytes(4)
            self.compatible_brands = []
            i = 0
            while not self._io.is_eof():
                self.compatible_brands.append(KaitaiStream.resolve_enum(QuicktimeMov.Brand, self._io.read_u4be()))
                i += 1



    class Fixed32(KaitaiStruct):
        """Fixed-point 32-bit number."""
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.int_part = self._io.read_s2be()
            self.frac_part = self._io.read_u2be()


    class Fixed16(KaitaiStruct):
        """Fixed-point 16-bit number."""
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.int_part = self._io.read_s1()
            self.frac_part = self._io.read_u1()


    class Atom(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.len32 = self._io.read_u4be()
            self.atom_type = KaitaiStream.resolve_enum(QuicktimeMov.AtomType, self._io.read_u4be())
            if self.len32 == 1:
                self.len64 = self._io.read_u8be()

            _on = self.atom_type
            if _on == QuicktimeMov.AtomType.moof:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.tkhd:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.TkhdBody(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.stbl:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.traf:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.minf:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.trak:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.moov:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.mdia:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.dinf:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.AtomList(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.mvhd:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.MvhdBody(_io__raw_body, self, self._root)
            elif _on == QuicktimeMov.AtomType.ftyp:
                self._raw_body = self._io.read_bytes(self.len)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = QuicktimeMov.FtypBody(_io__raw_body, self, self._root)
            else:
                self.body = self._io.read_bytes(self.len)

        @property
        def len(self):
            if hasattr(self, '_m_len'):
                return self._m_len

            self._m_len = ((self._io.size() - 8) if self.len32 == 0 else ((self.len64 - 16) if self.len32 == 1 else (self.len32 - 8)))
            return getattr(self, '_m_len', None)


    class TkhdBody(KaitaiStruct):
        """
        .. seealso::
           Source - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25550
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.version = self._io.read_u1()
            self.flags = self._io.read_bytes(3)
            self.creation_time = self._io.read_u4be()
            self.modification_time = self._io.read_u4be()
            self.track_id = self._io.read_u4be()
            self.reserved1 = self._io.read_bytes(4)
            self.duration = self._io.read_u4be()
            self.reserved2 = self._io.read_bytes(8)
            self.layer = self._io.read_u2be()
            self.alternative_group = self._io.read_u2be()
            self.volume = self._io.read_u2be()
            self.reserved3 = self._io.read_u2be()
            self.matrix = self._io.read_bytes(36)
            self.width = QuicktimeMov.Fixed32(self._io, self, self._root)
            self.height = QuicktimeMov.Fixed32(self._io, self, self._root)


    class AtomList(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.items = []
            i = 0
            while not self._io.is_eof():
                self.items.append(QuicktimeMov.Atom(self._io, self, self._root))
                i += 1