1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Video file reader, using QuickTime
2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# This module was quickly ripped out of another software package, so there is a good
4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# chance that it does not work as-is and it needs some hacking.
5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Jack Jansen, August 2000
7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom warnings import warnpy3k
10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehwarnpy3k("In 3.x, the videoreader module is removed.", stacklevel=2)
11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport sys
14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import Qt
15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import QuickTime
16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import Qd
17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import Qdoffs
18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import QDOffscreen
19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom Carbon import Res
20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehtry:
21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    from Carbon import MediaDescr
22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexcept ImportError:
23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _audiodescr(data):
24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return None
25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehelse:
26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _audiodescr(data):
27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return MediaDescr.SoundDescription.decode(data)
28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehtry:
29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    from imgformat import macrgb
30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexcept ImportError:
31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    macrgb = "Macintosh RGB format"
32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport os
33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# import audio.format
34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass VideoFormat:
36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, name, descr, width, height, format):
37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.__name = name
38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.__descr = descr
39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.__width = width
40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.__height = height
41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.__format = format
42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def getname(self):
44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.__name
45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def getdescr(self):
47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.__descr
48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def getsize(self):
50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.__width, self.__height
51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def getformat(self):
53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.__format
54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass _Reader:
56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, path):
57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fd = Qt.OpenMovieFile(path, 0)
58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.movie, d1, d2 = Qt.NewMovieFromFile(fd, 0, 0)
59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.movietimescale = self.movie.GetMovieTimeScale()
60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiotrack = self.movie.GetMovieIndTrackType(1,
62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                QuickTime.AudioMediaCharacteristic, QuickTime.movieTrackCharacteristic)
63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiomedia = self.audiotrack.GetTrackMedia()
64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except Qt.Error:
65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiotrack = self.audiomedia = None
66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiodescr = {}
67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            handle = Res.Handle('')
69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            n = self.audiomedia.GetMediaSampleDescriptionCount()
70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiomedia.GetMediaSampleDescription(1, handle)
71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiodescr = _audiodescr(handle.data)
72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiotimescale = self.audiomedia.GetMediaTimeScale()
73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            del handle
74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videotrack = self.movie.GetMovieIndTrackType(1,
77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                QuickTime.VisualMediaCharacteristic, QuickTime.movieTrackCharacteristic)
78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videomedia = self.videotrack.GetTrackMedia()
79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except Qt.Error:
80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videotrack = self.videomedia = self.videotimescale = None
81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.videotrack:
82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videotimescale = self.videomedia.GetMediaTimeScale()
83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            x0, y0, x1, y1 = self.movie.GetMovieBox()
84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videodescr = {'width':(x1-x0), 'height':(y1-y0)}
85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self._initgworld()
86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.videocurtime = None
87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.audiocurtime = None
88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __del__(self):
91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.audiomedia = None
92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.audiotrack = None
93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.videomedia = None
94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.videotrack = None
95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.movie = None
96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _initgworld(self):
98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        old_port, old_dev = Qdoffs.GetGWorld()
99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            movie_w = self.videodescr['width']
101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            movie_h = self.videodescr['height']
102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            movie_rect = (0, 0, movie_w, movie_h)
103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.gworld = Qdoffs.NewGWorld(32,  movie_rect, None, None, QDOffscreen.keepLocal)
104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.pixmap = self.gworld.GetGWorldPixMap()
105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            Qdoffs.LockPixels(self.pixmap)
106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            Qdoffs.SetGWorld(self.gworld.as_GrafPtr(), None)
107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            Qd.EraseRect(movie_rect)
108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.movie.SetMovieGWorld(self.gworld.as_GrafPtr(), None)
109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.movie.SetMovieBox(movie_rect)
110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.movie.SetMovieActive(1)
111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.movie.MoviesTask(0)
112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.movie.SetMoviePlayHints(QuickTime.hintsHighQuality, QuickTime.hintsHighQuality)
113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # XXXX framerate
114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            Qdoffs.SetGWorld(old_port, old_dev)
116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _gettrackduration_ms(self, track):
118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        tracktime = track.GetTrackDuration()
119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self._movietime_to_ms(tracktime)
120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _movietime_to_ms(self, time):
122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        value, d1, d2 = Qt.ConvertTimeScale((time, self.movietimescale, None), 1000)
123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return value
124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _videotime_to_ms(self, time):
126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None), 1000)
127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return value
128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _audiotime_to_ms(self, time):
130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        value, d1, d2 = Qt.ConvertTimeScale((time, self.audiotimescale, None), 1000)
131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return value
132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _videotime_to_movietime(self, time):
134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None),
135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.movietimescale)
136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return value
137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def HasAudio(self):
139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return not self.audiotrack is None
140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def HasVideo(self):
142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return not self.videotrack is None
143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetAudioDuration(self):
145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.audiotrack:
146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return 0
147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self._gettrackduration_ms(self.audiotrack)
148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetVideoDuration(self):
150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.videotrack:
151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return 0
152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self._gettrackduration_ms(self.videotrack)
153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetAudioFormat(self):
155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.audiodescr:
156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return None, None, None, None, None
157ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        bps = self.audiodescr['sampleSize']
158ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        nch = self.audiodescr['numChannels']
159ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if nch == 1:
160ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            channels = ['mono']
161ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif nch == 2:
162ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            channels = ['left', 'right']
163ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
164ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            channels = map(lambda x: str(x+1), range(nch))
165ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if bps % 8:
166ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # Funny bits-per sample. We pretend not to understand
167ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            blocksize = 0
168ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            fpb = 0
169ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
170ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # QuickTime is easy (for as far as we support it): samples are always a whole
171ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
172ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            blocksize = (bps/8)*nch
173ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            fpb = 1
174ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.audiodescr['dataFormat'] == 'raw ':
175ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            encoding = 'linear-excess'
176ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif self.audiodescr['dataFormat'] == 'twos':
177ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            encoding = 'linear-signed'
178ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
179ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            encoding = 'quicktime-coding-%s'%self.audiodescr['dataFormat']
180ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh##      return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format',
181ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh##          channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
182ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return channels, encoding, blocksize, fpb, bps
183ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
184ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetAudioFrameRate(self):
185ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.audiodescr:
186ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return None
187ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return int(self.audiodescr['sampleRate'])
188ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
189ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetVideoFormat(self):
190ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        width = self.videodescr['width']
191ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        height = self.videodescr['height']
192ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return VideoFormat('dummy_format', 'Dummy Video Format', width, height, macrgb)
193ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
194ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def GetVideoFrameRate(self):
195ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        tv = self.videocurtime
196ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if tv is None:
197ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            tv = 0
198ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
199ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        tv, dur = self.videomedia.GetMediaNextInterestingTime(flags, tv, 1.0)
200ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        dur = self._videotime_to_ms(dur)
201ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return int((1000.0/dur)+0.5)
202ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
203ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def ReadAudio(self, nframes, time=None):
204ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not time is None:
205ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiocurtime = time
206ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
207ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.audiocurtime is None:
208ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiocurtime = 0
209ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        tv = self.audiomedia.GetMediaNextInterestingTimeOnly(flags, self.audiocurtime, 1.0)
210ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if tv < 0 or (self.audiocurtime and tv < self.audiocurtime):
211ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return self._audiotime_to_ms(self.audiocurtime), None
212ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        h = Res.Handle('')
213ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        desc_h = Res.Handle('')
214ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        size, actualtime, sampleduration, desc_index, actualcount, flags = \
215ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.audiomedia.GetMediaSample(h, 0, tv, desc_h, nframes)
216ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.audiocurtime = actualtime + actualcount*sampleduration
217ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self._audiotime_to_ms(actualtime), h.data
218ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
219ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def ReadVideo(self, time=None):
220ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not time is None:
221ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videocurtime = time
222ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        flags = QuickTime.nextTimeStep
223ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.videocurtime is None:
224ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            flags = flags | QuickTime.nextTimeEdgeOK
225ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.videocurtime = 0
226ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        tv = self.videomedia.GetMediaNextInterestingTimeOnly(flags, self.videocurtime, 1.0)
227ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if tv < 0 or (self.videocurtime and tv <= self.videocurtime):
228ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return self._videotime_to_ms(self.videocurtime), None
229ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.videocurtime = tv
230ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        moviecurtime = self._videotime_to_movietime(self.videocurtime)
231ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.movie.SetMovieTimeValue(moviecurtime)
232ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.movie.MoviesTask(0)
233ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self._videotime_to_ms(self.videocurtime), self._getpixmapcontent()
234ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
235ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _getpixmapcontent(self):
236ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Shuffle the offscreen PixMap data, because it may have funny stride values"""
237ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rowbytes = Qdoffs.GetPixRowBytes(self.pixmap)
238ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        width = self.videodescr['width']
239ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        height = self.videodescr['height']
240ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        start = 0
241ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rv = []
242ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for i in range(height):
243ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            nextline = Qdoffs.GetPixMapBytes(self.pixmap, start, width*4)
244ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            start = start + rowbytes
245ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            rv.append(nextline)
246ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return ''.join(rv)
247ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
248ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef reader(url):
249ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
250ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rdr = _Reader(url)
251ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except IOError:
252ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return None
253ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return rdr
254ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
255ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef _test():
256ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import EasyDialogs
257ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
258ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        from PIL import Image
259ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except ImportError:
260ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        Image = None
261ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import MacOS
262ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    Qt.EnterMovies()
263ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    path = EasyDialogs.AskFileForOpen(message='Video to convert')
264ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if not path: sys.exit(0)
265ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    rdr = reader(path)
266ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if not rdr:
267ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.exit(1)
268ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    dstdir = EasyDialogs.AskFileForSave(message='Name for output folder')
269ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if not dstdir: sys.exit(0)
270ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    num = 0
271ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    os.mkdir(dstdir)
272ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    videofmt = rdr.GetVideoFormat()
273ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    imgfmt = videofmt.getformat()
274ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    imgw, imgh = videofmt.getsize()
275ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    timestamp, data = rdr.ReadVideo()
276ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    while data:
277ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fname = 'frame%04.4d.jpg'%num
278ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        num = num+1
279ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        pname = os.path.join(dstdir, fname)
280ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not Image: print 'Not',
281ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        print 'Writing %s, size %dx%d, %d bytes'%(fname, imgw, imgh, len(data))
282ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if Image:
283ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            img = Image.fromstring("RGBA", (imgw, imgh), data)
284ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            img.save(pname, 'JPEG')
285ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timestamp, data = rdr.ReadVideo()
286ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            MacOS.SetCreatorAndType(pname, 'ogle', 'JPEG')
287ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if num > 20:
288ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print 'stopping at 20 frames so your disk does not fill up:-)'
289ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                break
290ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    print 'Total frames:', num
291ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
292ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehif __name__ == '__main__':
293ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    _test()
294ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    sys.exit(1)
295