blob: 5c05ed0cec0185e554f1b37fff81e91a72981428 [file] [log] [blame]
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001# Classes to read and write CMIF video files.
2# (For a description of the CMIF video format, see cmif-file.ms.)
3
4
5# Layers of functionality:
6#
7# VideoParams: maintain essential parameters of a video file
8# Displayer: display a frame in a window (with some extra parameters)
9# Grabber: grab a frame from a window
10# BasicVinFile: read a CMIF video file
11# BasicVoutFile: write a CMIF video file
12# VinFile: BasicVinFile + Displayer
13# VoutFile: BasicVoutFile + Displayer + Grabber
Guido van Rossum4526f371992-09-07 15:28:57 +000014#
15# XXX Future extension:
16# BasicVinoutFile: supports overwriting of individual frames
Guido van Rossum4045c2f1992-09-07 09:24:17 +000017
18
19# Imported modules
Guido van Rossum094a9de1991-12-03 16:50:00 +000020
21import sys
22import gl
23import GL
Guido van Rossum444339d1991-12-03 16:52:40 +000024import colorsys
Guido van Rossum094a9de1991-12-03 16:50:00 +000025
Guido van Rossum094a9de1991-12-03 16:50:00 +000026
Guido van Rossum4045c2f1992-09-07 09:24:17 +000027# Exception raised for various occasions
28
29Error = 'VFile.Error' # file format errors
30CallError = 'VFile.CallError' # bad call
Guido van Rossum4526f371992-09-07 15:28:57 +000031AssertError = 'VFile.AssertError' # internal malfunction
Guido van Rossum4045c2f1992-09-07 09:24:17 +000032
33
34# Constants returned by gl.getdisplaymode(), from <gl/get.h>
35
Guido van Rossum2080b341992-02-28 15:59:23 +000036DMRGB = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +000037DMSINGLE = 1
38DMDOUBLE = 2
39DMRGBDOUBLE = 5
40
41
42# Max nr. of colormap entries to use
Guido van Rossum2080b341992-02-28 15:59:23 +000043
Guido van Rossum58b38cc1992-02-11 14:45:43 +000044MAXMAP = 4096 - 256
45
Guido van Rossum4045c2f1992-09-07 09:24:17 +000046
47# Parametrizations of colormap handling based on color system.
48# (These functions are used via eval with a constructed argument!)
49
50def conv_grey(l, x, y):
51 return colorsys.yiq_to_rgb(l, 0, 0)
52
53def conv_yiq(y, i, q):
54 return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5)
55
56def conv_hls(l, h, s):
57 return colorsys.hls_to_rgb(h, l, s)
58
59def conv_hsv(v, h, s):
60 return colorsys.hsv_to_rgb(h, s, v)
61
62def conv_rgb(r, g, b):
Guido van Rossum58b38cc1992-02-11 14:45:43 +000063 raise Error, 'Attempt to make RGB colormap'
Guido van Rossum4045c2f1992-09-07 09:24:17 +000064
65def conv_rgb8(rgb, d1, d2):
Guido van Rossum2080b341992-02-28 15:59:23 +000066 rgb = int(rgb*255.0)
67 r = (rgb >> 5) & 0x07
68 g = (rgb ) & 0x07
69 b = (rgb >> 3) & 0x03
70 return (r/7.0, g/7.0, b/3.0)
Guido van Rossum094a9de1991-12-03 16:50:00 +000071
Guido van Rossum9e3f3351992-09-29 13:40:47 +000072def conv_jpeg(r, g, b):
73 raise Error, 'Attempt to make RGB colormap (jpeg)'
74
75conv_jpeggrey = conv_grey
Jack Jansen3b253711992-12-14 12:25:21 +000076conv_mono = conv_grey
Guido van Rossum9e3f3351992-09-29 13:40:47 +000077
Guido van Rossum4045c2f1992-09-07 09:24:17 +000078
79# Choose one of the above based upon a color system name
80
81def choose_conversion(format):
82 try:
83 return eval('conv_' + format)
84 except:
85 raise Error, 'Unknown color system: ' + `format`
Guido van Rossum094a9de1991-12-03 16:50:00 +000086
Guido van Rossum094a9de1991-12-03 16:50:00 +000087
Guido van Rossum4526f371992-09-07 15:28:57 +000088# Inverses of the above
89
90def inv_grey(r, g, b):
91 y, i, q = colorsys.rgb_to_yiq(r, g, b)
92 return y, 0, 0
93
94def inv_yiq(r, g, b):
95 y, i, q = colorsys.rgb_to_yiq(r, g, b)
96 return y, i/1.2 + 0.5, q + 0.5
97
98def inv_hls(r, g, b):
99 h, l, s = colorsys.rgb_to_hls(r, g, b)
100 return l, h, s
101
102def inv_hsv(r, g, b):
103 h, s, v = colorsys.rgb_to_hsv(r, g, b)
104 return v, h, s
105
106def inv_rgb(r, g, b):
107 raise Error, 'Attempt to invert RGB colormap'
108
109def inv_rgb8(r, g, b):
110 r = int(r*7.0)
111 g = int(g*7.0)
112 b = int(b*7.0)
113 rgb = ((r&7) << 5) | ((b&3) << 3) | (g&7)
114 return rgb / 255.0, 0, 0
115
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000116def inv_jpeg(r, g, b):
117 raise Error, 'Attempt to invert RGB colormap (jpeg)'
118
119inv_jpeggrey = inv_grey
120
Guido van Rossum4526f371992-09-07 15:28:57 +0000121
122# Choose one of the above based upon a color system name
123
124def choose_inverse(format):
125 try:
126 return eval('inv_' + format)
127 except:
128 raise Error, 'Unknown color system: ' + `format`
129
130
131# Predicate to see whether this is an entry level (non-XS) Indigo.
132# If so we can lrectwrite 8-bit wide pixels into a window in RGB mode
133
134def is_entry_indigo():
135 # XXX hack, hack. We should call gl.gversion() but that doesn't
136 # exist in earlier Python versions. Therefore we check the number
137 # of bitplanes *and* the size of the monitor.
138 xmax = gl.getgdesc(GL.GD_XPMAX)
139 if xmax <> 1024: return 0
140 ymax = gl.getgdesc(GL.GD_YPMAX)
141 if ymax != 768: return 0
142 r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED)
143 g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN)
144 b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
145 return (r, g, b) == (3, 3, 2)
146
147
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000148# Routines to grab data, per color system (only a few really supported).
149# (These functions are used via eval with a constructed argument!)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000150
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000151def grab_rgb(w, h, pf):
152 if gl.getdisplaymode() <> DMRGB:
153 raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
154 if pf <> 1 and pf <> 0:
155 raise Error, 'Sorry, only grab rgb with packfactor 1'
156 return gl.lrectread(0, 0, w-1, h-1), None
Guido van Rossum094a9de1991-12-03 16:50:00 +0000157
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000158def grab_rgb8(w, h, pf):
159 if gl.getdisplaymode() <> DMRGB:
160 raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode'
161 if pf <> 1 and pf <> 0:
162 raise Error, 'Sorry, can only grab rgb8 with packfactor 1'
Guido van Rossum4526f371992-09-07 15:28:57 +0000163 if not is_entry_indigo():
164 raise Error, 'Sorry, can only grab rgb8 on entry level Indigo'
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000165 # XXX Dirty Dirty here.
166 # XXX Set buffer to cmap mode, grab image and set it back.
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000167 gl.cmode()
168 gl.gconfig()
169 gl.pixmode(GL.PM_SIZE, 8)
170 data = gl.lrectread(0, 0, w-1, h-1)
171 data = data[:w*h] # BUG FIX for python lrectread
172 gl.RGBmode()
173 gl.gconfig()
174 gl.pixmode(GL.PM_SIZE, 32)
175 return data, None
Guido van Rossum2080b341992-02-28 15:59:23 +0000176
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000177def grab_grey(w, h, pf):
178 raise Error, 'Sorry, grabbing grey not implemented'
179
180def grab_yiq(w, h, pf):
181 raise Error, 'Sorry, grabbing yiq not implemented'
182
183def grab_hls(w, h, pf):
184 raise Error, 'Sorry, grabbing hls not implemented'
185
186def grab_hsv(w, h, pf):
187 raise Error, 'Sorry, grabbing hsv not implemented'
188
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000189def grab_jpeg(w, h, pf):
190 # XXX Ought to grab rgb and compress it
191 raise Error, 'sorry, grabbing jpeg not implemented'
192
193def grab_jpeggrey(w, h, pf):
194 raise Error, 'sorry, grabbing jpeggrey not implemented'
195
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000196
197# Choose one of the above based upon a color system name
198
199def choose_grabber(format):
200 try:
201 return eval('grab_' + format)
202 except:
203 raise Error, 'Unknown color system: ' + `format`
204
205
206# Base class to manage video format parameters
207
208class VideoParams:
209
210 # Initialize an instance.
211 # Set all parameters to something decent
212 # (except width and height are set to zero)
213
214 def init(self):
215 # Essential parameters
216 self.format = 'grey' # color system used
Jack Jansen3b253711992-12-14 12:25:21 +0000217 # Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey,
218 # mono
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000219 self.width = 0 # width of frame
220 self.height = 0 # height of frame
221 self.packfactor = 1 # expansion using rectzoom
222 # if packfactor == 0, data is one 32-bit word/pixel;
223 # otherwise, data is one byte/pixel
224 self.c0bits = 8 # bits in first color dimension
225 self.c1bits = 0 # bits in second color dimension
226 self.c2bits = 0 # bits in third color dimension
227 self.offset = 0 # colormap index offset (XXX ???)
228 self.chrompack = 0 # set if separate chrominance data
229 return self
230
231 # Set the frame width and height (e.g. from gl.getsize())
232
233 def setsize(self, size):
234 self.width, self.height = size
235
236 # Retrieve the frame width and height (e.g. for gl.prefsize())
237
238 def getsize(self):
239 return (self.width, self.height)
240
241 # Set all parameters.
242 # This does limited validity checking;
243 # if the check fails no parameters are changed
244
245 def setinfo(self, values):
246 (self.format, self.width, self.height, self.packfactor,\
247 self.c0bits, self.c1bits, self.c2bits, self.offset, \
248 self.chrompack) = values
249
250 # Retrieve all parameters in a format suitable for a subsequent
251 # call to setinfo()
252
Guido van Rossum2080b341992-02-28 15:59:23 +0000253 def getinfo(self):
254 return (self.format, self.width, self.height, self.packfactor,\
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000255 self.c0bits, self.c1bits, self.c2bits, self.offset, \
256 self.chrompack)
Guido van Rossum2080b341992-02-28 15:59:23 +0000257
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000258 # Write the relevant bits to stdout
Guido van Rossum094a9de1991-12-03 16:50:00 +0000259
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000260 def printinfo(self):
261 print 'Format: ', self.format
262 print 'Size: ', self.width, 'x', self.height
263 print 'Pack: ', self.packfactor, '; chrom:', self.chrompack
264 print 'Bits: ', self.c0bits, self.c1bits, self.c2bits
265 print 'Offset: ', self.offset
Guido van Rossum094a9de1991-12-03 16:50:00 +0000266
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000267
268# Class to display video frames in a window.
269# It is the caller's responsibility to ensure that the correct window
Guido van Rossum4526f371992-09-07 15:28:57 +0000270# is current when using showframe(), initcolormap(), clear() and clearto()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000271
272class Displayer(VideoParams):
273
274 # Initialize an instance.
275 # This does not need a current window
276
277 def init(self):
278 self = VideoParams.init(self)
279 # User-settable parameters
280 self.magnify = 1.0 # frame magnification factor
281 self.xorigin = 0 # x frame offset
282 self.yorigin = 0 # y frame offset (from bottom)
283 self.quiet = 0 # if set, don't print messages
284 self.fallback = 1 # allow fallback to grey
285 # Internal flags
286 self.colormapinited = 0 # must initialize window
287 self.skipchrom = 0 # don't skip chrominance data
Guido van Rossum4526f371992-09-07 15:28:57 +0000288 self.color0 = None # magic, used by clearto()
289 self.fixcolor0 = 0 # don't need to fix color0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000290 return self
291
292 # setinfo() must reset some internal flags
293
294 def setinfo(self, values):
Guido van Rossum42e07af1992-09-22 15:01:43 +0000295 VideoParams.setinfo(self, values)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000296 self.colormapinited = 0
297 self.skipchrom = 0
Guido van Rossum4526f371992-09-07 15:28:57 +0000298 self.color0 = None
299 self.fixcolor0 = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000300
301 # Show one frame, initializing the window if necessary
Guido van Rossum094a9de1991-12-03 16:50:00 +0000302
Guido van Rossum9a35d571992-08-20 11:51:47 +0000303 def showframe(self, data, chromdata):
Guido van Rossum42e07af1992-09-22 15:01:43 +0000304 self.showpartframe(data, chromdata, \
305 (0,0,self.width,self.height))
306
307 def showpartframe(self, data, chromdata, (x,y,w,h)):
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000308 pf = self.packfactor
309 if self.format in ('jpeg', 'jpeggrey'):
310 import jpeg
311 data, width, height, bytes = jpeg.decompress(data)
312 if self.format == 'jpeg':
313 b = 4
Guido van Rossum4df20fa1992-09-29 17:07:10 +0000314 p = 1
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000315 else:
316 b = 1
Guido van Rossum4df20fa1992-09-29 17:07:10 +0000317 p = pf
318 if (width, height, bytes) <> (w/p, h/p, b):
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000319 raise Error, 'jpeg data has wrong size'
Jack Jansen3b253711992-12-14 12:25:21 +0000320 elif self.format == 'mono':
321 import imageop
322 data = imageop.mono2grey(data, w, h, 0x20, 0xdf)
Guido van Rossum444339d1991-12-03 16:52:40 +0000323 if not self.colormapinited:
324 self.initcolormap()
Guido van Rossum4526f371992-09-07 15:28:57 +0000325 if self.fixcolor0:
326 gl.mapcolor(self.color0)
327 self.fixcolor0 = 0
Guido van Rossum094a9de1991-12-03 16:50:00 +0000328 factor = self.magnify
329 if pf: factor = factor * pf
Guido van Rossum2080b341992-02-28 15:59:23 +0000330 if chromdata and not self.skipchrom:
Guido van Rossum094a9de1991-12-03 16:50:00 +0000331 cp = self.chrompack
Guido van Rossum42e07af1992-09-22 15:01:43 +0000332 cx = int(x*factor*cp) + self.xorigin
333 cy = int(y*factor*cp) + self.yorigin
Guido van Rossum094a9de1991-12-03 16:50:00 +0000334 cw = (w+cp-1)/cp
335 ch = (h+cp-1)/cp
336 gl.rectzoom(factor*cp, factor*cp)
337 gl.pixmode(GL.PM_SIZE, 16)
Guido van Rossum444339d1991-12-03 16:52:40 +0000338 gl.writemask(self.mask - ((1 << self.c0bits) - 1))
Guido van Rossum42e07af1992-09-22 15:01:43 +0000339 gl.lrectwrite(cx, cy, cx + cw - 1, cy + ch - 1, \
340 chromdata)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000341 #
342 if pf:
Guido van Rossum444339d1991-12-03 16:52:40 +0000343 gl.writemask((1 << self.c0bits) - 1)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000344 gl.pixmode(GL.PM_SIZE, 8)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000345 w = w/pf
346 h = h/pf
Guido van Rossum42e07af1992-09-22 15:01:43 +0000347 x = x/pf
348 y = y/pf
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000349 gl.rectzoom(factor, factor)
Guido van Rossum42e07af1992-09-22 15:01:43 +0000350 x = int(x*factor)+self.xorigin
351 y = int(y*factor)+self.yorigin
352 gl.lrectwrite(x, y, x + w - 1, y + h - 1, data)
Guido van Rossum4526f371992-09-07 15:28:57 +0000353 gl.gflush()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000354
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000355 # Initialize the window: set RGB or colormap mode as required,
356 # fill in the colormap, and clear the window
357
Guido van Rossum094a9de1991-12-03 16:50:00 +0000358 def initcolormap(self):
Guido van Rossum4526f371992-09-07 15:28:57 +0000359 self.colormapinited = 1
360 self.color0 = None
361 self.fixcolor0 = 0
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000362 if self.format in ('rgb', 'jpeg'):
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000363 gl.RGBmode()
364 gl.gconfig()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000365 gl.RGBcolor(200, 200, 200) # XXX rather light grey
Guido van Rossum33a8d421992-08-21 12:41:23 +0000366 gl.clear()
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000367 return
Guido van Rossum4df20fa1992-09-29 17:07:10 +0000368 # This only works on an Entry-level Indigo from IRIX 4.0.5
369 if self.format == 'rgb8' and is_entry_indigo() and \
370 gl.gversion() == 'GL4DLG-4.0.': # Note trailing '.'!
371 gl.RGBmode()
372 gl.gconfig()
373 gl.RGBcolor(200, 200, 200) # XXX rather light grey
374 gl.clear()
375 gl.pixmode(GL.PM_SIZE, 8)
376 return
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000377 gl.cmode()
378 gl.gconfig()
Guido van Rossum2080b341992-02-28 15:59:23 +0000379 self.skipchrom = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000380 if self.offset == 0:
381 self.mask = 0x7ff
382 else:
383 self.mask = 0xfff
Guido van Rossum9a35d571992-08-20 11:51:47 +0000384 if not self.quiet:
385 sys.stderr.write('Initializing color map...')
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000386 self._initcmap()
Guido van Rossum4526f371992-09-07 15:28:57 +0000387 gl.clear()
Guido van Rossum9a35d571992-08-20 11:51:47 +0000388 if not self.quiet:
389 sys.stderr.write(' Done.\n')
Guido van Rossume0be2b31992-09-01 14:45:57 +0000390
Guido van Rossum4526f371992-09-07 15:28:57 +0000391 # Clear the window to a default color
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000392
Guido van Rossume0be2b31992-09-01 14:45:57 +0000393 def clear(self):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000394 if not self.colormapinited: raise CallError
Guido van Rossum4526f371992-09-07 15:28:57 +0000395 if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE):
396 gl.RGBcolor(200, 200, 200) # XXX rather light grey
Guido van Rossum2080b341992-02-28 15:59:23 +0000397 gl.clear()
Guido van Rossum4526f371992-09-07 15:28:57 +0000398 return
399 gl.writemask(0xffffffff)
400 gl.clear()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000401
Guido van Rossum4526f371992-09-07 15:28:57 +0000402 # Clear the window to a given RGB color.
403 # This may steal the first color index used; the next call to
404 # showframe() will restore the intended mapping for that index
405
406 def clearto(self, r, g, b):
407 if not self.colormapinited: raise CallError
408 if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE):
409 gl.RGBcolor(r, g, b)
410 gl.clear()
411 return
412 index = self.color0[0]
413 self.fixcolor0 = 1
414 gl.mapcolor(index, r, g, b)
415 gl.writemask(0xffffffff)
416 gl.clear()
417 gl.gflush()
418
419 # Do the hard work for initializing the colormap (internal).
420 # This also sets the current color to the first color index
421 # used -- the caller should never change this since it is used
422 # by clear() and clearto()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000423
424 def _initcmap(self):
425 convcolor = choose_conversion(self.format)
Guido van Rossum2080b341992-02-28 15:59:23 +0000426 maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE)
427 if maxbits > 11:
428 maxbits = 11
Guido van Rossum4526f371992-09-07 15:28:57 +0000429 c0bits = self.c0bits
430 c1bits = self.c1bits
431 c2bits = self.c2bits
Guido van Rossum2080b341992-02-28 15:59:23 +0000432 if c0bits+c1bits+c2bits > maxbits:
433 if self.fallback and c0bits < maxbits:
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000434 # Cannot display frames in this mode, use grey
Guido van Rossum2080b341992-02-28 15:59:23 +0000435 self.skipchrom = 1
436 c1bits = c2bits = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000437 convcolor = choose_conversion('grey')
Guido van Rossum094a9de1991-12-03 16:50:00 +0000438 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000439 raise Error, 'Sorry, '+`maxbits`+ \
440 ' bits max on this machine'
441 maxc0 = 1 << c0bits
442 maxc1 = 1 << c1bits
443 maxc2 = 1 << c2bits
444 if self.offset == 0 and maxbits == 11:
445 offset = 2048
446 else:
447 offset = self.offset
448 if maxbits <> 11:
449 offset = offset & ((1<<maxbits)-1)
Guido van Rossum4526f371992-09-07 15:28:57 +0000450 self.color0 = None
451 self.fixcolor0 = 0
Guido van Rossum2080b341992-02-28 15:59:23 +0000452 for c0 in range(maxc0):
453 c0v = c0/float(maxc0-1)
454 for c1 in range(maxc1):
455 if maxc1 == 1:
456 c1v = 0
Guido van Rossum094a9de1991-12-03 16:50:00 +0000457 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000458 c1v = c1/float(maxc1-1)
459 for c2 in range(maxc2):
460 if maxc2 == 1:
461 c2v = 0
462 else:
463 c2v = c2/float(maxc2-1)
464 index = offset + c0 + (c1<<c0bits) + \
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000465 (c2 << (c0bits+c1bits))
Guido van Rossum2080b341992-02-28 15:59:23 +0000466 if index < MAXMAP:
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000467 rv, gv, bv = \
468 convcolor(c0v, c1v, c2v)
469 r, g, b = int(rv*255.0), \
470 int(gv*255.0), \
471 int(bv*255.0)
Guido van Rossum2080b341992-02-28 15:59:23 +0000472 gl.mapcolor(index, r, g, b)
Guido van Rossum4526f371992-09-07 15:28:57 +0000473 if self.color0 == None:
474 self.color0 = \
475 index, r, g, b
476 # Permanently make the first color index current
477 gl.color(self.color0[0])
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000478 gl.gflush() # send the colormap changes to the X server
Guido van Rossum094a9de1991-12-03 16:50:00 +0000479
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000480
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000481# Class to grab frames from a window.
482# (This has fewer user-settable parameters than Displayer.)
483# It is the caller's responsibility to initialize the window and to
484# ensure that it is current when using grabframe()
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000485
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000486class Grabber(VideoParams):
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000487
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000488 # XXX The init() method of VideoParams is just fine, for now
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000489
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000490 # Grab a frame.
491 # Return (data, chromdata) just like getnextframe().
Guido van Rossum2080b341992-02-28 15:59:23 +0000492
493 def grabframe(self):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000494 grabber = choose_grabber(self.format)
495 return grabber(self.width, self.height, self.packfactor)
496
497
498# Read a CMIF video file header.
499# Return (version, values) where version is 0.0, 1.0, 2.0 or 3.0,
500# and values is ready for setinfo().
501# Raise Error if there is an error in the info
502
503def readfileheader(fp, filename):
504 #
505 # Get identifying header
506 #
507 line = fp.readline(20)
508 if line == 'CMIF video 0.0\n':
509 version = 0.0
510 elif line == 'CMIF video 1.0\n':
511 version = 1.0
512 elif line == 'CMIF video 2.0\n':
513 version = 2.0
514 elif line == 'CMIF video 3.0\n':
515 version = 3.0
516 else:
517 # XXX Could be version 0.0 without identifying header
518 raise Error, \
519 filename + ': Unrecognized file header: ' + `line`[:20]
520 #
521 # Get color encoding info
Guido van Rossum4526f371992-09-07 15:28:57 +0000522 # (The format may change to 'rgb' later when packfactor == 0)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000523 #
524 if version <= 1.0:
525 format = 'grey'
526 c0bits, c1bits, c2bits = 8, 0, 0
527 chrompack = 0
528 offset = 0
529 elif version == 2.0:
530 line = fp.readline()
531 try:
532 c0bits, c1bits, c2bits, chrompack = eval(line[:-1])
533 except:
534 raise Error, filename + ': Bad 2.0 color info'
535 if c1bits or c2bits:
536 format = 'yiq'
537 else:
538 format = 'grey'
539 offset = 0
540 elif version == 3.0:
541 line = fp.readline()
542 try:
543 format, rest = eval(line[:-1])
544 except:
545 raise Error, filename + ': Bad 3.0 color info'
Guido van Rossumff3da051992-12-09 22:16:35 +0000546 if format == 'xrgb8':
547 format = 'rgb8' # rgb8 upside-down, for X
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000548 if format in ('rgb', 'jpeg'):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000549 c0bits = c1bits = c2bits = 0
550 chrompack = 0
551 offset = 0
Jack Jansen3b253711992-12-14 12:25:21 +0000552 elif format in ('grey', 'jpeggrey', 'mono'):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000553 c0bits = rest
554 c1bits = c2bits = 0
555 chrompack = 0
556 offset = 0
557 else:
Guido van Rossum4526f371992-09-07 15:28:57 +0000558 # XXX ought to check that the format is valid
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000559 try:
560 c0bits, c1bits, c2bits, chrompack, offset = rest
561 except:
562 raise Error, filename + ': Bad 3.0 color info'
563 #
564 # Get frame geometry info
565 #
566 line = fp.readline()
567 try:
568 x = eval(line[:-1])
569 except:
570 raise Error, filename + ': Bad (w,h,pf) info'
Guido van Rossum4526f371992-09-07 15:28:57 +0000571 if type(x) <> type(()):
572 raise Error, filename + ': Bad (w,h,pf) info'
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000573 if len(x) == 3:
574 width, height, packfactor = x
575 if packfactor == 0 and version < 3.0:
576 format = 'rgb'
Guido van Rossum4526f371992-09-07 15:28:57 +0000577 c0bits = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000578 elif len(x) == 2 and version <= 1.0:
579 width, height = x
580 packfactor = 2
581 else:
582 raise Error, filename + ': Bad (w,h,pf) info'
Guido van Rossum4df20fa1992-09-29 17:07:10 +0000583 if packfactor > 1:
584 width = (width / packfactor) * packfactor
585 height = (height / packfactor) * packfactor
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000586 #
587 # Return (version, values)
588 #
589 values = (format, width, height, packfactor, \
590 c0bits, c1bits, c2bits, offset, chrompack)
591 return (version, values)
592
593
594# Read a *frame* header -- separate functions per version.
595# Return (timecode, datasize, chromdatasize).
596# Raise EOFError if end of data is reached.
597# Raise Error if data is bad.
598
599def readv0frameheader(fp):
600 line = fp.readline()
601 if not line or line == '\n': raise EOFError
602 try:
603 t = eval(line[:-1])
604 except:
605 raise Error, 'Bad 0.0 frame header'
606 return (t, 0, 0)
607
608def readv1frameheader(fp):
609 line = fp.readline()
610 if not line or line == '\n': raise EOFError
611 try:
612 t, datasize = eval(line[:-1])
613 except:
614 raise Error, 'Bad 1.0 frame header'
615 return (t, datasize, 0)
616
617def readv2frameheader(fp):
618 line = fp.readline()
619 if not line or line == '\n': raise EOFError
620 try:
621 t, datasize = eval(line[:-1])
622 except:
623 raise Error, 'Bad 2.0 frame header'
624 return (t, datasize, 0)
625
626def readv3frameheader(fp):
627 line = fp.readline()
628 if not line or line == '\n': raise EOFError
629 try:
630 t, datasize, chromdatasize = x = eval(line[:-1])
631 except:
632 raise Error, 'Bad 3.0 frame header'
633 return x
634
635
636# Write a CMIF video file header (always version 3.0)
637
638def writefileheader(fp, values):
639 (format, width, height, packfactor, \
640 c0bits, c1bits, c2bits, offset, chrompack) = values
641 #
642 # Write identifying header
643 #
644 fp.write('CMIF video 3.0\n')
645 #
646 # Write color encoding info
647 #
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000648 if format in ('rgb', 'jpeg'):
649 data = (format, 0)
Jack Jansen3b253711992-12-14 12:25:21 +0000650 elif format in ('grey', 'jpeggrey', 'mono'):
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000651 data = (format, c0bits)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000652 else:
653 data = (format, (c0bits, c1bits, c2bits, chrompack, offset))
654 fp.write(`data`+'\n')
655 #
656 # Write frame geometry info
657 #
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000658 if format in ('rgb', 'jpeg'):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000659 packfactor = 0
660 elif packfactor == 0:
661 packfactor = 1
662 data = (width, height, packfactor)
663 fp.write(`data`+'\n')
664
665
666# Basic class for reading CMIF video files
667
668class BasicVinFile(VideoParams):
669
670 def init(self, filename):
671 if filename == '-':
672 fp = sys.stdin
673 else:
674 fp = open(filename, 'r')
675 return self.initfp(fp, filename)
676
677 def initfp(self, fp, filename):
678 self = VideoParams.init(self)
679 self.fp = fp
680 self.filename = filename
681 self.version, values = readfileheader(fp, filename)
682 VideoParams.setinfo(self, values)
683 if self.version == 0.0:
684 w, h, pf = self.width, self.height, self.packfactor
685 if pf == 0:
686 self._datasize = w*h*4
687 else:
688 self._datasize = (w/pf) * (h/pf)
689 self._readframeheader = self._readv0frameheader
690 elif self.version == 1.0:
691 self._readframeheader = readv1frameheader
692 elif self.version == 2.0:
693 self._readframeheader = readv2frameheader
694 elif self.version == 3.0:
695 self._readframeheader = readv3frameheader
696 else:
697 raise Error, \
698 filename + ': Bad version: ' + `self.version`
699 self.framecount = 0
700 self.atframeheader = 1
Guido van Rossum4526f371992-09-07 15:28:57 +0000701 self.eofseen = 0
702 self.errorseen = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000703 try:
704 self.startpos = self.fp.tell()
705 self.canseek = 1
706 except IOError:
707 self.startpos = -1
708 self.canseek = 0
709 return self
710
711 def _readv0frameheader(self, fp):
712 t, ds, cs = readv0frameheader(fp)
713 ds = self._datasize
714 return (t, ds, cs)
715
716 def close(self):
717 self.fp.close()
718 del self.fp
719 del self._readframeheader
720
721 def setinfo(self, values):
722 raise CallError # Can't change info of input file!
723
724 def setsize(self, size):
725 raise CallError # Can't change info of input file!
726
727 def rewind(self):
728 if not self.canseek:
729 raise Error, self.filename + ': can\'t seek'
730 self.fp.seek(self.startpos)
731 self.framecount = 0
732 self.atframeheader = 1
Guido van Rossum4526f371992-09-07 15:28:57 +0000733 self.eofseen = 0
734 self.errorseen = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000735
736 def warmcache(self):
Guido van Rossum4526f371992-09-07 15:28:57 +0000737 print '[BasicVinFile.warmcache() not implemented]'
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000738
739 def printinfo(self):
740 print 'File: ', self.filename
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000741 print 'Size: ', getfilesize(self.filename)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000742 print 'Version: ', self.version
743 VideoParams.printinfo(self)
744
745 def getnextframe(self):
746 t, ds, cs = self.getnextframeheader()
747 data, cdata = self.getnextframedata(ds, cs)
748 return (t, data, cdata)
749
750 def skipnextframe(self):
751 t, ds, cs = self.getnextframeheader()
752 self.skipnextframedata(ds, cs)
753 return t
754
755 def getnextframeheader(self):
Guido van Rossum4526f371992-09-07 15:28:57 +0000756 if self.eofseen: raise EOFError
757 if self.errorseen: raise CallError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000758 if not self.atframeheader: raise CallError
759 self.atframeheader = 0
760 try:
761 return self._readframeheader(self.fp)
762 except Error, msg:
Guido van Rossum4526f371992-09-07 15:28:57 +0000763 self.errorseen = 1
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000764 # Patch up the error message
765 raise Error, self.filename + ': ' + msg
Guido van Rossum4526f371992-09-07 15:28:57 +0000766 except EOFError:
767 self.eofseen = 1
768 raise EOFError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000769
770 def getnextframedata(self, ds, cs):
Guido van Rossum4526f371992-09-07 15:28:57 +0000771 if self.eofseen: raise EOFError
772 if self.errorseen: raise CallError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000773 if self.atframeheader: raise CallError
774 if ds:
775 data = self.fp.read(ds)
Guido van Rossum4526f371992-09-07 15:28:57 +0000776 if len(data) < ds:
777 self.eofseen = 1
778 raise EOFError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000779 else:
780 data = ''
781 if cs:
782 cdata = self.fp.read(cs)
Guido van Rossum4526f371992-09-07 15:28:57 +0000783 if len(cdata) < cs:
784 self.eofseen = 1
785 raise EOFError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000786 else:
787 cdata = ''
788 self.atframeheader = 1
789 self.framecount = self.framecount + 1
790 return (data, cdata)
791
792 def skipnextframedata(self, ds, cs):
Guido van Rossum4526f371992-09-07 15:28:57 +0000793 if self.eofseen: raise EOFError
794 if self.errorseen: raise CallError
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000795 if self.atframeheader: raise CallError
796 # Note that this won't raise EOFError for a partial frame
797 # since there is no easy way to tell whether a seek
798 # ended up beyond the end of the file
799 if self.canseek:
800 self.fp.seek(ds + cs, 1) # Relative seek
801 else:
802 dummy = self.fp.read(ds + cs)
803 del dummy
804 self.atframeheader = 1
805 self.framecount = self.framecount + 1
806
807
Guido van Rossum9e3f3351992-09-29 13:40:47 +0000808# Subroutine to return a file's size in bytes
809
810def getfilesize(filename):
811 import os, stat
812 try:
813 st = os.stat(filename)
814 return st[stat.ST_SIZE]
815 except os.error:
816 return 0
817
818
Guido van Rossum269b2a21992-09-08 15:04:01 +0000819# Derived class implementing random access and index cached in the file
Guido van Rossum4526f371992-09-07 15:28:57 +0000820
821class RandomVinFile(BasicVinFile):
822
823 def initfp(self, fp, filename):
824 self = BasicVinFile.initfp(self, fp, filename)
825 self.index = []
826 return self
827
828 def warmcache(self):
829 if len(self.index) == 0:
Guido van Rossum269b2a21992-09-08 15:04:01 +0000830 try:
831 self.readcache()
832 except Error:
833 self.buildcache()
Guido van Rossum4526f371992-09-07 15:28:57 +0000834 else:
835 print '[RandomVinFile.warmcache(): too late]'
Guido van Rossum269b2a21992-09-08 15:04:01 +0000836 self.rewind()
837
838 def buildcache(self):
839 self.index = []
840 self.rewind()
841 while 1:
842 try: dummy = self.skipnextframe()
843 except EOFError: break
844 self.rewind()
845
846 def writecache(self):
847 # Raises IOerror if the file is not seekable & writable!
848 import marshal
849 if len(self.index) == 0:
850 self.buildcache()
851 if len(self.index) == 0:
852 raise Error, self.filename + ': No frames'
853 self.fp.seek(0, 2)
854 self.fp.write('\n/////CMIF/////\n')
855 pos = self.fp.tell()
856 data = `pos`
857 data = '\n-*-*-CMIF-*-*-\n' + data + ' '*(15-len(data)) + '\n'
858 try:
859 marshal.dump(self.index, self.fp)
860 self.fp.write(data)
861 self.fp.flush()
862 finally:
863 self.rewind()
864
865 def readcache(self):
866 # Raises Error if there is no cache in the file
867 import marshal
868 if len(self.index) <> 0:
869 raise CallError
870 self.fp.seek(-32, 2)
871 data = self.fp.read()
872 if data[:16] <> '\n-*-*-CMIF-*-*-\n' or data[-1:] <> '\n':
873 self.rewind()
874 raise Error, self.filename + ': No cache'
875 pos = eval(data[16:-1])
876 self.fp.seek(pos)
877 try:
878 self.index = marshal.load(self.fp)
879 except TypeError:
880 self.rewind()
881 raise Error, self.filename + ': Bad cache'
Guido van Rossum4526f371992-09-07 15:28:57 +0000882 self.rewind()
883
884 def getnextframeheader(self):
885 if self.framecount < len(self.index):
886 return self._getindexframeheader(self.framecount)
887 if self.framecount > len(self.index):
888 raise AssertError, \
889 'managed to bypass index?!?'
890 rv = BasicVinFile.getnextframeheader(self)
891 if self.canseek:
892 pos = self.fp.tell()
893 self.index.append(rv, pos)
894 return rv
895
896 def getrandomframe(self, i):
897 t, ds, cs = self.getrandomframeheader(i)
898 data, cdata = self.getnextframedata()
899 return t, ds, cs
900
901 def getrandomframeheader(self, i):
902 if i < 0: raise ValueError, 'negative frame index'
903 if not self.canseek:
904 raise Error, self.filename + ': can\'t seek'
905 if i < len(self.index):
906 return self._getindexframeheader(i)
907 if len(self.index) > 0:
908 rv = self.getrandomframeheader(len(self.index)-1)
909 else:
910 self.rewind()
911 rv = self.getnextframeheader()
912 while i > self.framecount:
913 self.skipnextframedata()
914 rv = self.getnextframeheader()
915 return rv
916
917 def _getindexframeheader(self, i):
918 (rv, pos) = self.index[i]
919 self.fp.seek(pos)
920 self.framecount = i
921 self.atframeheader = 0
922 self.eofseen = 0
923 self.errorseen = 0
924 return rv
925
926
927# Basic class for writing CMIF video files
928
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000929class BasicVoutFile(VideoParams):
930
931 def init(self, filename):
932 if filename == '-':
933 fp = sys.stdout
934 else:
935 fp = open(filename, 'w')
936 return self.initfp(fp, filename)
937
938 def initfp(self, fp, filename):
939 self = VideoParams.init(self)
940 self.fp = fp
941 self.filename = filename
Guido van Rossum4526f371992-09-07 15:28:57 +0000942 self.version = 3.0 # In case anyone inquries
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000943 self.headerwritten = 0
944 return self
945
946 def flush(self):
947 self.fp.flush()
948
949 def close(self):
950 self.fp.close()
951 del self.fp
952
953 def setinfo(self, values):
954 if self.headerwritten: raise CallError
955 VideoParams.setinfo(self, values)
956
957 def writeheader(self):
958 if self.headerwritten: raise CallError
959 writefileheader(self.fp, self.getinfo())
960 self.headerwritten = 1
961 self.atheader = 1
962 self.framecount = 0
963
964 def rewind(self):
965 self.fp.seek(0)
966 self.headerwritten = 0
967 self.atheader = 1
968 self.framecount = 0
969
970 def printinfo(self):
971 print 'File: ', self.filename
972 VideoParams.printinfo(self)
973
974 def writeframe(self, t, data, cdata):
975 if data: ds = len(data)
976 else: ds = 0
977 if cdata: cs = len(cdata)
978 else: cs = 0
979 self.writeframeheader(t, ds, cs)
980 self.writeframedata(data, cdata)
981
982 def writeframeheader(self, t, ds, cs):
983 if not self.headerwritten: self.writeheader()
984 if not self.atheader: raise CallError
Guido van Rossum4526f371992-09-07 15:28:57 +0000985 data = `(t, ds, cs)`
986 n = len(data)
987 if n < 63: data = data + ' '*(63-n)
988 self.fp.write(data + '\n')
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000989 self.atheader = 0
990
991 def writeframedata(self, data, cdata):
992 if not self.headerwritten or self.atheader: raise CallError
993 if data: self.fp.write(data)
994 if cdata: self.fp.write(cdata)
995 self.atheader = 1
996 self.framecount = self.framecount + 1
997
998
999# Classes that combine files with displayers and/or grabbers:
1000
Guido van Rossum4526f371992-09-07 15:28:57 +00001001class VinFile(RandomVinFile, Displayer):
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001002
1003 def initfp(self, fp, filename):
1004 self = Displayer.init(self)
Guido van Rossum4526f371992-09-07 15:28:57 +00001005 return RandomVinFile.initfp(self, fp, filename)
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001006
1007 def shownextframe(self):
1008 t, data, cdata = self.getnextframe()
1009 self.showframe(data, cdata)
1010 return t
1011
1012
1013class VoutFile(BasicVoutFile, Displayer, Grabber):
1014
1015 def initfp(self, fp, filename):
1016 self = Displayer.init(self)
1017## self = Grabber.init(self) # XXX not needed
1018 return BasicVoutFile.initfp(self, fp, filename)
1019
1020
1021# Simple test program (VinFile only)
1022
Guido van Rossum094a9de1991-12-03 16:50:00 +00001023def test():
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001024 import time
Guido van Rossum094a9de1991-12-03 16:50:00 +00001025 if sys.argv[1:]: filename = sys.argv[1]
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001026 else: filename = 'film.video'
Guido van Rossum094a9de1991-12-03 16:50:00 +00001027 vin = VinFile().init(filename)
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001028 vin.printinfo()
Guido van Rossum094a9de1991-12-03 16:50:00 +00001029 gl.foreground()
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001030 gl.prefsize(vin.getsize())
Guido van Rossum58b38cc1992-02-11 14:45:43 +00001031 wid = gl.winopen(filename)
1032 vin.initcolormap()
1033 t0 = time.millitimer()
1034 while 1:
Guido van Rossum4526f371992-09-07 15:28:57 +00001035 try: t, data, cdata = vin.getnextframe()
Guido van Rossum4045c2f1992-09-07 09:24:17 +00001036 except EOFError: break
1037 dt = t0 + t - time.millitimer()
1038 if dt > 0: time.millisleep(dt)
Guido van Rossum4526f371992-09-07 15:28:57 +00001039 vin.showframe(data, cdata)
Guido van Rossum58b38cc1992-02-11 14:45:43 +00001040 time.sleep(2)