def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos:pos + 4]) name = data[pos + 12:pos + length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct.unpack(">I4s", data[pos:pos + 8]) if atom_name != b"data": raise MP4MetadataError("unexpected atom %r inside %r" % (atom_name, atom.name)) if length < 1: raise MP4MetadataError("atom %r has a length of zero" % atom.name) version = ord(data[pos + 8:pos + 8 + 1]) flags = struct.unpack(">I", b"\x00" + data[pos + 9:pos + 12])[0] value.append( MP4FreeForm(data[pos + 16:pos + length], dataformat=flags, version=version)) pos += length key = _name2key(atom.name + b":" + mean + b":" + name) self.__add(key, value)
def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos : pos + 4]) name = data[pos + 12 : pos + length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct.unpack(">I4s", data[pos : pos + 8]) if atom_name != b"data": raise MP4MetadataError( "unexpected atom %r inside %r" % (atom_name, atom.name) ) version = ord(data[pos + 8 : pos + 8 + 1]) flags = struct.unpack(">I", b"\x00" + data[pos + 9 : pos + 12])[0] value.append( MP4FreeForm( data[pos + 16 : pos + length], dataformat=flags, version=version ) ) pos += length key = _name2key(atom.name + b":" + mean + b":" + name) self.__add(key, value)
def __read_offsets(self, filename): fileobj = open(filename, 'rb') atoms = Atoms(fileobj) moov = atoms[b'moov'] samples = [] for atom in moov.findall(b'stco', True): fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = ">%dI" % cdata.uint_be(data[:4]) offsets = struct.unpack(fmt, data[4:]) for offset in offsets: fileobj.seek(offset) samples.append(fileobj.read(8)) for atom in moov.findall(b'co64', True): fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = ">%dQ" % cdata.uint_be(data[:4]) offsets = struct.unpack(fmt, data[4:]) for offset in offsets: fileobj.seek(offset) samples.append(fileobj.read(8)) try: for atom in atoms[b"moof"].findall(b'tfhd', True): data = fileobj.read(atom.length - 9) flags = cdata.uint_be(b"\x00" + data[:3]) if flags & 1: offset = cdata.ulonglong_be(data[7:15]) fileobj.seek(offset) samples.append(fileobj.read(8)) except KeyError: pass fileobj.close() return samples
def __read_offsets(self, filename): fileobj = open(filename, 'rb') atoms = Atoms(fileobj) moov = atoms['moov'] samples = [] for atom in moov.findall('stco', True): fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = ">%dI" % cdata.uint_be(data[:4]) offsets = struct.unpack(fmt, data[4:]) for offset in offsets: fileobj.seek(offset) samples.append(fileobj.read(8)) for atom in moov.findall('co64', True): fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = ">%dQ" % cdata.uint_be(data[:4]) offsets = struct.unpack(fmt, data[4:]) for offset in offsets: fileobj.seek(offset) samples.append(fileobj.read(8)) try: for atom in atoms["moof"].findall('tfhd', True): data = fileobj.read(atom.length - 9) flags = cdata.uint_be("\x00" + data[:3]) if flags & 1: offset = cdata.ulonglong_be(data[7:15]) fileobj.seek(offset) samples.append(fileobj.read(8)) except KeyError: pass fileobj.close() return samples
def __init__(self, atoms, fileobj): for trak in list(atoms["moov"].findall(b"trak")): hdlr = trak["mdia", "hdlr"] fileobj.seek(hdlr.offset) data = fileobj.read(hdlr.length) if data[16:20] == b"soun": break else: raise MP4StreamInfoError("track has no audio data") mdhd = trak["mdia", "mdhd"] fileobj.seek(mdhd.offset) data = bytearray(fileobj.read(mdhd.length)) if data[8] == 0: offset = 20 fmt = ">2I" else: offset = 28 fmt = ">IQ" end = offset + struct_calcsize(fmt) unit, length = struct_unpack(fmt, data[offset:end]) self.length = float(length) / unit try: atom = trak["mdia", "minf", "stbl", "stsd"] fileobj.seek(atom.offset) data = bytearray(fileobj.read(atom.length)) if data[20:24] == b"mp4a": length = cdata.uint_be(data[16:20]) (self.channels, self.bits_per_sample, _, self.sample_rate) = struct_unpack(">3HI", data[40:50]) # ES descriptor type if data[56:60] == b"esds" and ord(data[64:65]) == 0x03: pos = 65 # skip extended descriptor type tag, length, ES ID # and stream priority if data[pos:pos+3] == b"\x80\x80\x80": pos += 3 pos += 4 # decoder config descriptor type if data[pos] == 0x04: pos += 1 # skip extended descriptor type tag, length, # object type ID, stream type, buffer size # and maximum bitrate if data[pos:pos+3] == b"\x80\x80\x80": pos += 3 pos += 10 # average bitrate self.bitrate = cdata.uint_be(data[pos:pos+4]) except (ValueError, KeyError): # stsd atoms are optional pass
def test_uint(self): self.failUnlessEqual(cdata.uint_le(self.ZERO(4)), 0) self.failUnlessEqual(cdata.uint_le(self.LEONE(4)), 1) self.failUnlessEqual(cdata.uint_le(self.BEONE(4)), 2**32 >> 8) self.failUnlessEqual(cdata.uint_le(self.NEGONE(4)), 2**32 - 1) self.assertTrue(cdata.uint_le is cdata.uint32_le) self.failUnlessEqual(cdata.uint_be(self.ZERO(4)), 0) self.failUnlessEqual(cdata.uint_be(self.LEONE(4)), 2**32 >> 8) self.failUnlessEqual(cdata.uint_be(self.BEONE(4)), 1) self.failUnlessEqual(cdata.uint_be(self.NEGONE(4)), 2**32 - 1) self.assertTrue(cdata.uint_be is cdata.uint32_be)
def test_uint(self): self.failUnlessEqual(cdata.uint_le(self.ZERO(4)), 0) self.failUnlessEqual(cdata.uint_le(self.LEONE(4)), 1) self.failUnlessEqual(cdata.uint_le(self.BEONE(4)), 2 ** 32 >> 8) self.failUnlessEqual(cdata.uint_le(self.NEGONE(4)), 2 ** 32 - 1) self.assertTrue(cdata.uint_le is cdata.uint32_le) self.failUnlessEqual(cdata.uint_be(self.ZERO(4)), 0) self.failUnlessEqual(cdata.uint_be(self.LEONE(4)), 2 ** 32 >> 8) self.failUnlessEqual(cdata.uint_be(self.BEONE(4)), 1) self.failUnlessEqual(cdata.uint_be(self.NEGONE(4)), 2 ** 32 - 1) self.assertTrue(cdata.uint_be is cdata.uint32_be)
def __update_parents(self, fileobj, path, delta, offset): # Update all parent atoms with the new size. for atom in path: fileobj.seek(atom.offset + offset) size = cdata.uint_be(fileobj.read(4)) + delta fileobj.seek(atom.offset + offset) fileobj.write(cdata.to_uint_be(size))
def __init__(self, atoms, fileobj): hdlr = atoms["moov.trak.mdia.hdlr"] fileobj.seek(hdlr.offset) if "soun" not in fileobj.read(hdlr.length): raise M4AStreamInfoError("track has no audio data") mdhd = atoms["moov.trak.mdia.mdhd"] fileobj.seek(mdhd.offset) data = fileobj.read(mdhd.length) if ord(data[8]) == 0: offset = 20 fmt = ">2I" else: offset = 28 fmt = ">IQ" end = offset + struct.calcsize(fmt) unit, length = struct.unpack(fmt, data[offset:end]) self.length = float(length) / unit try: atom = atoms["moov.trak.mdia.minf.stbl.stsd"] fileobj.seek(atom.offset) data = fileobj.read(atom.length) self.bitrate = cdata.uint_be(data[-17:-13]) except (ValueError, KeyError): # Bitrate values are optional. pass
def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos : pos + 4]) name = data[pos + 12 : pos + length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct.unpack(">I4s", data[pos : pos + 8]) if atom_name != "data": raise MP4MetadataError("unexpected atom %r inside %r" % (atom_name, atom.name)) value.append(data[pos + 16 : pos + length]) pos += length if value: self["%s:%s:%s" % (atom.name, mean, name)] = value
def __parse_freeform(self, atom, data): try: fileobj = StringIO(data) mean_length = cdata.uint_be(fileobj.read(4)) # skip over 8 bytes of atom name, flags mean = fileobj.read(mean_length - 4)[8:] name_length = cdata.uint_be(fileobj.read(4)) name = fileobj.read(name_length - 4)[8:] value_length = cdata.uint_be(fileobj.read(4)) # Name, flags, and reserved bytes value = fileobj.read(value_length - 4)[12:] except struct.error: # Some ---- atoms have no data atom, I have no clue why # they actually end up in the file. pass else: self["%s:%s:%s" % (atom.name, mean, name)] = value
def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos:pos+4]) name = data[pos+12:pos+length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct_unpack(">I4s", data[pos:pos+8]) if atom_name != b"data": raise MP4MetadataError( "unexpected atom %r inside %r" % (atom_name, atom.name)) value.append(data[pos+16:pos+length]) pos += length if value: self[atom.name + b":" + mean + b":" + name] = value
def _parse_full_atom(data): """Some atoms are versioned. Split them up in (version, flags, payload). Can raise ValueError. """ if len(data) < 4: raise ValueError("not enough data") version = ord(data[0:1]) flags = cdata.uint_be(b"\x00" + data[1:4]) return version, flags, data[4:]
def parse_full_atom(data): """Some atoms are versioned. Split them up in (version, flags, payload). Can raise ValueError. """ if len(data) < 4: raise ValueError("not enough data") version = ord(data[0:1]) flags = cdata.uint_be(b"\x00" + data[1:4]) return version, flags, data[4:]
def __update_offset_table(self, fileobj, fmt, atom, delta, offset): """Update offset table in the specified atom.""" if atom.offset > offset: atom.offset += delta fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = fmt % cdata.uint_be(data[:4]) offsets = struct_unpack(fmt, data[4:]) offsets = [o + (0, delta)[offset < o] for o in offsets] fileobj.seek(atom.offset + 16) fileobj.write(struct_pack(fmt, *offsets))
def __update_offset_table(self, fileobj, fmt, atom, delta, offset): """Update offset table in the specified atom.""" if atom.offset > offset: atom.offset += delta fileobj.seek(atom.offset + 12) data = fileobj.read(atom.length - 12) fmt = fmt % cdata.uint_be(data[:4]) offsets = struct.unpack(fmt, data[4:]) offsets = [o + (0, delta)[offset < o] for o in offsets] fileobj.seek(atom.offset + 16) fileobj.write(struct.pack(fmt, *offsets))
def __update_tfhd(self, fileobj, atom, delta, offset): if atom.offset > offset: atom.offset += delta fileobj.seek(atom.offset + 9) data = fileobj.read(atom.length - 9) flags = cdata.uint_be(b"\x00" + data[:3]) if flags & 1: o = cdata.ulonglong_be(data[7:15]) if o > offset: o += delta fileobj.seek(atom.offset + 16) fileobj.write(cdata.to_ulonglong_be(o))
def __update_parents(self, fileobj, path, delta): """Update all parent atoms with the new size.""" for atom in path: fileobj.seek(atom.offset) size = cdata.uint_be(fileobj.read(4)) if size == 1: # 64bit # skip name (4B) and read size (8B) size = cdata.ulonglong_be(fileobj.read(12)[4:]) fileobj.seek(atom.offset + 8) fileobj.write(cdata.to_ulonglong_be(size + delta)) else: # 32bit fileobj.seek(atom.offset) fileobj.write(cdata.to_uint_be(size + delta))
def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos : pos + 4]) name = data[pos + 12 : pos + length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct.unpack(">I4s", data[pos : pos + 8]) if atom_name != "data": raise MP4MetadataError("unexpected atom %r inside %r" % (atom_name, atom.name)) version = ord(data[pos + 8]) if version != 0: raise MP4MetadataError("Unsupported version: %r" % version) flags = struct.unpack(">I", "\x00" + data[pos + 9 : pos + 12])[0] value.append(MP4FreeForm(data[pos + 16 : pos + length], dataformat=flags)) pos += length if value: self["%s:%s:%s" % (atom.name, mean, name)] = value
def __parse_freeform(self, atom, data): length = cdata.uint_be(data[:4]) mean = data[12:length] pos = length length = cdata.uint_be(data[pos:pos + 4]) name = data[pos + 12:pos + length] pos += length value = [] while pos < atom.length - 8: length, atom_name = struct.unpack(">I4s", data[pos:pos + 8]) if atom_name != b"data": raise MP4MetadataError("unexpected atom %r inside %r" % (atom_name, atom.name)) version = ord(data[pos + 8:pos + 8 + 1]) if version != 0: raise MP4MetadataError("Unsupported version: %r" % version) flags = struct.unpack(">I", b"\x00" + data[pos + 9:pos + 12])[0] value.append( MP4FreeForm(data[pos + 16:pos + length], dataformat=flags)) pos += length if value: self[atom.name + b":" + mean + b":" + name] = value
def __init__(self, fileobj): page = OggPage(fileobj) while not page.packets[0].startswith("\x80theora"): page = OggPage(fileobj) if not page.first: raise OggTheoraHeaderError( "page has ID header, but doesn't start a stream") data = page.packets[0] vmaj, vmin = struct.unpack("2B", data[7:9]) if (vmaj, vmin) != (3, 2): raise OggTheoraHeaderError( "found Theora version %d.%d != 3.2" % (vmaj, vmin)) fps_num, fps_den = struct.unpack(">2I", data[22:30]) self.fps = fps_num / float(fps_den) self.bitrate = cdata.uint_be("\x00" + data[37:40]) self.granule_shift = (cdata.ushort_be(data[40:42]) >> 5) & 0x1F self.serial = page.serial
def __init__(self, fileobj): page = OggPage(fileobj) while not page.packets[0].startswith(b"\x80theora"): page = OggPage(fileobj) if not page.first: raise OggTheoraHeaderError( "page has ID header, but doesn't start a stream") data = page.packets[0] vmaj, vmin = struct.unpack("2B", data[7:9]) if (vmaj, vmin) != (3, 2): raise OggTheoraHeaderError( "found Theora version %d.%d != 3.2" % (vmaj, vmin)) fps_num, fps_den = struct.unpack(">2I", data[22:30]) self.fps = fps_num / float(fps_den) self.bitrate = cdata.uint_be(b"\x00" + data[37:40]) self.granule_shift = (cdata.ushort_be(data[40:42]) >> 5) & 0x1F self.serial = page.serial
def __parse_uint32(self, atom, data): self[atom.name] = [cdata.uint_be(value[1]) for value in self.__parse_data(atom, data)]
def __parse_text(self, atom, data): flags = cdata.uint_be(data[8:12]) if flags == 1: self[atom.name] = data[16:].decode('utf-8', 'replace')