def load(self, atoms, fileobj): try: moov = atoms[b"moov"] except KeyError: raise MP4StreamInfoError("not a MP4 file") for trak in moov.findall(b"trak"): hdlr = trak[b"mdia", b"hdlr"] ok, data = hdlr.read(fileobj) if not ok: raise MP4StreamInfoError("Not enough data") if data[8:12] == b"soun": break else: raise MP4NoTrackError("track has no audio data") mdhd = trak[b"mdia", b"mdhd"] ok, data = mdhd.read(fileobj) if not ok: raise MP4StreamInfoError("Not enough data") try: version, flags, data = parse_full_atom(data) except ValueError as e: raise MP4StreamInfoError(e) if version == 0: offset = 8 fmt = ">2I" elif version == 1: offset = 16 fmt = ">IQ" else: raise MP4StreamInfoError("Unknown mdhd version %d" % version) end = offset + struct.calcsize(fmt) unit, length = struct.unpack(fmt, data[offset:end]) try: self.length = float(length) / unit except ZeroDivisionError: self.length = 0 try: atom = trak[b"mdia", b"minf", b"stbl", b"stsd"] except KeyError: pass else: self._parse_stsd(atom, fileobj)
def _parse_stsd(self, atom, fileobj): """Sets channels, bits_per_sample, sample_rate and optionally bitrate. Can raise MP4StreamInfoError. """ assert atom.name == b"stsd" ok, data = atom.read(fileobj) if not ok: raise MP4StreamInfoError("Invalid stsd") try: version, flags, data = parse_full_atom(data) except ValueError as e: raise MP4StreamInfoError(e) if version != 0: raise MP4StreamInfoError("Unsupported stsd version") try: num_entries, offset = cdata.uint32_be_from(data, 0) except cdata.error as e: raise MP4StreamInfoError(e) if num_entries == 0: return # look at the first entry if there is one entry_fileobj = cBytesIO(data[offset:]) try: entry_atom = Atom(entry_fileobj) except AtomError as e: raise MP4StreamInfoError(e) try: entry = AudioSampleEntry(entry_atom, entry_fileobj) except ASEntryError as e: raise MP4StreamInfoError(e) else: self.channels = entry.channels self.bits_per_sample = entry.sample_size self.sample_rate = entry.sample_rate self.bitrate = entry.bitrate self.codec = entry.codec self.codec_description = entry.codec_description
def test_parse_full_atom(self): p = parse_full_atom(b"\x01\x02\x03\x04\xff") self.assertEqual(p, (1, 131844, b'\xff')) self.assertRaises(ValueError, parse_full_atom, b"\x00\x00\x00")
def test_parse_full_atom(self): p = parse_full_atom(b"\x01\x02\x03\x04\xff") self.assertEqual(p, (1, 131844, b'\xff')) self.assertRaises(ValueError, parse_full_atom, b"\x00\x00\x00")