source: trunk/src/testing/bin/fileServer/misc/mmpython/video/ogminfo.py @ 4

Revision 4, 13.1 KB checked in by ajaworski, 13 years ago (diff)

Added modified SAGE sources

Line 
1#if 0
2# -----------------------------------------------------------------------
3# ogminfo.py - Ogg Streaming Video Files
4# -----------------------------------------------------------------------
5# $Id: ogminfo.py,v 1.16 2004/05/20 19:54:52 dischi Exp $
6#
7# $Log: ogminfo.py,v $
8# Revision 1.16  2004/05/20 19:54:52  dischi
9# more ogm fixes
10#
11# Revision 1.15  2004/05/20 08:49:29  dischi
12# more ogm fixes, better length calc (again)
13#
14# Revision 1.14  2004/05/18 21:55:52  dischi
15# o get correct length for ogm files
16# o set metadat for the different streams
17# o chapter support
18#
19# Revision 1.13  2003/09/22 16:21:54  the_krow
20# utf-8 comment parsing
21#
22# Revision 1.12  2003/09/09 19:57:08  dischi
23# bad hack to make length oggs work
24#
25# Revision 1.11  2003/09/09 19:32:26  dischi
26# bugfix for finding oggs
27#
28# Revision 1.10  2003/08/04 08:44:51  the_krow
29# Maximum iterations field added as suggested by Magnus in the
30# freevo list.
31#
32# Revision 1.9  2003/07/13 15:20:53  dischi
33# make the module quiet
34#
35# Revision 1.8  2003/07/10 11:16:31  the_krow
36# o Added length calculation for audio only files.
37# o In the future we will use this as a general parser for ogm/ogg
38#
39# Revision 1.7  2003/06/30 13:17:20  the_krow
40# o Refactored mediainfo into factory, synchronizedobject
41# o Parsers now register directly at mmpython not at mmpython.mediainfo
42# o use mmpython.Factory() instead of mmpython.mediainfo.get_singleton()
43# o Bugfix in PNG parser
44# o Renamed disc.AudioInfo into disc.AudioDiscInfo
45# o Renamed disc.DataInfo into disc.DataDiscInfo
46#
47# Revision 1.6  2003/06/29 12:11:16  dischi
48# changed print to _print
49#
50# Revision 1.5  2003/06/23 20:48:11  the_krow
51# width + height fixes for OGM files
52#
53# Revision 1.4  2003/06/23 13:20:51  the_krow
54# basic parsing should now work.
55#
56#
57#
58# -----------------------------------------------------------------------
59# MMPython - Media Metadata for Python
60# Copyright (C) 2003 Thomas Schueppel, Dirk Meyer
61#
62# This program is free software; you can redistribute it and/or modify
63# it under the terms of the GNU General Public License as published by
64# the Free Software Foundation; either version 2 of the License, or
65# (at your option) any later version.
66#
67# This program is distributed in the hope that it will be useful, but
68# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
69# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
70# Public License for more details.
71#
72# You should have received a copy of the GNU General Public License along
73# with this program; if not, write to the Free Software Foundation, Inc.,
74# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
75#
76# -----------------------------------------------------------------------
77#endif
78
79
80from mmpython import mediainfo
81import mmpython
82import struct
83import re
84import stat
85import os
86
87import fourcc
88
89PACKET_TYPE_HEADER   = 0x01
90PACKED_TYPE_METADATA = 0x03
91PACKED_TYPE_SETUP    = 0x05
92PACKET_TYPE_BITS     = 0x07
93PACKET_IS_SYNCPOINT  = 0x08
94
95#VORBIS_VIDEO_PACKET_INFO = 'video'
96
97STREAM_HEADER_VIDEO = '<4sIQQIIHII'
98STREAM_HEADER_AUDIO = '<4sIQQIIHHHI'
99
100_print = mediainfo._debug
101
102VORBISCOMMENT_tags = { 'title': 'TITLE',
103                       'album': 'ALBUM',
104                       'artist': 'ARTIST',
105                       'comment': 'COMMENT',
106                       'date': 'DATE',
107                       'encoder': 'ENCODER',
108                       'trackno': 'TRACKNUMBER',
109                       'language': 'LANGUAGE',
110                       'genre': 'GENRE',
111                     }
112
113MAXITERATIONS = 10
114
115class OgmInfo(mediainfo.AVInfo):
116    def __init__(self, file):
117        mediainfo.AVInfo.__init__(self)
118        self.samplerate  = 1
119        self.all_streams = []           # used to add meta data to streams
120        self.all_header  = []
121
122        for i in range(MAXITERATIONS):
123            granule, nextlen = self._parseOGGS(file)
124            if granule == None:
125                break
126            elif granule > 0:
127                # ok, file started
128                break
129
130        # seek to the end of the stream, to avoid scanning the whole file
131        if (os.stat(file.name)[stat.ST_SIZE] > 50000):
132            file.seek(os.stat(file.name)[stat.ST_SIZE]-49000)
133
134        # read the rest of the file into a buffer
135        h = file.read()
136
137        # find last OggS to get length info
138        if len(h) > 200:
139            idx = h.find('OggS')
140            pos = -49000 + idx
141            if idx:
142                file.seek(os.stat(file.name)[stat.ST_SIZE] + pos)
143            while 1:
144                granule, nextlen = self._parseOGGS(file)
145                if not nextlen:
146                    break
147
148        # Copy metadata to the streams
149        if len(self.all_header) == len(self.all_streams):
150            for i in range(len(self.all_header)):
151                # set length
152                self.length = max(self.all_streams[i].length, self.length)
153
154                # get meta info
155                for key in self.all_streams[i].keys:
156                    if self.all_header[i].has_key(key):
157                        self.all_streams[i][key] = self.all_header[i][key]
158                        del self.all_header[i][key]
159                    if self.all_header[i].has_key(key.upper()):
160                        self.all_streams[i][key] = self.all_header[i][key.upper()]
161                        del self.all_header[i][key.upper()]
162
163                # Extract subtitles:
164                if hasattr(self.all_streams[i], 'type') and \
165                   self.all_streams[i].type == 'subtitle':
166                    self.subtitles.append(self.all_streams[i].language)
167
168                # Chapter parser
169                if self.all_header[i].has_key('CHAPTER01') and not self.chapters:
170                    while 1:
171                        s = 'CHAPTER0%s' % (len(self.chapters) + 1)
172                        if len(s) < 9:
173                            s = '0' + s
174                        if self.all_header[i].has_key(s) and \
175                               self.all_header[i].has_key(s + 'NAME'):
176                            pos = self.all_header[i][s]
177                            try:
178                                pos = int(pos)
179                            except ValueError:
180                                new_pos = 0
181                                for v in pos.split(':'):
182                                    new_pos = new_pos * 60 + float(v)
183                                pos = int(new_pos)
184                               
185                            c = mediainfo.ChapterInfo(self.all_header[i][s + 'NAME'], pos)
186                            del self.all_header[i][s + 'NAME']
187                            del self.all_header[i][s]
188                            self.chapters.append(c)
189                        else:
190                            break
191
192        for stream in self.all_streams:
193            if not stream.length:
194                stream.length = self.length
195               
196        # Copy Metadata from tables into the main set of attributes       
197        for header in self.all_header:
198            self.appendtable('VORBISCOMMENT', header)
199
200        self.tag_map = { ('VORBISCOMMENT', 'en') : VORBISCOMMENT_tags }
201        for k in self.tag_map.keys():
202            _print(k)
203            map(lambda x:self.setitem(x,self.gettable(k[0],k[1]),
204                                      self.tag_map[k][x]), self.tag_map[k].keys())
205
206       
207    def _parseOGGS(self,file):
208        h = file.read(27)
209        if len(h) == 0:
210            # Regular File end
211            return None, None
212        elif len(h) < 27:
213            _print("%d Bytes of Garbage found after End." % len(h))
214            return None, None
215        if h[:4] != "OggS":       
216            self.valid = 0
217            _print("Invalid Ogg")
218            return None, None
219
220        self.valid = 1
221        version = ord(h[4])
222        if version != 0:
223            _print("Unsupported OGG/OGM Version %d." % version)
224            return None, None
225        head = struct.unpack('<BQIIIB', h[5:])
226        headertype, granulepos, serial, pageseqno, checksum, pageSegCount = head
227
228        self.valid = 1
229        self.mime = 'application/ogm'
230        self.type = 'OGG Media'
231        tab = file.read(pageSegCount)
232        nextlen = 0
233        for i in range(len(tab)):
234            nextlen += ord(tab[i])
235        else:
236            h = file.read(1)
237            packettype = ord(h[0]) & PACKET_TYPE_BITS
238            if packettype == PACKET_TYPE_HEADER:
239                h += file.read(nextlen-1)
240                self._parseHeader(h, granulepos)
241            elif packettype == PACKED_TYPE_METADATA:
242                h += file.read(nextlen-1)
243                self._parseMeta(h)
244            else:
245                file.seek(nextlen-1,1)
246        if len(self.all_streams) > serial:
247            stream = self.all_streams[serial]
248            if hasattr(stream, 'samplerate') and \
249                   stream.samplerate:
250                stream.length = granulepos / stream.samplerate
251            elif hasattr(stream, 'bitrate') and \
252                     stream.bitrate:
253                stream.length = granulepos / stream.bitrate
254
255        return granulepos, nextlen + 27 + pageSegCount
256
257       
258    def _parseMeta(self,h):
259        flags = ord(h[0])
260        headerlen = len(h)
261        if headerlen >= 7 and h[1:7] == 'vorbis':
262            header = {}
263            nextlen, self.encoder = self._extractHeaderString(h[7:])
264            numItems = struct.unpack('<I',h[7+nextlen:7+nextlen+4])[0]
265            start = 7+4+nextlen
266            for i in range(numItems):
267                (nextlen, s) = self._extractHeaderString(h[start:])
268                start += nextlen
269                if s:
270                    a = re.split('=',s)
271                    header[(a[0]).upper()]=a[1]
272            # Put Header fields into info fields
273            self.type = 'OGG Vorbis'
274            self.subtype = ''
275            self.all_header.append(header)
276
277           
278    def _parseHeader(self,header,granule):
279        headerlen = len(header)
280        flags = ord(header[0])
281
282        if headerlen >= 30 and header[1:7] == 'vorbis':
283            #print("Vorbis Audio Header")
284            ai = mediainfo.AudioInfo()
285            ai.version, ai.channels, ai.samplerate, bitrate_max, ai.bitrate, \
286                        bitrate_min, blocksize, framing = \
287                        struct.unpack('<IBIiiiBB',header[7:7+23])
288            ai.codec = 'Vorbis'
289            #ai.granule = granule
290            #ai.length = granule / ai.samplerate
291            self.audio.append(ai)
292            self.all_streams.append(ai)
293           
294        elif headerlen >= 7 and header[1:7] == 'theora':
295            #print "Theora Header"
296            # Theora Header
297            # XXX Finish Me
298            vi = mediainfo.VideoInfo()
299            vi.codec = 'theora'
300            self.video.append(vi)
301            self.all_streams.append(vi)
302
303        elif headerlen >= 142 and header[1:36] == 'Direct Show Samples embedded in Ogg':
304            #print 'Direct Show Samples embedded in Ogg'
305            # Old Directshow format
306            # XXX Finish Me
307            vi = mediainfo.VideoInfo()
308            vi.codec = 'dshow'
309            self.video.append(vi)
310            self.all_streams.append(vi)
311
312        elif flags & PACKET_TYPE_BITS == PACKET_TYPE_HEADER and headerlen >= struct.calcsize(STREAM_HEADER_VIDEO)+1:
313            #print "New Directshow Format"
314            # New Directshow Format
315            htype = header[1:9]
316
317            if htype[:5] == 'video':
318                streamheader = struct.unpack( STREAM_HEADER_VIDEO, header[9:struct.calcsize(STREAM_HEADER_VIDEO)+9] )
319                vi = mediainfo.VideoInfo()
320                (type, ssize, timeunit, samplerate, vi.length, buffersize, \
321                 vi.bitrate, vi.width, vi.height) = streamheader
322
323                vi.width /= 65536
324                vi.height /= 65536
325                # XXX length, bitrate are very wrong
326                try:
327                    vi.codec = fourcc.RIFFCODEC[type]
328                except:
329                    vi.codec = 'Unknown (%s)' % type
330                vi.fps = 10000000 / timeunit
331                self.video.append(vi)
332                self.all_streams.append(vi)
333
334            elif htype[:5] == 'audio':
335                streamheader = struct.unpack( STREAM_HEADER_AUDIO, header[9:struct.calcsize(STREAM_HEADER_AUDIO)+9] )
336                ai = mediainfo.AudioInfo()
337                (type, ssize, timeunit, ai.samplerate, ai.length, buffersize, ai.bitrate, ai.channels, bloc, ai.bitrate) = streamheader
338                self.samplerate = ai.samplerate
339                _print("Samplerate %d" % self.samplerate)
340                self.audio.append(ai)
341                self.all_streams.append(ai)
342
343            elif htype[:4] == 'text':
344                subtitle = mediainfo.MediaInfo()
345                subtitle.keys.append('language')
346                subtitle.type   = 'subtitle'
347                subtitle.length = 0
348                self.all_streams.append(subtitle)
349               
350        else:
351            _print("Unknown Header")
352             
353
354    def _extractHeaderString(self,header):
355        len = struct.unpack( '<I', header[:4] )[0]
356        try:
357            return (len+4,unicode(header[4:4+len], 'utf-8'))
358        except:
359            return (len+4,None)
360
361
362mmpython.registertype( 'application/ogg', ('ogm', 'ogg',), mediainfo.TYPE_AV, OgmInfo )
Note: See TracBrowser for help on using the repository browser.