#if 0
# $Id: movinfo.py 363 2004-07-14 13:42:57Z dischi $
# $Log$
# Revision 1.24  2004/07/14 13:42:57  dischi
# small debug updates
#
# Revision 1.23  2004/05/24 12:54:35  dischi
# debug update
#
# Revision 1.22  2004/05/11 15:18:59  dischi
# o more stream infos (like codec)
# o better error handling for bad i18n tables
# o language detection
#
# Revision 1.21  2004/05/10 15:19:59  dischi
# o better stream detection
# o correct length calculation inside the track
# o support for compressed infos
#
# Revision 1.20  2004/03/07 10:27:58  dischi
# Oops
#
# Revision 1.19  2004/03/02 20:48:21  dischi
# fix gettable
#
# Revision 1.18  2003/08/30 09:36:22  dischi
# turn off some debug based on DEBUG
#
# Revision 1.17  2003/07/02 11:17:30  the_krow
# language is now part of the table key
#
# Revision 1.16  2003/06/30 13:17:20  the_krow
# o Refactored mediainfo into factory, synchronizedobject
# o Parsers now register directly at mmpython not at mmpython.mediainfo
# o use mmpython.Factory() instead of mmpython.mediainfo.get_singleton()
# o Bugfix in PNG parser
# o Renamed disc.AudioInfo into disc.AudioDiscInfo
# o Renamed disc.DataInfo into disc.DataDiscInfo
#
# Revision 1.15  2003/06/29 18:30:56  dischi
# length is broken, deactivated it until it is fixed
#
# Revision 1.14  2003/06/29 11:59:35  dischi
# make some debug silent
#
# Revision 1.13  2003/06/20 19:17:22  dischi
# remove filename again and use file.name
#
# Revision 1.12  2003/06/20 14:53:05  the_krow
# Metadata are copied from Quicktime Userdata to MediaInfo fields. This
#  may be broken since it assumes the Quicktime Comment language to be
#  set to 0.
#
# Revision 1.11  2003/06/19 17:31:12  dischi
# error handling (and nonsense data)
#
# Revision 1.10  2003/06/12 18:53:18  the_krow
# OGM detection added.
# .ram is a valid extension to real files
#
# Revision 1.9  2003/06/12 16:56:53  the_krow
# Some Quicktime should work.
#
# Revision 1.8  2003/06/12 15:58:05  the_krow
# QT parsing of i18n metadata
#
# Revision 1.7  2003/06/12 14:43:22  the_krow
# Realmedia file parsing. Title, Artist, Copyright work. Couldn't find
# many technical parameters to retrieve.
# Some initial QT parsing
# added Real to __init__.py
#
# Revision 1.6  2003/06/08 19:53:21  dischi
# also give the filename to init for additional data tests
#
# Revision 1.5  2003/06/08 15:40:26  dischi
# catch exception, raised for small text files
#
# Revision 1.4  2003/06/08 13:44:58  dischi
# Changed all imports to use the complete mmpython path for mediainfo
#
# Revision 1.3  2003/06/08 13:11:38  dischi
# removed print at the end and moved it into register
#
# Revision 1.2  2003/05/13 12:31:43  the_krow
# + Copyright Notice
#
#
# MMPython - Media Metadata for Python
# Copyright (C) 2003 Thomas Schueppel
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# 
# -----------------------------------------------------------------------
#endif

import re
import struct
import string
import time
import zlib
import fourcc
#import mmpython
from modules.mmpython import mediainfo
from movlanguages import *

# http://developer.apple.com/documentation/QuickTime/QTFF/index.html

ATOM_DEBUG = 0

if mediainfo.DEBUG > 1:
    ATOM_DEBUG = 1
    
class MovInfo(mediainfo.AVInfo):
    def __init__(self,file):
        mediainfo.AVInfo.__init__(self)
        self.context = 'video'
        self.valid = 0
        self.mime = 'video/quicktime'
        self.type = 'Quicktime Video'
        h = file.read(8)                
        (size,type) = struct.unpack('>I4s',h)
        if type == 'moov':
            self.valid = 1
        elif type == 'wide':
            self.valid = 1
        else:
            return
        # Extended size
        if size == 1:
            #print "Extended Size"
            size = struct.unpack('>Q', file.read(8))                  
        while self._readatom(file):
            pass
        try:
            info = self.gettable('QTUDTA', 'en')
            self.setitem('title', info, 'nam')
            self.setitem('artist', info, 'aut')
            self.setitem('copyright', info, 'cpy')
        except:
            pass


    def _readatom(self, file):
        
        s = file.read(8)
        if len(s) < 8:
            return 0

        atomsize,atomtype = struct.unpack('>I4s', s)
        if not str(atomtype).decode('latin1').isalnum():
            # stop at nonsense data
            return 0

        if mediainfo.DEBUG or ATOM_DEBUG:
            print "%s [%X]" % (atomtype,atomsize)

        if atomtype == 'udta':
            # Userdata (Metadata)
            pos = 0
            tabl = {}
            i18ntabl = {}
            atomdata = file.read(atomsize-8)
            while pos < atomsize-12:
                (datasize,datatype) = struct.unpack('>I4s', atomdata[pos:pos+8])
                if ord(datatype[0]) == 169:
                    # i18n Metadata... 
                    mypos = 8+pos
                    while mypos < datasize+pos:
                        # first 4 Bytes are i18n header
                        (tlen,lang) = struct.unpack('>HH', atomdata[mypos:mypos+4])
                        i18ntabl[lang] = i18ntabl.get(lang, {})
                        i18ntabl[lang][datatype[1:]] = atomdata[mypos+4:mypos+tlen+4]
                        mypos += tlen+4
                elif datatype == 'WLOC':
                    # Drop Window Location
                    pass
                else:
                    if ord(atomdata[pos+8:pos+datasize][0]) > 1:
                        tabl[datatype] = atomdata[pos+8:pos+datasize]
                pos += datasize
            if len(i18ntabl.keys()) > 0:
                for k in i18ntabl.keys():                
                    if QTLANGUAGES.has_key(k):
                        self.appendtable('QTUDTA', i18ntabl[k], QTLANGUAGES[k])
                        self.appendtable('QTUDTA', tabl, QTLANGUAGES[k])
            else:
                #print "NO i18"
                self.appendtable('QTUDTA', tabl)
             
        elif atomtype == 'trak':
            atomdata = file.read(atomsize-8)
            pos   = 0
            vi    = None
            ai    = None
            info  = None
            while pos < atomsize-8:
                (datasize,datatype) = struct.unpack('>I4s', atomdata[pos:pos+8])
                if datatype == 'tkhd':
                    tkhd = struct.unpack('>6I8x4H36xII', atomdata[pos+8:pos+datasize])
                    vi = mediainfo.VideoInfo()
                    vi.width = tkhd[10] >> 16
                    vi.height = tkhd[11] >> 16
                    vi.id = tkhd[3]

                    ai = mediainfo.AudioInfo()
                    ai.id = tkhd[3]
                    
                    try:
                        # XXX Date number of Seconds is since January 1st 1904!!!
                        # XXX 2082844800 is the difference between Unix and Apple time
                        # XXX Fix me to work on Apple, too
                        self.date = int(tkhd[1]) - 2082844800
                        self.date = time.strftime('%y/%m/%d', time.gmtime(self.date))
                    except Exception, e:
                        print 'ex', e
                    
                elif datatype == 'mdia':
                    pos      += 8
                    datasize -= 8
                    if ATOM_DEBUG:
                        print '--> mdia information'

                    while datasize:
                        mdia = struct.unpack('>I4s', atomdata[pos:pos+8])
                        if mdia[1] == 'mdhd':
                            mdhd = struct.unpack('>IIIIIhh', atomdata[pos+8:pos+8+24])
                            # duration / time scale
                            if vi:
                                vi.length = mdhd[4] / mdhd[3]
                            if ai:
                                ai.length = mdhd[4] / mdhd[3]
                                if mdhd[5] in QTLANGUAGES:
                                    ai.language = QTLANGUAGES[mdhd[5]]
                            # mdhd[6] == quality 
                            self.length = max(self.length, mdhd[4] / mdhd[3])
                        elif mdia[1] == 'minf':
                            # minf has only atoms inside
                            pos -=      (mdia[0] - 8)
                            datasize += (mdia[0] - 8)
                        elif mdia[1] == 'stbl':
                            # stbl has only atoms inside
                            pos -=      (mdia[0] - 8)
                            datasize += (mdia[0] - 8)
                        elif mdia[1] == 'hdlr':
                            hdlr = struct.unpack('>I4s4s', atomdata[pos+8:pos+8+12])
                            if hdlr[1] == 'mhlr':
                                if hdlr[2] == 'vide' and not vi in self.video:
                                    self.video.append(vi)
                                    info = vi
                                if hdlr[2] == 'soun' and not ai in self.audio:
                                    self.audio.append(ai)
                                    info = ai
                        elif mdia[1] == 'stsd':
                            stsd = struct.unpack('>2I', atomdata[pos+8:pos+8+8])
                            if stsd[1] > 0 and info:
                                codec = struct.unpack('>I4s', atomdata[pos+16:pos+16+8])
                                info.codec = codec[1]
                                if info.codec == 'jpeg':
                                    # jpeg is no video, remove it from the list
                                    self.video.remove(vi)
                                    info = None

                        elif mdia[1] == 'dinf':
                            dref = struct.unpack('>I4s', atomdata[pos+8:pos+8+8])
                            if ATOM_DEBUG:
                                print '  --> %s, %s' % mdia
                                print '    --> %s, %s (reference)' % dref
                            
                        elif ATOM_DEBUG:
                            if mdia[1].startswith('st'):
                                print '  --> %s, %s (sample)' % mdia
                            elif mdia[1] in ('vmhd', 'smhd'):
                                print '  --> %s, %s (media information header)' % mdia
                            else:
                                print '  --> %s, %s (unknown)' % mdia

                        pos      += mdia[0]
                        datasize -= mdia[0]

                elif datatype == 'udta' and ATOM_DEBUG:
                    print struct.unpack('>I4s', atomdata[:8])
                elif ATOM_DEBUG:
                    if datatype == 'edts':
                        print "--> %s [%d] (edit list)" % (datatype, datasize)
                    else:
                        print "--> %s [%d] (unknown)" % (datatype, datasize)
                pos += datasize

        elif atomtype == 'mvhd':
            # movie header
            mvhd = struct.unpack('>6I2h', file.read(28))
            self.length = max(self.length, mvhd[4] / mvhd[3])
            self.volume = mvhd[6]
            file.seek(atomsize-8-28,1)

        elif atomtype == 'cmov':
            # compressed movie
            datasize, atomtype = struct.unpack('>I4s', file.read(8))
            if not atomtype == 'dcom':
                return atomsize

            method = struct.unpack('>4s', file.read(datasize-8))[0]

            datasize, atomtype = struct.unpack('>I4s', file.read(8))
            if not atomtype == 'cmvd':
                return atomsize

            if method == 'zlib':
                data = file.read(datasize-8)
                try:
                    decompressed = zlib.decompress(data)
                except Exception, e:
                    try:
                        decompressed = zlib.decompress(data[4:])
                    except Exception, e:
                        if mediainfo.DEBUG:
                            print 'unable to decompress atom'
                        return atomsize
                import StringIO
                decompressedIO = StringIO.StringIO(decompressed)
                while self._readatom(decompressedIO):
                    pass
                
            else:
                if mediainfo.DEBUG:
                    print 'unknown compression %s' % method
                # unknown compression method
                file.seek(datasize-8,1)
        
        elif atomtype == 'moov':
            # decompressed movie info
            while self._readatom(file):
                pass
            
        elif atomtype == 'mdat':
            pos = file.tell() + atomsize - 8
            # maybe there is data inside the mdat
            if ATOM_DEBUG:
                print 'parsing mdat'
            while self._readatom(file):
                pass
            if ATOM_DEBUG:
                print 'end of mdat'
            file.seek(pos, 0)
            
        
        else:
            if ATOM_DEBUG and not atomtype in ('wide', 'free'):
                print 'unhandled base atom %s' % atomtype

            # Skip unknown atoms
            try:
                file.seek(atomsize-8,1)
            except IOError:
                return 0

        return atomsize
        
#mmpython.registertype( 'video/quicktime', ('mov', 'qt'), mediainfo.TYPE_AV, MovInfo )

# doc links:
# [1] http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap4/chapter_5_section_2.html#//apple_ref/doc/uid/TP40000939-CH206-BBCBIICE
