10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Video file reader, using QuickTime
20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# This module was quickly ripped out of another software package, so there is a good
40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# chance that it does not work as-is and it needs some hacking.
50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
60a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Jack Jansen, August 2000
70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
80a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
90a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom warnings import warnpy3k
100a8c90248264a8b26970b4473770bcc3df8515fJosh Gaowarnpy3k("In 3.x, the videoreader module is removed.", stacklevel=2)
110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
130a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport sys
140a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import Qt
150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import QuickTime
160a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import Qd
170a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import Qdoffs
180a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import QDOffscreen
190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Carbon import Res
200a8c90248264a8b26970b4473770bcc3df8515fJosh Gaotry:
210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    from Carbon import MediaDescr
220a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoexcept ImportError:
230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _audiodescr(data):
240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return None
250a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoelse:
260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _audiodescr(data):
270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return MediaDescr.SoundDescription.decode(data)
280a8c90248264a8b26970b4473770bcc3df8515fJosh Gaotry:
290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    from imgformat import macrgb
300a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoexcept ImportError:
310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    macrgb = "Macintosh RGB format"
320a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport os
330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# import audio.format
340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
350a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass VideoFormat:
360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, name, descr, width, height, format):
370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.__name = name
380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.__descr = descr
390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.__width = width
400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.__height = height
410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.__format = format
420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def getname(self):
440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.__name
450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def getdescr(self):
470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.__descr
480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def getsize(self):
500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.__width, self.__height
510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def getformat(self):
530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.__format
540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
550a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _Reader:
560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, path):
570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        fd = Qt.OpenMovieFile(path, 0)
580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.movie, d1, d2 = Qt.NewMovieFromFile(fd, 0, 0)
590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.movietimescale = self.movie.GetMovieTimeScale()
600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        try:
610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiotrack = self.movie.GetMovieIndTrackType(1,
620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                QuickTime.AudioMediaCharacteristic, QuickTime.movieTrackCharacteristic)
630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiomedia = self.audiotrack.GetTrackMedia()
640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        except Qt.Error:
650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiotrack = self.audiomedia = None
660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiodescr = {}
670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            handle = Res.Handle('')
690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            n = self.audiomedia.GetMediaSampleDescriptionCount()
700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiomedia.GetMediaSampleDescription(1, handle)
710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiodescr = _audiodescr(handle.data)
720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiotimescale = self.audiomedia.GetMediaTimeScale()
730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            del handle
740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        try:
760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videotrack = self.movie.GetMovieIndTrackType(1,
770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                QuickTime.VisualMediaCharacteristic, QuickTime.movieTrackCharacteristic)
780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videomedia = self.videotrack.GetTrackMedia()
790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        except Qt.Error:
800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videotrack = self.videomedia = self.videotimescale = None
810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.videotrack:
820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videotimescale = self.videomedia.GetMediaTimeScale()
830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            x0, y0, x1, y1 = self.movie.GetMovieBox()
840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videodescr = {'width':(x1-x0), 'height':(y1-y0)}
850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self._initgworld()
860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.videocurtime = None
870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.audiocurtime = None
880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __del__(self):
910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.audiomedia = None
920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.audiotrack = None
930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.videomedia = None
940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.videotrack = None
950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.movie = None
960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _initgworld(self):
980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        old_port, old_dev = Qdoffs.GetGWorld()
990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        try:
1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            movie_w = self.videodescr['width']
1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            movie_h = self.videodescr['height']
1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            movie_rect = (0, 0, movie_w, movie_h)
1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.gworld = Qdoffs.NewGWorld(32,  movie_rect, None, None, QDOffscreen.keepLocal)
1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.pixmap = self.gworld.GetGWorldPixMap()
1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            Qdoffs.LockPixels(self.pixmap)
1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            Qdoffs.SetGWorld(self.gworld.as_GrafPtr(), None)
1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            Qd.EraseRect(movie_rect)
1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.movie.SetMovieGWorld(self.gworld.as_GrafPtr(), None)
1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.movie.SetMovieBox(movie_rect)
1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.movie.SetMovieActive(1)
1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.movie.MoviesTask(0)
1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.movie.SetMoviePlayHints(QuickTime.hintsHighQuality, QuickTime.hintsHighQuality)
1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # XXXX framerate
1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        finally:
1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            Qdoffs.SetGWorld(old_port, old_dev)
1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _gettrackduration_ms(self, track):
1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        tracktime = track.GetTrackDuration()
1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._movietime_to_ms(tracktime)
1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _movietime_to_ms(self, time):
1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        value, d1, d2 = Qt.ConvertTimeScale((time, self.movietimescale, None), 1000)
1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return value
1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _videotime_to_ms(self, time):
1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None), 1000)
1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return value
1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _audiotime_to_ms(self, time):
1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        value, d1, d2 = Qt.ConvertTimeScale((time, self.audiotimescale, None), 1000)
1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return value
1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _videotime_to_movietime(self, time):
1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None),
1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.movietimescale)
1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return value
1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def HasAudio(self):
1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return not self.audiotrack is None
1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def HasVideo(self):
1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return not self.videotrack is None
1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetAudioDuration(self):
1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.audiotrack:
1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return 0
1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._gettrackduration_ms(self.audiotrack)
1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetVideoDuration(self):
1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.videotrack:
1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return 0
1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._gettrackduration_ms(self.videotrack)
1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetAudioFormat(self):
1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.audiodescr:
1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return None, None, None, None, None
1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        bps = self.audiodescr['sampleSize']
1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        nch = self.audiodescr['numChannels']
1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if nch == 1:
1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            channels = ['mono']
1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        elif nch == 2:
1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            channels = ['left', 'right']
1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            channels = map(lambda x: str(x+1), range(nch))
1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if bps % 8:
1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # Funny bits-per sample. We pretend not to understand
1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            blocksize = 0
1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            fpb = 0
1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # QuickTime is easy (for as far as we support it): samples are always a whole
1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            blocksize = (bps/8)*nch
1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            fpb = 1
1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.audiodescr['dataFormat'] == 'raw ':
1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            encoding = 'linear-excess'
1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        elif self.audiodescr['dataFormat'] == 'twos':
1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            encoding = 'linear-signed'
1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            encoding = 'quicktime-coding-%s'%self.audiodescr['dataFormat']
1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao##      return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format',
1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao##          channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return channels, encoding, blocksize, fpb, bps
1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetAudioFrameRate(self):
1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not self.audiodescr:
1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return None
1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return int(self.audiodescr['sampleRate'])
1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetVideoFormat(self):
1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        width = self.videodescr['width']
1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        height = self.videodescr['height']
1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return VideoFormat('dummy_format', 'Dummy Video Format', width, height, macrgb)
1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def GetVideoFrameRate(self):
1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        tv = self.videocurtime
1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if tv is None:
1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            tv = 0
1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        tv, dur = self.videomedia.GetMediaNextInterestingTime(flags, tv, 1.0)
2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        dur = self._videotime_to_ms(dur)
2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return int((1000.0/dur)+0.5)
2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def ReadAudio(self, nframes, time=None):
2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not time is None:
2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiocurtime = time
2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.audiocurtime is None:
2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiocurtime = 0
2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        tv = self.audiomedia.GetMediaNextInterestingTimeOnly(flags, self.audiocurtime, 1.0)
2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if tv < 0 or (self.audiocurtime and tv < self.audiocurtime):
2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return self._audiotime_to_ms(self.audiocurtime), None
2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        h = Res.Handle('')
2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        desc_h = Res.Handle('')
2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        size, actualtime, sampleduration, desc_index, actualcount, flags = \
2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.audiomedia.GetMediaSample(h, 0, tv, desc_h, nframes)
2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.audiocurtime = actualtime + actualcount*sampleduration
2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._audiotime_to_ms(actualtime), h.data
2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def ReadVideo(self, time=None):
2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not time is None:
2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videocurtime = time
2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        flags = QuickTime.nextTimeStep
2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.videocurtime is None:
2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            flags = flags | QuickTime.nextTimeEdgeOK
2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.videocurtime = 0
2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        tv = self.videomedia.GetMediaNextInterestingTimeOnly(flags, self.videocurtime, 1.0)
2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if tv < 0 or (self.videocurtime and tv <= self.videocurtime):
2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return self._videotime_to_ms(self.videocurtime), None
2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.videocurtime = tv
2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        moviecurtime = self._videotime_to_movietime(self.videocurtime)
2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.movie.SetMovieTimeValue(moviecurtime)
2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.movie.MoviesTask(0)
2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._videotime_to_ms(self.videocurtime), self._getpixmapcontent()
2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _getpixmapcontent(self):
2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        """Shuffle the offscreen PixMap data, because it may have funny stride values"""
2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        rowbytes = Qdoffs.GetPixRowBytes(self.pixmap)
2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        width = self.videodescr['width']
2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        height = self.videodescr['height']
2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        start = 0
2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        rv = []
2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for i in range(height):
2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            nextline = Qdoffs.GetPixMapBytes(self.pixmap, start, width*4)
2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            start = start + rowbytes
2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            rv.append(nextline)
2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return ''.join(rv)
2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef reader(url):
2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    try:
2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        rdr = _Reader(url)
2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    except IOError:
2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return None
2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return rdr
2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _test():
2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    import EasyDialogs
2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    try:
2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        from PIL import Image
2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    except ImportError:
2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        Image = None
2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    import MacOS
2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    Qt.EnterMovies()
2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    path = EasyDialogs.AskFileForOpen(message='Video to convert')
2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not path: sys.exit(0)
2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    rdr = reader(path)
2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not rdr:
2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        sys.exit(1)
2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    dstdir = EasyDialogs.AskFileForSave(message='Name for output folder')
2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not dstdir: sys.exit(0)
2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    num = 0
2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    os.mkdir(dstdir)
2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    videofmt = rdr.GetVideoFormat()
2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    imgfmt = videofmt.getformat()
2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    imgw, imgh = videofmt.getsize()
2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    timestamp, data = rdr.ReadVideo()
2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    while data:
2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        fname = 'frame%04.4d.jpg'%num
2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        num = num+1
2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pname = os.path.join(dstdir, fname)
2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not Image: print 'Not',
2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        print 'Writing %s, size %dx%d, %d bytes'%(fname, imgw, imgh, len(data))
2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if Image:
2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            img = Image.fromstring("RGBA", (imgw, imgh), data)
2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            img.save(pname, 'JPEG')
2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            timestamp, data = rdr.ReadVideo()
2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            MacOS.SetCreatorAndType(pname, 'ogle', 'JPEG')
2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if num > 20:
2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                print 'stopping at 20 frames so your disk does not fill up:-)'
2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                break
2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print 'Total frames:', num
2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == '__main__':
2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    _test()
2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    sys.exit(1)
295