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