source: trunk/src/testing/app/FileViewer/FileServer/misc/mmpython/video/movinfo.py @ 4

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

Added modified SAGE sources

Line 
1#if 0
2# $Id: movinfo.py,v 1.24 2004/07/14 13:42:57 dischi Exp $
3# $Log: movinfo.py,v $
4# Revision 1.24  2004/07/14 13:42:57  dischi
5# small debug updates
6#
7# Revision 1.23  2004/05/24 12:54:35  dischi
8# debug update
9#
10# Revision 1.22  2004/05/11 15:18:59  dischi
11# o more stream infos (like codec)
12# o better error handling for bad i18n tables
13# o language detection
14#
15# Revision 1.21  2004/05/10 15:19:59  dischi
16# o better stream detection
17# o correct length calculation inside the track
18# o support for compressed infos
19#
20# Revision 1.20  2004/03/07 10:27:58  dischi
21# Oops
22#
23# Revision 1.19  2004/03/02 20:48:21  dischi
24# fix gettable
25#
26# Revision 1.18  2003/08/30 09:36:22  dischi
27# turn off some debug based on DEBUG
28#
29# Revision 1.17  2003/07/02 11:17:30  the_krow
30# language is now part of the table key
31#
32# Revision 1.16  2003/06/30 13:17:20  the_krow
33# o Refactored mediainfo into factory, synchronizedobject
34# o Parsers now register directly at mmpython not at mmpython.mediainfo
35# o use mmpython.Factory() instead of mmpython.mediainfo.get_singleton()
36# o Bugfix in PNG parser
37# o Renamed disc.AudioInfo into disc.AudioDiscInfo
38# o Renamed disc.DataInfo into disc.DataDiscInfo
39#
40# Revision 1.15  2003/06/29 18:30:56  dischi
41# length is broken, deactivated it until it is fixed
42#
43# Revision 1.14  2003/06/29 11:59:35  dischi
44# make some debug silent
45#
46# Revision 1.13  2003/06/20 19:17:22  dischi
47# remove filename again and use file.name
48#
49# Revision 1.12  2003/06/20 14:53:05  the_krow
50# Metadata are copied from Quicktime Userdata to MediaInfo fields. This
51#  may be broken since it assumes the Quicktime Comment language to be
52#  set to 0.
53#
54# Revision 1.11  2003/06/19 17:31:12  dischi
55# error handling (and nonsense data)
56#
57# Revision 1.10  2003/06/12 18:53:18  the_krow
58# OGM detection added.
59# .ram is a valid extension to real files
60#
61# Revision 1.9  2003/06/12 16:56:53  the_krow
62# Some Quicktime should work.
63#
64# Revision 1.8  2003/06/12 15:58:05  the_krow
65# QT parsing of i18n metadata
66#
67# Revision 1.7  2003/06/12 14:43:22  the_krow
68# Realmedia file parsing. Title, Artist, Copyright work. Couldn't find
69# many technical parameters to retrieve.
70# Some initial QT parsing
71# added Real to __init__.py
72#
73# Revision 1.6  2003/06/08 19:53:21  dischi
74# also give the filename to init for additional data tests
75#
76# Revision 1.5  2003/06/08 15:40:26  dischi
77# catch exception, raised for small text files
78#
79# Revision 1.4  2003/06/08 13:44:58  dischi
80# Changed all imports to use the complete mmpython path for mediainfo
81#
82# Revision 1.3  2003/06/08 13:11:38  dischi
83# removed print at the end and moved it into register
84#
85# Revision 1.2  2003/05/13 12:31:43  the_krow
86# + Copyright Notice
87#
88#
89# MMPython - Media Metadata for Python
90# Copyright (C) 2003 Thomas Schueppel
91#
92# This program is free software; you can redistribute it and/or modify
93# it under the terms of the GNU General Public License as published by
94# the Free Software Foundation; either version 2 of the License, or
95# (at your option) any later version.
96#
97# This program is distributed in the hope that it will be useful, but
98# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
99# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
100# Public License for more details.
101#
102# You should have received a copy of the GNU General Public License along
103# with this program; if not, write to the Free Software Foundation, Inc.,
104# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
105#
106# -----------------------------------------------------------------------
107#endif
108
109import re
110import struct
111import string
112import time
113import zlib
114import fourcc
115import mmpython
116from mmpython import mediainfo
117from movlanguages import *
118
119# http://developer.apple.com/documentation/QuickTime/QTFF/index.html
120
121ATOM_DEBUG = 0
122
123if mediainfo.DEBUG > 1:
124    ATOM_DEBUG = 1
125   
126class MovInfo(mediainfo.AVInfo):
127    def __init__(self,file):
128        mediainfo.AVInfo.__init__(self)
129        self.context = 'video'
130        self.valid = 0
131        self.mime = 'video/quicktime'
132        self.type = 'Quicktime Video'
133        h = file.read(8)               
134        (size,type) = struct.unpack('>I4s',h)
135        if type == 'moov':
136            self.valid = 1
137        elif type == 'wide':
138            self.valid = 1
139        else:
140            return
141        # Extended size
142        if size == 1:
143            #print "Extended Size"
144            size = struct.unpack('>Q', file.read(8))                 
145        while self._readatom(file):
146            pass
147        try:
148            info = self.gettable('QTUDTA', 'en')
149            self.setitem('title', info, 'nam')
150            self.setitem('artist', info, 'aut')
151            self.setitem('copyright', info, 'cpy')
152        except:
153            pass
154
155
156    def _readatom(self, file):
157       
158        s = file.read(8)
159        if len(s) < 8:
160            return 0
161
162        atomsize,atomtype = struct.unpack('>I4s', s)
163        if not str(atomtype).decode('latin1').isalnum():
164            # stop at nonsense data
165            return 0
166
167        if mediainfo.DEBUG or ATOM_DEBUG:
168            print "%s [%X]" % (atomtype,atomsize)
169
170        if atomtype == 'udta':
171            # Userdata (Metadata)
172            pos = 0
173            tabl = {}
174            i18ntabl = {}
175            atomdata = file.read(atomsize-8)
176            while pos < atomsize-12:
177                (datasize,datatype) = struct.unpack('>I4s', atomdata[pos:pos+8])
178                if ord(datatype[0]) == 169:
179                    # i18n Metadata...
180                    mypos = 8+pos
181                    while mypos < datasize+pos:
182                        # first 4 Bytes are i18n header
183                        (tlen,lang) = struct.unpack('>HH', atomdata[mypos:mypos+4])
184                        i18ntabl[lang] = i18ntabl.get(lang, {})
185                        i18ntabl[lang][datatype[1:]] = atomdata[mypos+4:mypos+tlen+4]
186                        mypos += tlen+4
187                elif datatype == 'WLOC':
188                    # Drop Window Location
189                    pass
190                else:
191                    if ord(atomdata[pos+8:pos+datasize][0]) > 1:
192                        tabl[datatype] = atomdata[pos+8:pos+datasize]
193                pos += datasize
194            if len(i18ntabl.keys()) > 0:
195                for k in i18ntabl.keys():               
196                    if QTLANGUAGES.has_key(k):
197                        self.appendtable('QTUDTA', i18ntabl[k], QTLANGUAGES[k])
198                        self.appendtable('QTUDTA', tabl, QTLANGUAGES[k])
199            else:
200                #print "NO i18"
201                self.appendtable('QTUDTA', tabl)
202             
203        elif atomtype == 'trak':
204            atomdata = file.read(atomsize-8)
205            pos   = 0
206            vi    = None
207            ai    = None
208            info  = None
209            while pos < atomsize-8:
210                (datasize,datatype) = struct.unpack('>I4s', atomdata[pos:pos+8])
211                if datatype == 'tkhd':
212                    tkhd = struct.unpack('>6I8x4H36xII', atomdata[pos+8:pos+datasize])
213                    vi = mediainfo.VideoInfo()
214                    vi.width = tkhd[10] >> 16
215                    vi.height = tkhd[11] >> 16
216                    vi.id = tkhd[3]
217
218                    ai = mediainfo.AudioInfo()
219                    ai.id = tkhd[3]
220                   
221                    try:
222                        # XXX Date number of Seconds is since January 1st 1904!!!
223                        # XXX 2082844800 is the difference between Unix and Apple time
224                        # XXX Fix me to work on Apple, too
225                        self.date = int(tkhd[1]) - 2082844800
226                        self.date = time.strftime('%y/%m/%d', time.gmtime(self.date))
227                    except Exception, e:
228                        print 'ex', e
229                   
230                elif datatype == 'mdia':
231                    pos      += 8
232                    datasize -= 8
233                    if ATOM_DEBUG:
234                        print '--> mdia information'
235
236                    while datasize:
237                        mdia = struct.unpack('>I4s', atomdata[pos:pos+8])
238                        if mdia[1] == 'mdhd':
239                            mdhd = struct.unpack('>IIIIIhh', atomdata[pos+8:pos+8+24])
240                            # duration / time scale
241                            if vi:
242                                vi.length = mdhd[4] / mdhd[3]
243                            if ai:
244                                ai.length = mdhd[4] / mdhd[3]
245                                if mdhd[5] in QTLANGUAGES:
246                                    ai.language = QTLANGUAGES[mdhd[5]]
247                            # mdhd[6] == quality
248                            self.length = max(self.length, mdhd[4] / mdhd[3])
249                        elif mdia[1] == 'minf':
250                            # minf has only atoms inside
251                            pos -=      (mdia[0] - 8)
252                            datasize += (mdia[0] - 8)
253                        elif mdia[1] == 'stbl':
254                            # stbl has only atoms inside
255                            pos -=      (mdia[0] - 8)
256                            datasize += (mdia[0] - 8)
257                        elif mdia[1] == 'hdlr':
258                            hdlr = struct.unpack('>I4s4s', atomdata[pos+8:pos+8+12])
259                            if hdlr[1] == 'mhlr':
260                                if hdlr[2] == 'vide' and not vi in self.video:
261                                    self.video.append(vi)
262                                    info = vi
263                                if hdlr[2] == 'soun' and not ai in self.audio:
264                                    self.audio.append(ai)
265                                    info = ai
266                        elif mdia[1] == 'stsd':
267                            stsd = struct.unpack('>2I', atomdata[pos+8:pos+8+8])
268                            if stsd[1] > 0 and info:
269                                codec = struct.unpack('>I4s', atomdata[pos+16:pos+16+8])
270                                info.codec = codec[1]
271                                if info.codec == 'jpeg':
272                                    # jpeg is no video, remove it from the list
273                                    self.video.remove(vi)
274                                    info = None
275
276                        elif mdia[1] == 'dinf':
277                            dref = struct.unpack('>I4s', atomdata[pos+8:pos+8+8])
278                            if ATOM_DEBUG:
279                                print '  --> %s, %s' % mdia
280                                print '    --> %s, %s (reference)' % dref
281                           
282                        elif ATOM_DEBUG:
283                            if mdia[1].startswith('st'):
284                                print '  --> %s, %s (sample)' % mdia
285                            elif mdia[1] in ('vmhd', 'smhd'):
286                                print '  --> %s, %s (media information header)' % mdia
287                            else:
288                                print '  --> %s, %s (unknown)' % mdia
289
290                        pos      += mdia[0]
291                        datasize -= mdia[0]
292
293                elif datatype == 'udta' and ATOM_DEBUG:
294                    print struct.unpack('>I4s', atomdata[:8])
295                elif ATOM_DEBUG:
296                    if datatype == 'edts':
297                        print "--> %s [%d] (edit list)" % (datatype, datasize)
298                    else:
299                        print "--> %s [%d] (unknown)" % (datatype, datasize)
300                pos += datasize
301
302        elif atomtype == 'mvhd':
303            # movie header
304            mvhd = struct.unpack('>6I2h', file.read(28))
305            self.length = max(self.length, mvhd[4] / mvhd[3])
306            self.volume = mvhd[6]
307            file.seek(atomsize-8-28,1)
308
309        elif atomtype == 'cmov':
310            # compressed movie
311            datasize, atomtype = struct.unpack('>I4s', file.read(8))
312            if not atomtype == 'dcom':
313                return atomsize
314
315            method = struct.unpack('>4s', file.read(datasize-8))[0]
316
317            datasize, atomtype = struct.unpack('>I4s', file.read(8))
318            if not atomtype == 'cmvd':
319                return atomsize
320
321            if method == 'zlib':
322                data = file.read(datasize-8)
323                try:
324                    decompressed = zlib.decompress(data)
325                except Exception, e:
326                    try:
327                        decompressed = zlib.decompress(data[4:])
328                    except Exception, e:
329                        if mediainfo.DEBUG:
330                            print 'unable to decompress atom'
331                        return atomsize
332                import StringIO
333                decompressedIO = StringIO.StringIO(decompressed)
334                while self._readatom(decompressedIO):
335                    pass
336               
337            else:
338                if mediainfo.DEBUG:
339                    print 'unknown compression %s' % method
340                # unknown compression method
341                file.seek(datasize-8,1)
342       
343        elif atomtype == 'moov':
344            # decompressed movie info
345            while self._readatom(file):
346                pass
347           
348        elif atomtype == 'mdat':
349            pos = file.tell() + atomsize - 8
350            # maybe there is data inside the mdat
351            if ATOM_DEBUG:
352                print 'parsing mdat'
353            while self._readatom(file):
354                pass
355            if ATOM_DEBUG:
356                print 'end of mdat'
357            file.seek(pos, 0)
358           
359       
360        else:
361            if ATOM_DEBUG and not atomtype in ('wide', 'free'):
362                print 'unhandled base atom %s' % atomtype
363
364            # Skip unknown atoms
365            try:
366                file.seek(atomsize-8,1)
367            except IOError:
368                return 0
369
370        return atomsize
371       
372mmpython.registertype( 'video/quicktime', ('mov', 'qt'), mediainfo.TYPE_AV, MovInfo )
373
374# doc links:
375# [1] http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap4/chapter_5_section_2.html#//apple_ref/doc/uid/TP40000939-CH206-BBCBIICE
Note: See TracBrowser for help on using the repository browser.