MythTV master
audiofile.py
Go to the documentation of this file.
1#-*- coding: UTF-8 -*-
2"""
3read audio stream from audio file
4"""
5
6import os
7import struct
8import xbmcvfs
9
10class UnknownFormat(Exception):pass
11class FormatError(Exception):pass
12
13class AudioFile(object):
14 f = None
15 audioStart = 0
16
17 def AudioFile(self):
18 self.f = None
20
21 def Open(self,filename):
23 self.f = xbmcvfs.File(filename)
24 ext = os.path.splitext(filename)[1].lower()
25 if ext == '.mp3': self.AnalyzeMp3()
26 elif ext == '.ogg': self.AnalyzeOgg()
27 elif ext == '.wma': self.AnalyzeWma()
28 #elif ext == '.flac': self.AnalyzeFlac()
29 elif ext == '.flac': pass
30 elif ext == '.ape': pass
31 elif ext == '.wav': pass
32 else: # not supported format
33 self.f.close()
34 self.f = None
35 raise UnknownFormat
36
37 def Close(self):
38 self.f.close()
39 self.f = None
40
41 def ReadAudioStream(self, len, offset=0):
42 self.f.seek(self.audioStartaudioStart+offset, 0)
43 return self.f.readBytes(len)
44
45 def AnalyzeMp3(self):
46 # Searching ID3v2 tag
47 while True:
48 buf = self.f.readBytes(3)
49 if len(buf) < 3 or self.f.tell() > 50000:
50 # ID tag is not found
51 self.f.seek(0,0)
53 return
54 if buf == b'ID3':
55 self.f.seek(3,1) # skip version/flag
56 # ID length (synchsafe integer)
57 tl = struct.unpack('4b', self.f.readBytes(4))
58 taglen = (tl[0]<<21)|(tl[1]<<14)|(tl[2]<<7)|tl[3]
59 self.f.seek(taglen,1)
60 break
61 self.f.seek(-2,1)
62 # Searching MPEG SOF
63 while True:
64 buf = self.f.readBytes(1)
65 if len(buf) < 1 or self.f.seek(0,1) > 1000000:
66 raise FormatError
67 if buf == b'\xff':
68 rbit = struct.unpack('B',self.f.readBytes(1))[0] >> 5
69 if rbit == 7: # 11 1's in total
70 self.f.seek(-2,1)
71 self.audioStartaudioStart = self.f.tell()
72 return
73
74 def AnalyzeOgg(self):
75 # Parse page (OggS)
76 while True:
77 buf = self.f.readBytes(27) # header
78 if len(buf) < 27 or self.f.tell() > 50000:
79 # parse error
80 raise FormatError
81 if buf[0:4] != b'OggS':
82 # not supported page format
83 raise UnknownFormat
84 numseg = struct.unpack('B', buf[26])[0]
85 #print "#seg: %d" % numseg
86
87 segtbl = struct.unpack('%dB'%numseg, self.f.readBytes(numseg)) # segment table
88 for seglen in segtbl:
89 buf = self.f.readBytes(7) # segment header
90 #print "segLen(%s): %d" % (buf[1:7],seglen)
91 if buf == b"\x05vorbis":
92 self.f.seek(-7,1) # rollback
93 self.audioStartaudioStart = self.f.tell()
94 return
95 self.f.seek(seglen-7,1) # skip to next segment
96
97 def AnalyzeWma(self):
98 # Searching GUID
99 while True:
100 buf = self.f.readBytes(16)
101 if len(buf) < 16 or self.f.tell() > 50000:
102 raise FormatError
103 guid = buf.encode("hex");
104 if guid == "3626b2758e66cf11a6d900aa0062ce6c":
105 # ASF_Data_Object
106 self.f.seek(-16,1) # rollback
107 self.audioStartaudioStart = self.f.tell()
108 return
109 else:
110 objlen = struct.unpack('<Q', self.f.readBytes(8))[0]
111 self.f.seek(objlen-24,1) # jump to next object
112
113 def AnalyzeFlac(self):
114 if self.f.readBytes(4) != b'fLaC':
115 raise UnknownFormat
116 # Searching GUID
117 while True:
118 buf = self.f.readBytes(4)
119 if len(buf) < 16 or self.f.tell() > 50000:
120 # not found
121 raise FormatError
122 metalen = buf[1] | (buf[2]<<8) | (buf[3]<<16);
123 self.f.seek(metalen,1) # skip this metadata block
124 if buf[0] & 0x80:
125 # it was the last metadata block
126 self.audioStartaudioStart = self.f.tell()
127 return
def AudioFile(self)
Definition: audiofile.py:17
def AnalyzeWma(self)
Definition: audiofile.py:97
def AnalyzeOgg(self)
Definition: audiofile.py:74
def AnalyzeMp3(self)
Definition: audiofile.py:45
def Open(self, filename)
Definition: audiofile.py:21
def AnalyzeFlac(self)
Definition: audiofile.py:113
def ReadAudioStream(self, len, offset=0)
Definition: audiofile.py:41
def Close(self)
Definition: audiofile.py:37
#define close
Definition: compat.h:30