PyXR

c:\projects\bitpim\src \ wma_file.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: wma_file.py 3608 2006-10-06 02:51:56Z djpham $
0009 
0010 """ Deal with WMA file format"""
0011 
0012 # System modules
0013 import struct
0014 
0015 # BitPim modules
0016 
0017 # constants
0018 ASF_Header_Object_GUID="\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C"
0019 ASF_File_Properties_Object_GUID="\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65"
0020 ASF_Stream_Properties_Object_GUID="\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65"
0021 ASF_Header_Extension_Object_GUID="\xB5\x03\xBF\x5F\x2E\xA9\xCF\x11\x8E\xE3\x00\xC0\x0C\x20\x53\x65"
0022 ASF_Content_Description_Object_GUID="\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C"
0023 ASF_Extended_Content_Description_Object_GUID="\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50"
0024 
0025 #-------------------------------------------------------------------------------
0026 class ASF_Object(object):
0027     def __init__(self, data, keep_data=False):
0028         self.guid=None
0029         self.size=None
0030         self.data=None
0031         if len(data)<24:
0032             # not a valid ASF Object
0033             return
0034         self.guid=data[:16]
0035         self.size=struct.unpack('<Q', data[16:24])[0]
0036         self.decode(data[24:self.size])
0037         self.valid=bool(self.guid and self.size)
0038         if keep_data:
0039             self.data=data[:self.size]
0040 
0041     def unpack(self, format, data, start=0):
0042         return struct.unpack(format, data[start:start+struct.calcsize(format)])
0043     def decode(self, data):
0044         pass
0045 
0046 #-------------------------------------------------------------------------------
0047 class ASF_File_Properties_Object(ASF_Object):
0048     def __init__(self, data, keep_data=False):
0049         super(ASF_File_Properties_Object, self).__init__(data, keep_data)
0050 
0051     def decode(self, data):
0052         self.file_id=data[:16]
0053         (self.file_size, self.creation_date, self.data_packets_count,
0054          _play_duration, self.send_duration, self.preroll, _flags,
0055          self.min_packet_size, self.max_packet_size,
0056          self.max_bitrate)=self.unpack('<QQQQQQLLLL', data, 16)
0057         self.play_duration=_play_duration*100e-9
0058         self.broadcast_flag=_flags&1
0059         self.seekable_flag=_flags&2
0060 
0061 #-------------------------------------------------------------------------------
0062 class ASF_Stream_Properties_Object(ASF_Object):
0063     def __init__(self, data, keep_data=False):
0064         super(ASF_Stream_Properties_Object, self).__init__(data, keep_data)
0065     def decode(self, data):
0066         self.stream_type=data[:16]
0067         self.error_correction_type=data[16:32]
0068         (self.time_offset, self.type_specific_data_len,
0069          self.error_correction_data_len, _flags)=self.unpack('<QLLH', data, 32)
0070         self.stream_number=_flags&0x7F
0071         self.encrypted_content_flag=_flags&0x8000
0072 
0073 #-------------------------------------------------------------------------------
0074 class ASF_Header_Extension_Object(ASF_Object):
0075     def __init__(self, data, keep_data=False):
0076         super(ASF_Header_Extension_Object, self).__init__(data, keep_data)
0077     def decode(self, data):
0078         self.header_extension_data_size=self.unpack('<L', data, 18)[0]
0079 
0080 #-------------------------------------------------------------------------------
0081 class ASF_Content_Description_Object(ASF_Object):
0082     def __init__(self, data, keep_data=False):
0083         super(ASF_Content_Description_Object, self).__init__(data, keep_data)
0084     def _substr(self, data, start, len):
0085         if len:
0086             _s=str(data[start:start+len]).decode('utf_16_le', 'ignore')
0087             if _s[-1]=='\x00':
0088                 _s=_s[:-1]
0089             return (start+len, _s)
0090         return (start, '')
0091     def decode(self, data):
0092         (_title_len, _author_len, _cpright_len, _desc_len,
0093          _rating_len)=self.unpack('<HHHHH', data)
0094         _start=10
0095         (_start, self.title)=self._substr(data, _start, _title_len)
0096         (_start, self.author)=self._substr(data, _start, _author_len)
0097         (_start, self.copyright)=self._substr(data, _start, _cpright_len)
0098         (_start, self.description)=self._substr(data, _start, _desc_len)
0099         (_start, self.rating)=self._substr(data, _start, _rating_len)
0100 
0101 #-------------------------------------------------------------------------------
0102 class ASF_Extended_Content_Description_Object(ASF_Object):
0103     def __init__(self, data, keep_data=False):
0104         super(ASF_Extended_Content_Description_Object, self).__init__(data,
0105                                                                       keep_data)
0106     def _decode_descriptor(self, data, start, res):
0107         _start=start
0108         _name_len=self.unpack('<H', data, _start)[0]
0109         _start+=2
0110         _name=data[_start:_start+_name_len].decode('utf_16_le', 'ignore')
0111         if _name[-1]=='\x00':
0112             _name=_name[:-1]
0113         _start+=_name_len
0114         (_value_type,
0115          _value_len)=self.unpack('<HH', data, _start)
0116         _start+=4
0117         if _value_type==0:
0118             # unicode type
0119             _value=data[_start:_start+_value_len].decode('utf_16_le', 'ignore')
0120             if _value[-1]=='\x00':
0121                 _value=_value[:-1]
0122         elif _value_type==1:
0123             # byte array
0124             _value=data[_start:_start+_value_len]
0125         elif _value_type==2 or _value_type==3:
0126             _value=self.unpack('<L', data, _start)[0]
0127         elif _value_type==4:
0128             _value=self.unpack('<Q', data, _start)[0]
0129         elif _value_type==5:
0130             _value=self.unpack('<H', data, _start)[0]
0131         else:
0132             _value=None
0133         res[_name]=_value
0134         return _start+_value_len
0135 
0136     def decode(self, data):
0137         self.descriptors_count=self.unpack('<H', data)[0]
0138         _start=2
0139         self.descriptors={}
0140         for _cnt in range(self.descriptors_count):
0141             _start=self._decode_descriptor(data, _start,
0142                                            self.descriptors)
0143         
0144 #-------------------------------------------------------------------------------
0145 class ASF_Header_Object(ASF_Object):
0146     def __init__(self, data, keep_data=False):
0147         # Madatory objects
0148         self.file_properties=None
0149         self.stream_properties=None
0150         self.header_extension=None
0151         # List of options objects, not all are recognized and decoded
0152         self.content_description=None
0153         self.extended_content_description=None
0154         super(ASF_Header_Object, self).__init__(data, keep_data)
0155 
0156     _obj_attr_tab={
0157         ASF_File_Properties_Object: 'file_properties',
0158         ASF_Stream_Properties_Object: 'stream_properties',
0159         ASF_Header_Extension_Object: 'header_extension',
0160         ASF_Content_Description_Object: 'content_description',
0161         ASF_Extended_Content_Description_Object: 'extended_content_description',
0162         }
0163         
0164     def decode(self, data):
0165         self.obj_cnt=self.unpack('<L', data)[0]
0166         _start=6
0167         for _cnt in range(self.obj_cnt):
0168             _obj=Create_ASF_Object(data[_start:], False, ASF_Object)
0169             if _obj:
0170                 _start+=_obj.size
0171                 _attr_name=self._obj_attr_tab.get(type(_obj), None)
0172                 if _attr_name:
0173                     setattr(self, _attr_name, _obj)
0174             else:
0175                 break
0176 
0177 #-------------------------------------------------------------------------------
0178 # lookup table for object instantiation
0179 Object_Table={
0180     ASF_Header_Object_GUID: ASF_Header_Object,
0181     ASF_File_Properties_Object_GUID: ASF_File_Properties_Object,
0182     ASF_Stream_Properties_Object_GUID: ASF_Stream_Properties_Object,
0183     ASF_Header_Extension_Object_GUID: ASF_Header_Extension_Object,
0184     ASF_Content_Description_Object_GUID: ASF_Content_Description_Object,
0185     ASF_Extended_Content_Description_Object_GUID: ASF_Extended_Content_Description_Object,
0186     }
0187 
0188 def Create_ASF_Object(data, keep_data=False, default_class=None):
0189     global Object_Table
0190     # look at the data and return a corresponding ASF Object
0191     if len(data)<24:
0192         # not long enough for an ASF Header block
0193         return None
0194     _guid=data[:16]
0195     if Object_Table.has_key(_guid):
0196         return Object_Table[_guid](data, keep_data)
0197     if default_class:
0198         return default_class(data, keep_data)
0199 
0200 #-------------------------------------------------------------------------------
0201 class WMA_File(object):
0202     def __init__(self, file_wrapper):
0203         global Object_Table, ASF_Header_Object_GUID
0204         self.valid=False
0205         self.header=None
0206         self.play_duration=None
0207         self.title=None
0208         self.author=None
0209         self.album=None
0210         self.genre=None
0211 
0212         if file_wrapper.size<24 or \
0213            file_wrapper.GetBytes(0, 16)!=ASF_Header_Object_GUID:
0214             return
0215         _size=struct.unpack('<Q', file_wrapper.GetBytes(16, 8))[0]
0216         self.header=ASF_Header_Object(file_wrapper.GetBytes(0, _size), False)
0217         self.valid=self.header.valid
0218         if not self.valid:
0219             return
0220         if self.header.file_properties and \
0221            self.header.file_properties.valid:
0222             self.play_duration=self.header.file_properties.play_duration
0223         if self.header.content_description and \
0224            self.header.content_description.valid:
0225             self.title=self.header.content_description.title
0226             self.author=self.header.content_description.author
0227         if self.header.extended_content_description and \
0228            self.header.extended_content_description.valid:
0229             _descriptors=self.header.extended_content_description.descriptors
0230             self.album=_descriptors.get('WM/AlbumTitle', '')
0231             self.genre=_descriptors.get('WM/Genre', '')
0232 

Generated by PyXR 0.9.4