#if 0
# -----------------------------------------------------------------------
# ogminfo.py - Ogg Streaming Video Files
# -----------------------------------------------------------------------
# $Id: ogminfo.py 336 2004-05-20 19:54:52Z dischi $
#
# $Log$
# Revision 1.16  2004/05/20 19:54:52  dischi
# more ogm fixes
#
# Revision 1.15  2004/05/20 08:49:29  dischi
# more ogm fixes, better length calc (again)
#
# Revision 1.14  2004/05/18 21:55:52  dischi
# o get correct length for ogm files
# o set metadat for the different streams
# o chapter support
#
# Revision 1.13  2003/09/22 16:21:54  the_krow
# utf-8 comment parsing
#
# Revision 1.12  2003/09/09 19:57:08  dischi
# bad hack to make length oggs work
#
# Revision 1.11  2003/09/09 19:32:26  dischi
# bugfix for finding oggs
#
# Revision 1.10  2003/08/04 08:44:51  the_krow
# Maximum iterations field added as suggested by Magnus in the
# freevo list.
#
# Revision 1.9  2003/07/13 15:20:53  dischi
# make the module quiet
#
# Revision 1.8  2003/07/10 11:16:31  the_krow
# o Added length calculation for audio only files.
# o In the future we will use this as a general parser for ogm/ogg
#
# Revision 1.7  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.6  2003/06/29 12:11:16  dischi
# changed print to _print
#
# Revision 1.5  2003/06/23 20:48:11  the_krow
# width + height fixes for OGM files
#
# Revision 1.4  2003/06/23 13:20:51  the_krow
# basic parsing should now work.
#
#
#
# -----------------------------------------------------------------------
# MMPython - Media Metadata for Python
# Copyright (C) 2003 Thomas Schueppel, Dirk Meyer
#
# 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 2 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


from mmpython import mediainfo
import mmpython
import struct
import re
import stat
import os

import fourcc

PACKET_TYPE_HEADER   = 0x01
PACKED_TYPE_METADATA = 0x03
PACKED_TYPE_SETUP    = 0x05
PACKET_TYPE_BITS     = 0x07
PACKET_IS_SYNCPOINT  = 0x08

#VORBIS_VIDEO_PACKET_INFO = 'video'

STREAM_HEADER_VIDEO = '<4sIQQIIHII'
STREAM_HEADER_AUDIO = '<4sIQQIIHHHI'

_print = mediainfo._debug

VORBISCOMMENT_tags = { 'title': 'TITLE',
                       'album': 'ALBUM',
                       'artist': 'ARTIST',
                       'comment': 'COMMENT',
                       'date': 'DATE',
                       'encoder': 'ENCODER',
                       'trackno': 'TRACKNUMBER',
                       'language': 'LANGUAGE',
                       'genre': 'GENRE',
                     }

MAXITERATIONS = 10

class OgmInfo(mediainfo.AVInfo):
    def __init__(self, file):
        mediainfo.AVInfo.__init__(self)
        self.samplerate  = 1
        self.all_streams = []           # used to add meta data to streams
        self.all_header  = []

        for i in range(MAXITERATIONS):
            granule, nextlen = self._parseOGGS(file)
            if granule == None:
                break
            elif granule > 0:
                # ok, file started
                break

        # seek to the end of the stream, to avoid scanning the whole file
        if (os.stat(file.name)[stat.ST_SIZE] > 50000):
            file.seek(os.stat(file.name)[stat.ST_SIZE]-49000)

        # read the rest of the file into a buffer
        h = file.read()

        # find last OggS to get length info
        if len(h) > 200:
            idx = h.find('OggS')
            pos = -49000 + idx
            if idx:
                file.seek(os.stat(file.name)[stat.ST_SIZE] + pos)
            while 1:
                granule, nextlen = self._parseOGGS(file)
                if not nextlen:
                    break

        # Copy metadata to the streams
        if len(self.all_header) == len(self.all_streams):
            for i in range(len(self.all_header)):
                # set length
                self.length = max(self.all_streams[i].length, self.length)

                # get meta info
                for key in self.all_streams[i].keys:
                    if self.all_header[i].has_key(key):
                        self.all_streams[i][key] = self.all_header[i][key]
                        del self.all_header[i][key]
                    if self.all_header[i].has_key(key.upper()):
                        self.all_streams[i][key] = self.all_header[i][key.upper()]
                        del self.all_header[i][key.upper()]

                # Extract subtitles:
                if hasattr(self.all_streams[i], 'type') and \
                   self.all_streams[i].type == 'subtitle':
                    self.subtitles.append(self.all_streams[i].language)

                # Chapter parser
                if self.all_header[i].has_key('CHAPTER01') and not self.chapters:
                    while 1:
                        s = 'CHAPTER0%s' % (len(self.chapters) + 1)
                        if len(s) < 9:
                            s = '0' + s
                        if self.all_header[i].has_key(s) and \
                               self.all_header[i].has_key(s + 'NAME'):
                            pos = self.all_header[i][s]
                            try:
                                pos = int(pos)
                            except ValueError:
                                new_pos = 0
                                for v in pos.split(':'):
                                    new_pos = new_pos * 60 + float(v)
                                pos = int(new_pos)
                                
                            c = mediainfo.ChapterInfo(self.all_header[i][s + 'NAME'], pos)
                            del self.all_header[i][s + 'NAME']
                            del self.all_header[i][s]
                            self.chapters.append(c)
                        else:
                            break

        for stream in self.all_streams:
            if not stream.length:
                stream.length = self.length
                
        # Copy Metadata from tables into the main set of attributes        
        for header in self.all_header:
            self.appendtable('VORBISCOMMENT', header)

        self.tag_map = { ('VORBISCOMMENT', 'en') : VORBISCOMMENT_tags }
        for k in self.tag_map.keys():
            _print(k)
            map(lambda x:self.setitem(x,self.gettable(k[0],k[1]),
                                      self.tag_map[k][x]), self.tag_map[k].keys())

        
    def _parseOGGS(self,file):
        h = file.read(27)
        if len(h) == 0:
            # Regular File end
            return None, None
        elif len(h) < 27:
            _print("%d Bytes of Garbage found after End." % len(h))
            return None, None
        if h[:4] != "OggS":        
            self.valid = 0
            _print("Invalid Ogg")
            return None, None

        self.valid = 1
        version = ord(h[4])
        if version != 0:
            _print("Unsupported OGG/OGM Version %d." % version)
            return None, None
        head = struct.unpack('<BQIIIB', h[5:])
        headertype, granulepos, serial, pageseqno, checksum, pageSegCount = head

        self.valid = 1
        self.mime = 'application/ogm'
        self.type = 'OGG Media'
        tab = file.read(pageSegCount)
        nextlen = 0
        for i in range(len(tab)):
            nextlen += ord(tab[i])
        else:
            h = file.read(1)
            packettype = ord(h[0]) & PACKET_TYPE_BITS 
            if packettype == PACKET_TYPE_HEADER:
                h += file.read(nextlen-1)
                self._parseHeader(h, granulepos)
            elif packettype == PACKED_TYPE_METADATA:
                h += file.read(nextlen-1)
                self._parseMeta(h)
            else:
                file.seek(nextlen-1,1)
        if len(self.all_streams) > serial:
            stream = self.all_streams[serial]
            if hasattr(stream, 'samplerate') and \
                   stream.samplerate:
                stream.length = granulepos / stream.samplerate
            elif hasattr(stream, 'bitrate') and \
                     stream.bitrate:
                stream.length = granulepos / stream.bitrate

        return granulepos, nextlen + 27 + pageSegCount

        
    def _parseMeta(self,h):
        flags = ord(h[0])
        headerlen = len(h)
        if headerlen >= 7 and h[1:7] == 'vorbis':
            header = {}
            nextlen, self.encoder = self._extractHeaderString(h[7:])
            numItems = struct.unpack('<I',h[7+nextlen:7+nextlen+4])[0]
            start = 7+4+nextlen
            for i in range(numItems):
                (nextlen, s) = self._extractHeaderString(h[start:])
                start += nextlen
                if s:
                    a = re.split('=',s)
                    header[(a[0]).upper()]=a[1]
            # Put Header fields into info fields
            self.type = 'OGG Vorbis'
            self.subtype = ''
            self.all_header.append(header)

            
    def _parseHeader(self,header,granule):
        headerlen = len(header)
        flags = ord(header[0])

        if headerlen >= 30 and header[1:7] == 'vorbis':
            #print("Vorbis Audio Header")
            ai = mediainfo.AudioInfo()
            ai.version, ai.channels, ai.samplerate, bitrate_max, ai.bitrate, \
                        bitrate_min, blocksize, framing = \
                        struct.unpack('<IBIiiiBB',header[7:7+23])
            ai.codec = 'Vorbis'
            #ai.granule = granule
            #ai.length = granule / ai.samplerate
            self.audio.append(ai)
            self.all_streams.append(ai)
            
        elif headerlen >= 7 and header[1:7] == 'theora':
            #print "Theora Header"
            # Theora Header
            # XXX Finish Me
            vi = mediainfo.VideoInfo()
            vi.codec = 'theora'
            self.video.append(vi)
            self.all_streams.append(vi)

        elif headerlen >= 142 and header[1:36] == 'Direct Show Samples embedded in Ogg':
            #print 'Direct Show Samples embedded in Ogg'
            # Old Directshow format
            # XXX Finish Me
            vi = mediainfo.VideoInfo()
            vi.codec = 'dshow'
            self.video.append(vi)
            self.all_streams.append(vi)

        elif flags & PACKET_TYPE_BITS == PACKET_TYPE_HEADER and headerlen >= struct.calcsize(STREAM_HEADER_VIDEO)+1:
            #print "New Directshow Format"
            # New Directshow Format
            htype = header[1:9]

            if htype[:5] == 'video':
                streamheader = struct.unpack( STREAM_HEADER_VIDEO, header[9:struct.calcsize(STREAM_HEADER_VIDEO)+9] )
                vi = mediainfo.VideoInfo()
                (type, ssize, timeunit, samplerate, vi.length, buffersize, \
                 vi.bitrate, vi.width, vi.height) = streamheader

                vi.width /= 65536
                vi.height /= 65536
                # XXX length, bitrate are very wrong
                try:
                    vi.codec = fourcc.RIFFCODEC[type]
                except:
                    vi.codec = 'Unknown (%s)' % type
                vi.fps = 10000000 / timeunit
                self.video.append(vi)
                self.all_streams.append(vi)

            elif htype[:5] == 'audio':
                streamheader = struct.unpack( STREAM_HEADER_AUDIO, header[9:struct.calcsize(STREAM_HEADER_AUDIO)+9] )
                ai = mediainfo.AudioInfo()
                (type, ssize, timeunit, ai.samplerate, ai.length, buffersize, ai.bitrate, ai.channels, bloc, ai.bitrate) = streamheader
                self.samplerate = ai.samplerate
                _print("Samplerate %d" % self.samplerate)
                self.audio.append(ai)
                self.all_streams.append(ai)

            elif htype[:4] == 'text':
                subtitle = mediainfo.MediaInfo()
                subtitle.keys.append('language')
                subtitle.type   = 'subtitle'
                subtitle.length = 0
                self.all_streams.append(subtitle)
                
        else:
            _print("Unknown Header")
              

    def _extractHeaderString(self,header):
        len = struct.unpack( '<I', header[:4] )[0]
        try:
            return (len+4,unicode(header[4:4+len], 'utf-8'))
        except:
            return (len+4,None)


mmpython.registertype( 'application/ogg', ('ogm', 'ogg',), mediainfo.TYPE_AV, OgmInfo )
