def __init__(self, fileobj): header = fileobj.read(76) if len(header) != 76 or not header.startswith(b"MAC "): raise MonkeysAudioHeaderError("not a Monkey's Audio file") self.version = cdata.ushort_le(header[4:6]) if self.version >= 3980: (blocks_per_frame, final_frame_blocks, total_frames, self.bits_per_sample, self.channels, self.sample_rate) = struct_unpack("<IIIHHI", header[56:76]) else: compression_level = cdata.ushort_le(header[6:8]) self.channels, self.sample_rate = struct_unpack( "<HI", header[10:16]) total_frames, final_frame_blocks = struct_unpack( "<II", header[24:32]) if self.version >= 3950: blocks_per_frame = 73728 * 4 elif self.version >= 3900 or (self.version >= 3800 and compression_level == 4): blocks_per_frame = 73728 else: blocks_per_frame = 9216 self.version /= 1000.0 self.length = 0.0 if self.sample_rate != 0 and total_frames > 0: total_blocks = ((total_frames - 1) * blocks_per_frame + final_frame_blocks) self.length = float(total_blocks) / self.sample_rate
def __read_offsets(self, filename): fileobj = open(filename, 'rb') atoms = Atoms(fileobj) moov = atoms['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["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 load(self, data): self.type, length = struct_unpack('>2I', data.read(8)) self.mime = data.read(length).decode('UTF-8', 'replace') length, = struct_unpack('>I', data.read(4)) self.desc = data.read(length).decode('UTF-8', 'replace') (self.width, self.height, self.depth, self.colors, length) = struct_unpack('>5I', data.read(20)) self.data = data.read(length)
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 __init__(self, fileobj): header = fileobj.read(76) if (len(header) != 76 or not header.startswith(b"OFR ") or struct_unpack("<I", header[4:8])[0] not in [12, 15]): raise OptimFROGHeaderError("not an OptimFROG file") (total_samples, total_samples_high, sample_type, self.channels, self.sample_rate) = struct_unpack("<IHBBI", header[8:20]) total_samples += total_samples_high << 32 self.channels += 1 if self.sample_rate: self.length = float(total_samples) / (self.channels * self.sample_rate) else: self.length = 0.0
def __init__(self, fileobj): header = bytearray(fileobj.read(32)) if len(header) != 32: raise MusepackHeaderError("not a Musepack file") # Skip ID3v2 tags if header[:3] == b"ID3": size = 10 + BitPaddedInt(header[6:10]) fileobj.seek(size) header = bytearray(fileobj.read(32)) if len(header) != 32: raise MusepackHeaderError("not a Musepack file") # SV7 if header.startswith(b"MP+"): self.version = header[3] & 0xF if self.version < 7: raise MusepackHeaderError("not a Musepack file") frames = cdata.uint_le(header[4:8]) flags = cdata.uint_le(header[8:12]) self.title_peak, self.title_gain = struct_unpack( "<Hh", header[12:16]) self.album_peak, self.album_gain = struct_unpack( "<Hh", header[16:20]) self.title_gain /= 100.0 self.album_gain /= 100.0 self.title_peak /= 65535.0 self.album_peak /= 65535.0 self.sample_rate = RATES[(flags >> 16) & 0x0003] self.bitrate = 0 # SV4-SV6 else: header_dword = cdata.uint_le(header[0:4]) self.version = (header_dword >> 11) & 0x03FF; if self.version < 4 or self.version > 6: raise MusepackHeaderError("not a Musepack file") self.bitrate = (header_dword >> 23) & 0x01FF; self.sample_rate = 44100 if self.version >= 5: frames = cdata.uint_le(header[4:8]) else: frames = cdata.ushort_le(header[6:8]) if self.version < 6: frames -= 1 self.channels = 2 self.length = float(frames * 1152 - 576) / self.sample_rate if not self.bitrate and self.length != 0: fileobj.seek(0, 2) self.bitrate = int(fileobj.tell() * 8 / (self.length * 1000) + 0.5)
def __init__(self, fileobj): header = bytearray(fileobj.read(32)) if len(header) != 32: raise MusepackHeaderError("not a Musepack file") # Skip ID3v2 tags if header[:3] == b"ID3": size = 10 + BitPaddedInt(header[6:10]) fileobj.seek(size) header = bytearray(fileobj.read(32)) if len(header) != 32: raise MusepackHeaderError("not a Musepack file") # SV7 if header.startswith(b"MP+"): self.version = header[3] & 0xF if self.version < 7: raise MusepackHeaderError("not a Musepack file") frames = cdata.uint_le(header[4:8]) flags = cdata.uint_le(header[8:12]) self.title_peak, self.title_gain = struct_unpack( "<Hh", header[12:16]) self.album_peak, self.album_gain = struct_unpack( "<Hh", header[16:20]) self.title_gain /= 100.0 self.album_gain /= 100.0 self.title_peak /= 65535.0 self.album_peak /= 65535.0 self.sample_rate = RATES[(flags >> 16) & 0x0003] self.bitrate = 0 # SV4-SV6 else: header_dword = cdata.uint_le(header[0:4]) self.version = (header_dword >> 11) & 0x03FF if self.version < 4 or self.version > 6: raise MusepackHeaderError("not a Musepack file") self.bitrate = (header_dword >> 23) & 0x01FF self.sample_rate = 44100 if self.version >= 5: frames = cdata.uint_le(header[4:8]) else: frames = cdata.ushort_le(header[6:8]) if self.version < 6: frames -= 1 self.channels = 2 self.length = float(frames * 1152 - 576) / self.sample_rate if not self.bitrate and self.length != 0: fileobj.seek(0, 2) self.bitrate = int(fileobj.tell() * 8 / (self.length * 1000) + 0.5)
def parse(self, asf, data, fileobj, size): super(HeaderExtensionObject, self).parse(asf, data, fileobj, size) asf.header_extension_obj = self datasize, = struct_unpack("<I", data[18:22]) datapos = 0 self.objects = [] while datapos < datasize: guid, size = struct_unpack("<16sQ", data[22+datapos:22+datapos+24]) if guid in _object_types: obj = _object_types[guid]() else: obj = UnknownObject(guid) obj.parse(asf, data[22+datapos+24:22+datapos+size], fileobj, size) self.objects.append(obj) datapos += size
def __init__(self, fileobj): self.offset = fileobj.tell() self.length, self.name = struct_unpack(">I4s", fileobj.read(8)) if self.length == 1: self.length, = struct_unpack(">Q", fileobj.read(8)) elif self.length < 8: return if self.name in _CONTAINERS: self.children = [] fileobj.seek(_SKIP_SIZE.get(self.name, 0), 1) while fileobj.tell() < self.offset + self.length: self.children.append(Atom(fileobj)) else: fileobj.seek(self.offset + self.length, 0)
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 = struct_unpack(">I", data[37:40] + b"\x00")[0] self.serial = page.serial
def parse(self, asf, data, fileobj, size): super(ExtendedContentDescriptionObject, self).parse(asf, data, fileobj, size) asf.extended_content_description_obj = self num_attributes, = struct_unpack("<H", data[0:2]) pos = 2 for i in range(num_attributes): name_length, = struct_unpack("<H", data[pos:pos+2]) pos += 2 name = data[pos:pos+name_length].decode("utf-16-le").strip("\x00") pos += name_length value_type, value_length = struct_unpack("<HH", data[pos:pos+4]) pos += 4 value = data[pos:pos+value_length] pos += value_length attr = _attribute_types[value_type](data=value) asf.tags.append((name, attr))
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 = struct_unpack(">I", data[37:40] + b"\x00")[0] self.serial = page.serial
def load(self, data): self.seekpoints = [] sp = data.read(self.__SEEKPOINT_SIZE) while len(sp) == self.__SEEKPOINT_SIZE: self.seekpoints.append( SeekPoint(*struct_unpack(self.__SEEKPOINT_FORMAT, sp))) sp = data.read(self.__SEEKPOINT_SIZE)
def __init__(self, fileobj): page = OggPage(fileobj) while not page.packets[0].startswith(b"\x01vorbis"): page = OggPage(fileobj) if not page.first: raise OggVorbisHeaderError( "page has ID header, but doesn't start a stream") (self.channels, self.sample_rate, max_bitrate, nominal_bitrate, min_bitrate) = struct_unpack("<B4i", page.packets[0][11:28]) self.serial = page.serial max_bitrate = max(0, max_bitrate) min_bitrate = max(0, min_bitrate) nominal_bitrate = max(0, nominal_bitrate) if nominal_bitrate == 0: self.bitrate = (max_bitrate + min_bitrate) // 2 elif max_bitrate and max_bitrate < nominal_bitrate: # If the max bitrate is less than the nominal, we know # the nominal is wrong. self.bitrate = max_bitrate elif min_bitrate > nominal_bitrate: self.bitrate = min_bitrate else: self.bitrate = nominal_bitrate if self.bitrate == 0 and self.length > 0: fileobj.seek(0, 2) self.bitrate = int((fileobj.tell() * 8) / self.length)
def load(self, data): self.seekpoints = [] sp = data.read(self.__SEEKPOINT_SIZE) while len(sp) == self.__SEEKPOINT_SIZE: self.seekpoints.append(SeekPoint( *struct_unpack(self.__SEEKPOINT_FORMAT, sp))) sp = data.read(self.__SEEKPOINT_SIZE)
def __init__(self, atoms, fileobj): hdlr = atoms["moov.trak.mdia.hdlr"] fileobj.seek(hdlr.offset) if b"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 = 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 = 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_cover(self, atom, data): length, name, imageformat = struct_unpack(">I4sI", data[:12]) if name != b"data": raise M4AMetadataError( "unexpected atom %r inside 'covr'" % name) if imageformat not in (M4ACover.FORMAT_JPEG, M4ACover.FORMAT_PNG): imageformat = M4ACover.FORMAT_JPEG self[atom.name]= M4ACover(data[16:length], imageformat)
def parse(self, asf, data, fileobj, size): super(MetadataLibraryObject, self).parse(asf, data, fileobj, size) asf.metadata_library_obj = self num_attributes, = struct_unpack("<H", data[0:2]) pos = 2 for i in range(num_attributes): (language, stream, name_length, value_type, value_length) = struct_unpack("<HHHHI", data[pos:pos+12]) pos += 12 name = data[pos:pos+name_length].decode("utf-16-le").strip("\x00") pos += name_length value = data[pos:pos+value_length] pos += value_length args = {'data': value, 'language': language, 'stream': stream} if value_type == 2: args['dword'] = False attr = _attribute_types[value_type](**args) asf.tags.append((name, attr))
def __parse_data(self, atom, data): pos = 0 while pos < atom.length - 8: length, name, flags = struct_unpack(">I4sI", data[pos:pos+12]) if name != b"data": raise MP4MetadataError( "unexpected atom %r inside %r" % (name, atom.name)) yield flags, data[pos+16:pos+length] pos += length
def __read_object(self, fileobj): guid, size = struct_unpack("<16sQ", fileobj.read(24)) if guid in _object_types: obj = _object_types[guid]() else: obj = UnknownObject(guid) data = fileobj.read(size - 24) obj.parse(self, data, fileobj, size) self.objects.append(obj)
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 __read_file(self, fileobj): header = fileobj.read(30) if len(header) != 30 or header[:16] != HeaderObject.GUID: raise ASFHeaderError("Not an ASF file.") self.extended_content_description_obj = None self.content_description_obj = None self.header_extension_obj = None self.metadata_obj = None self.metadata_library_obj = None self.size, self.num_objects = struct_unpack("<QL", header[16:28]) self.objects = [] for i in range(self.num_objects): self.__read_object(fileobj)
def load(self, data): header = data.read(self.__CUESHEET_SIZE) media_catalog_number, lead_in_samples, flags, num_tracks = \ struct_unpack(self.__CUESHEET_FORMAT, header) self.media_catalog_number = media_catalog_number.rstrip(b'\0') self.lead_in_samples = lead_in_samples self.compact_disc = bool(flags & 0x80) self.tracks = [] for i in range(num_tracks): track = data.read(self.__CUESHEET_TRACK_SIZE) start_offset, track_number, isrc_padded, flags, num_indexes = \ struct_unpack(self.__CUESHEET_TRACK_FORMAT, track) isrc = isrc_padded.rstrip(b'\0') type_ = (flags & 0x80) >> 7 pre_emphasis = bool(flags & 0x40) val = CueSheetTrack(track_number, start_offset, isrc, type_, pre_emphasis) for j in range(num_indexes): index = data.read(self.__CUESHEET_TRACKINDEX_SIZE) index_offset, index_number = struct_unpack( self.__CUESHEET_TRACKINDEX_FORMAT, index) val.indexes.append( CueSheetTrackIndex(index_number, index_offset)) self.tracks.append(val)
def load(self, data): header = data.read(self.__CUESHEET_SIZE) media_catalog_number, lead_in_samples, flags, num_tracks = \ struct_unpack(self.__CUESHEET_FORMAT, header) self.media_catalog_number = media_catalog_number.rstrip(b'\0') self.lead_in_samples = lead_in_samples self.compact_disc = bool(flags & 0x80) self.tracks = [] for i in range(num_tracks): track = data.read(self.__CUESHEET_TRACK_SIZE) start_offset, track_number, isrc_padded, flags, num_indexes = \ struct_unpack(self.__CUESHEET_TRACK_FORMAT, track) isrc = isrc_padded.rstrip(b'\0') type_ = (flags & 0x80) >> 7 pre_emphasis = bool(flags & 0x40) val = CueSheetTrack( track_number, start_offset, isrc, type_, pre_emphasis) for j in range(num_indexes): index = data.read(self.__CUESHEET_TRACKINDEX_SIZE) index_offset, index_number = struct_unpack( self.__CUESHEET_TRACKINDEX_FORMAT, index) val.indexes.append( CueSheetTrackIndex(index_number, index_offset)) self.tracks.append(val)
def load(self, data): page = OggPage(data) while not page.packets[0].startswith(b"\x7FFLAC"): page = OggPage(data) major, minor, self.packets, flac = struct_unpack( ">BBH4s", page.packets[0][5:13]) if flac != b"fLaC": raise OggFLACHeaderError("invalid FLAC marker (%r)" % flac) elif (major, minor) != (1, 0): raise OggFLACHeaderError("unknown mapping version: %d.%d" % (major, minor)) self.serial = page.serial # Skip over the block header. stringobj = BytesIO(page.packets[0][17:]) super(OggFLACStreamInfo, self).load(stringobj)
def __parse_cover(self, atom, data): self[atom.name] = [] pos = 0 while pos < atom.length - 8: length, name, imageformat = struct_unpack(">I4sI", data[pos:pos+12]) if name != b"data": if name == b"name": pos += length continue raise MP4MetadataError( "unexpected atom %r inside b'covr'" % name) if imageformat not in (MP4Cover.FORMAT_JPEG, MP4Cover.FORMAT_PNG): imageformat = MP4Cover.FORMAT_JPEG cover = MP4Cover(data[pos+16:pos+length], imageformat) self[atom.name].append(cover) pos += length
def load(self, data): page = OggPage(data) while not page.packets[0].startswith(b"\x7FFLAC"): page = OggPage(data) major, minor, self.packets, flac = struct_unpack( ">BBH4s", page.packets[0][5:13]) if flac != b"fLaC": raise OggFLACHeaderError("invalid FLAC marker (%r)" % flac) elif (major, minor) != (1, 0): raise OggFLACHeaderError( "unknown mapping version: %d.%d" % (major, minor)) self.serial = page.serial # Skip over the block header. stringobj = BytesIO(page.packets[0][17:]) super(OggFLACStreamInfo, self).load(stringobj)
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 __init__(self, fileobj, offset=None): """Parse MPEG stream information from a file-like object. If an offset argument is given, it is used to start looking for stream information and Xing headers; otherwise, ID3v2 tags will be skipped automatically. A correct offset can make loading files significantly faster. """ try: size = os.path.getsize(fileobj.name) except (IOError, OSError, AttributeError): fileobj.seek(0, 2) size = fileobj.tell() # If we don't get an offset, try to skip an ID3v2 tag. if offset is None: fileobj.seek(0, 0) idata = fileobj.read(10) try: id3, insize = struct_unpack('>3sxxx4s', idata) except struct_error: id3, insize = '', 0 insize = BitPaddedInt(insize) if id3 == b'ID3' and insize > 0: offset = insize else: offset = 0 # Try to find two valid headers (meaning, very likely MPEG data) # at the given offset, 30% through the file, 60% through the file, # and 90% through the file. for i in [offset, 0.3 * size, 0.6 * size, 0.9 * size]: try: self.__try(fileobj, int(i), size - offset) except error: pass else: break # If we can't find any two consecutive frames, try to find just # one frame back at the original offset given. else: self.__try(fileobj, offset, size - offset, False) self.sketchy = True
def __init__(self, fileobj, offset=None): """Parse MPEG stream information from a file-like object. If an offset argument is given, it is used to start looking for stream information and Xing headers; otherwise, ID3v2 tags will be skipped automatically. A correct offset can make loading files significantly faster. """ try: size = os.path.getsize(fileobj.name) except (IOError, OSError, AttributeError): fileobj.seek(0, 2) size = fileobj.tell() # If we don't get an offset, try to skip an ID3v2 tag. if offset is None: fileobj.seek(0, 0) idata = fileobj.read(10) try: id3, insize = struct_unpack(">3sxxx4s", idata) except struct_error: id3, insize = "", 0 insize = BitPaddedInt(insize) if id3 == b"ID3" and insize > 0: offset = insize else: offset = 0 # Try to find two valid headers (meaning, very likely MPEG data) # at the given offset, 30% through the file, 60% through the file, # and 90% through the file. for i in [offset, 0.3 * size, 0.6 * size, 0.9 * size]: try: self.__try(fileobj, int(i), size - offset) except error: pass else: break # If we can't find any two consecutive frames, try to find just # one frame back at the original offset given. else: self.__try(fileobj, offset, size - offset, False) self.sketchy = True
def __init__(self, fileobj=None): self.packets = [] if fileobj is None: return self.offset = fileobj.tell() header = fileobj.read(27) if len(header) == 0: raise EOFError try: (oggs, self.version, self.__type_flags, self.position, self.serial, self.sequence, crc, segments) = struct_unpack("<4sBBqIIiB", header) except struct_error: raise error("unable to read full header; got %r" % header) if oggs != b"OggS": raise error("read %r, expected %r, at 0x%x" % (oggs, b"OggS", fileobj.tell() - 27)) if self.version != 0: raise error("version %r unsupported" % self.version) total = 0 lacings = [] lacing_bytes = bytearray(fileobj.read(segments)) if len(lacing_bytes) != segments: raise error("unable to read %r lacing bytes" % segments) for c in lacing_bytes: total += c if c < 255: lacings.append(total) total = 0 if total: lacings.append(total) self.complete = False self.packets = list(map(fileobj.read, lacings)) if list(map(len, self.packets)) != lacings: raise error("unable to read full data")
def __init__(self, fileobj=None): self.packets = [] if fileobj is None: return self.offset = fileobj.tell() header = fileobj.read(27) if len(header) == 0: raise EOFError try: (oggs, self.version, self.__type_flags, self.position, self.serial, self.sequence, crc, segments) = struct_unpack( "<4sBBqIIiB", header) except struct_error: raise error("unable to read full header; got %r" % header) if oggs != b"OggS": raise error("read %r, expected %r, at 0x%x" % ( oggs, b"OggS", fileobj.tell() - 27)) if self.version != 0: raise error("version %r unsupported" % self.version) total = 0 lacings = [] lacing_bytes = bytearray(fileobj.read(segments)) if len(lacing_bytes) != segments: raise error("unable to read %r lacing bytes" % segments) for c in lacing_bytes: total += c if c < 255: lacings.append(total) total = 0 if total: lacings.append(total) self.complete = False self.packets = list(map(fileobj.read, lacings)) if list(map(len, self.packets)) != lacings: raise error("unable to read full data")
def parse(self, asf, data, fileobj, size): super(ContentDescriptionObject, self).parse(asf, data, fileobj, size) asf.content_description_obj = self lengths = struct_unpack("<HHHHH", data[:10]) texts = [] pos = 10 for length in lengths: end = pos + length if length > 0: texts.append(data[pos:end].decode("utf-16-le").strip("\x00")) else: texts.append(None) pos = end title, author, copyright, desc, rating = texts for key, value in list(dict( Title=title, Author=author, Copyright=copyright, Description=desc, Rating=rating).items()): if value is not None: asf.tags[key] = value
def parse(self, asf, data, fileobj, size): super(StreamPropertiesObject, self).parse(asf, data, fileobj, size) channels, sample_rate, bitrate = struct_unpack("<HII", data[56:66]) asf.info.channels = channels asf.info.sample_rate = sample_rate asf.info.bitrate = bitrate * 8
def parse(self, asf, data, fileobj, size): super(FilePropertiesObject, self).parse(asf, data, fileobj, size) length, _, preroll = struct_unpack("<QQQ", data[40:64]) asf.info.length = length / 10000000.0 - preroll / 1000.0
def __parse_pair(self, atom, data): self[atom.name] = [struct_unpack(">2H", data[2:6]) for flags, data in self.__parse_data(atom, data)]
def parse(self, data): return struct_unpack("<H", data)[0]
def __try(self, fileobj, offset, real_size, check_second=True): # This is going to be one really long function; bear with it, # because there's not really a sane point to cut it up. fileobj.seek(offset, 0) # We "know" we have an MPEG file if we find two frames that look like # valid MPEG data. If we can't find them in 32k of reads, something # is horribly wrong (the longest frame can only be about 4k). This # is assuming the offset didn't lie. data = fileobj.read(32768) frame_1 = data.find(b"\xff") while 0 <= frame_1 <= len(data) - 4: frame_data = struct_unpack(">I", data[frame_1:frame_1 + 4])[0] if (frame_data >> 16) & 0xE0 != 0xE0: frame_1 = data.find(b"\xff", frame_1 + 2) else: version = (frame_data >> 19) & 0x3 layer = (frame_data >> 17) & 0x3 protection = (frame_data >> 16) & 0x1 bitrate = (frame_data >> 12) & 0xF sample_rate = (frame_data >> 10) & 0x3 padding = (frame_data >> 9) & 0x1 private = (frame_data >> 8) & 0x1 self.mode = (frame_data >> 6) & 0x3 mode_extension = (frame_data >> 4) & 0x3 copyright = (frame_data >> 3) & 0x1 original = (frame_data >> 2) & 0x1 emphasis = (frame_data >> 0) & 0x3 # remove unused variables del private, mode_extension, copyright, original, emphasis if (version == 1 or layer == 0 or sample_rate == 0x3 or bitrate == 0 or bitrate == 0xF): frame_1 = data.find(b"\xff", frame_1 + 2) else: break else: raise HeaderNotFoundError("can't sync to an MPEG frame") # There is a serious problem here, which is that many flags # in an MPEG header are backwards. self.version = [2.5, None, 2, 1][version] self.layer = 4 - layer self.protected = not protection self.padding = bool(padding) self.bitrate = self.__BITRATE[(self.version, self.layer)][bitrate] self.bitrate *= 1000 self.sample_rate = self.__RATES[self.version][sample_rate] if self.layer == 1: frame_length = (12 * self.bitrate // self.sample_rate + padding) * 4 frame_size = 384 elif self.version >= 2 and self.layer == 3: frame_length = 72 * self.bitrate // self.sample_rate + padding frame_size = 576 else: frame_length = 144 * self.bitrate // self.sample_rate + padding frame_size = 1152 if check_second: possible = frame_1 + frame_length if possible > len(data) + 4: raise HeaderNotFoundError("can't sync to second MPEG frame") try: frame_data = struct_unpack(">H", data[possible:possible + 2])[0] except struct_error: raise HeaderNotFoundError("can't sync to second MPEG frame") if frame_data & 0xFFE0 != 0xFFE0: raise HeaderNotFoundError("can't sync to second MPEG frame") frame_count = real_size / frame_length samples = frame_size * frame_count self.length = samples / self.sample_rate # Try to find/parse the Xing header, which trumps the above length # and bitrate calculation. fileobj.seek(offset, 0) data = fileobj.read(32768) try: xing = data[:-4].index(b"Xing") except ValueError: # Try to find/parse the VBRI header, which trumps the above length # calculation. try: vbri = data[:-24].index(b"VBRI") except ValueError: pass else: # If a VBRI header was found, this is definitely MPEG audio. self.sketchy = False vbri_version = struct_unpack('>H', data[vbri + 4:vbri + 6])[0] if vbri_version == 1: frame_count = struct_unpack('>I', data[vbri + 14:vbri + 18])[0] samples = frame_size * frame_count self.length = (samples / self.sample_rate) or self.length else: # If a Xing header was found, this is definitely MPEG audio. self.sketchy = False flags = struct_unpack('>I', data[xing + 4:xing + 8])[0] if flags & 0x1: frame_count = struct_unpack('>I', data[xing + 8:xing + 12])[0] samples = frame_size * frame_count self.length = (samples / self.sample_rate) or self.length if flags & 0x2: byte = struct_unpack('>I', data[xing + 12:xing + 16])[0] self.bitrate = int((byte * 8) // self.length) # If the bitrate * the length is nowhere near the file # length, recalculate using the bitrate and file length. # Don't do this for very small files. fileobj.seek(2, 0) size = fileobj.tell() expected = (self.bitrate // 8) * self.length if not (size // 2 < expected < size * 2) and size > 2**16: self.length = size / (self.bitrate * 8)