1# Parsers/generators for QuickTime media descriptions
2import struct
3
4Error = 'MediaDescr.Error'
5
6class _MediaDescriptionCodec:
7    def __init__(self, trunc, size, names, fmt):
8        self.trunc = trunc
9        self.size = size
10        self.names = names
11        self.fmt = fmt
12
13    def decode(self, data):
14        if self.trunc:
15            data = data[:self.size]
16        values = struct.unpack(self.fmt, data)
17        if len(values) != len(self.names):
18            raise Error, ('Format length does not match number of names')
19        rv = {}
20        for i in range(len(values)):
21            name = self.names[i]
22            value = values[i]
23            if type(name) == type(()):
24                name, cod, dec = name
25                value = dec(value)
26            rv[name] = value
27        return rv
28
29    def encode(self, dict):
30        list = [self.fmt]
31        for name in self.names:
32            if type(name) == type(()):
33                name, cod, dec = name
34            else:
35                cod = dec = None
36            value = dict[name]
37            if cod:
38                value = cod(value)
39            list.append(value)
40        rv = struct.pack(*list)
41        return rv
42
43# Helper functions
44def _tofixed(float):
45    hi = int(float)
46    lo = int(float*0x10000) & 0xffff
47    return (hi<<16)|lo
48
49def _fromfixed(fixed):
50    hi = (fixed >> 16) & 0xffff
51    lo = (fixed & 0xffff)
52    return hi + (lo / float(0x10000))
53
54def _tostr31(str):
55    return chr(len(str)) + str + '\0'*(31-len(str))
56
57def _fromstr31(str31):
58    return str31[1:1+ord(str31[0])]
59
60SampleDescription = _MediaDescriptionCodec(
61        1,      # May be longer, truncate
62        16,     # size
63        ('descSize', 'dataFormat', 'resvd1', 'resvd2', 'dataRefIndex'), # Attributes
64        "l4slhh"        # Format
65)
66
67SoundDescription = _MediaDescriptionCodec(
68        1,
69        36,
70        ('descSize', 'dataFormat', 'resvd1', 'resvd2', 'dataRefIndex',
71        'version', 'revlevel', 'vendor', 'numChannels', 'sampleSize',
72        'compressionID', 'packetSize', ('sampleRate', _tofixed, _fromfixed)),
73        "l4slhhhh4shhhhl"       # Format
74)
75
76SoundDescriptionV1 = _MediaDescriptionCodec(
77        1,
78        52,
79        ('descSize', 'dataFormat', 'resvd1', 'resvd2', 'dataRefIndex',
80        'version', 'revlevel', 'vendor', 'numChannels', 'sampleSize',
81        'compressionID', 'packetSize', ('sampleRate', _tofixed, _fromfixed), 'samplesPerPacket',
82        'bytesPerPacket', 'bytesPerFrame', 'bytesPerSample'),
83        "l4slhhhh4shhhhlllll"   # Format
84)
85
86ImageDescription = _MediaDescriptionCodec(
87        1,      # May be longer, truncate
88        86,     # size
89        ('idSize', 'cType', 'resvd1', 'resvd2', 'dataRefIndex', 'version',
90         'revisionLevel', 'vendor', 'temporalQuality', 'spatialQuality',
91         'width', 'height', ('hRes', _tofixed, _fromfixed), ('vRes', _tofixed, _fromfixed),
92        'dataSize', 'frameCount', ('name', _tostr31, _fromstr31),
93         'depth', 'clutID'),
94        'l4slhhhh4sllhhlllh32shh',
95)
96
97# XXXX Others, like TextDescription and such, remain to be done.
98