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