def _parse_dac3(self, atom, fileobj): # ETSI TS 102 366 assert atom.name == b"dac3" ok, data = atom.read(fileobj) if not ok: raise ASEntryError("truncated %s atom" % atom.name) fileobj = cBytesIO(data) r = BitReader(fileobj) # sample_rate in AudioSampleEntry covers values in # fscod2 and not just fscod, so ignore fscod here. try: r.skip(2 + 5 + 3) # fscod, bsid, bsmod acmod = r.bits(3) lfeon = r.bits(1) bit_rate_code = r.bits(5) r.skip(5) # reserved except BitReaderError as e: raise ASEntryError(e) self.channels = [2, 1, 2, 3, 3, 4, 4, 5][acmod] + lfeon try: self.bitrate = [ 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 ][bit_rate_code] * 1000 except IndexError: pass
def _parse_dac3(self, atom, fileobj): # ETSI TS 102 366 assert atom.name == b"dac3" ok, data = atom.read(fileobj) if not ok: raise ASEntryError("truncated %s atom" % atom.name) fileobj = cBytesIO(data) r = BitReader(fileobj) # sample_rate in AudioSampleEntry covers values in # fscod2 and not just fscod, so ignore fscod here. try: r.skip(2 + 5 + 3) # fscod, bsid, bsmod acmod = r.bits(3) lfeon = r.bits(1) bit_rate_code = r.bits(5) r.skip(5) # reserved except BitReaderError as e: raise ASEntryError(e) self.channels = [2, 1, 2, 3, 3, 4, 4, 5][acmod] + lfeon try: self.bitrate = [ 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640][bit_rate_code] * 1000 except IndexError: pass
def _parse_adif(self, fileobj): r = BitReader(fileobj) try: copyright_id_present = r.bits(1) if copyright_id_present: r.skip(72) # copyright_id r.skip(1 + 1) # original_copy, home bitstream_type = r.bits(1) self.bitrate = r.bits(23) npce = r.bits(4) if bitstream_type == 0: r.skip(20) # adif_buffer_fullness pce = ProgramConfigElement(r) try: self.sample_rate = _FREQS[pce.sampling_frequency_index] except IndexError: pass self.channels = pce.channels # other pces.. for i in xrange(npce): ProgramConfigElement(r) r.align() except BitReaderError as e: raise AACError(e) # use bitrate + data size to guess length start = fileobj.tell() fileobj.seek(0, 2) length = fileobj.tell() - start if self.bitrate != 0: self.length = (8.0 * length) / self.bitrate
def __init__(self, atom, fileobj): ok, data = atom.read(fileobj) if not ok: raise ASEntryError("too short %r atom" % atom.name) fileobj = cBytesIO(data) r = BitReader(fileobj) try: # SampleEntry r.skip(6 * 8) # reserved r.skip(2 * 8) # data_ref_index # AudioSampleEntry r.skip(8 * 8) # reserved self.channels = r.bits(16) self.sample_size = r.bits(16) r.skip(2 * 8) # pre_defined r.skip(2 * 8) # reserved self.sample_rate = r.bits(32) >> 16 except BitReaderError as e: raise ASEntryError(e) assert r.is_aligned() try: extra = Atom(fileobj) except AtomError as e: raise ASEntryError(e) self.codec = atom.name.decode("latin-1") self.codec_description = None if atom.name == b"mp4a" and extra.name == b"esds": self._parse_esds(extra, fileobj) elif atom.name == b"alac" and extra.name == b"alac": self._parse_alac(extra, fileobj) elif atom.name == b"ac-3" and extra.name == b"dac3": self._parse_dac3(extra, fileobj) if self.codec_description is None: self.codec_description = self.codec.upper()
def _parse_alac(self, atom, fileobj): # https://alac.macosforge.org/trac/browser/trunk/ # ALACMagicCookieDescription.txt assert atom.name == b"alac" ok, data = atom.read(fileobj) if not ok: raise ASEntryError("truncated %s atom" % atom.name) try: version, flags, data = parse_full_atom(data) except ValueError as e: raise ASEntryError(e) if version != 0: raise ASEntryError("Unsupported version %d" % version) fileobj = cBytesIO(data) r = BitReader(fileobj) try: # for some files the AudioSampleEntry values default to 44100/2chan # and the real info is in the alac cookie, so prefer it r.skip(32) # frameLength compatibleVersion = r.bits(8) if compatibleVersion != 0: return self.sample_size = r.bits(8) r.skip(8 + 8 + 8) self.channels = r.bits(8) r.skip(16 + 32) self.bitrate = r.bits(32) self.sample_rate = r.bits(32) except BitReaderError as e: raise ASEntryError(e)