blob: e43bd9446d15fe99855a4c1646dd28160a3c2f68 [file] [log] [blame]
Guido van Rossum094a9de1991-12-03 16:50:00 +00001# VFile -- two classes for Video Files.
2# VinFile -- for video input.
3# VoutFile -- for video output.
4
5import sys
6import gl
7import GL
Guido van Rossum444339d1991-12-03 16:52:40 +00008import colorsys
Guido van Rossum094a9de1991-12-03 16:50:00 +00009
10Error = 'VFile.Error' # Exception
11
Guido van Rossum2080b341992-02-28 15:59:23 +000012# Missing from GL.py:
13DMRGB = 0
14
Guido van Rossum58b38cc1992-02-11 14:45:43 +000015MAXMAP = 4096 - 256
16
Guido van Rossum444339d1991-12-03 16:52:40 +000017def conv_grey(l,x,y): return colorsys.yiq_to_rgb(l,0,0)
18def conv_yiq (y,i,q): return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5)
19def conv_hls (l,h,s): return colorsys.hls_to_rgb(h,l,s)
20def conv_hsv (v,h,s): return colorsys.hsv_to_rgb(h,s,v)
21def conv_rgb (r,g,b):
Guido van Rossum58b38cc1992-02-11 14:45:43 +000022 raise Error, 'Attempt to make RGB colormap'
Guido van Rossum2080b341992-02-28 15:59:23 +000023def conv_rgb8(rgb,d1,d2):
24 rgb = int(rgb*255.0)
25 r = (rgb >> 5) & 0x07
26 g = (rgb ) & 0x07
27 b = (rgb >> 3) & 0x03
28 return (r/7.0, g/7.0, b/3.0)
Guido van Rossum094a9de1991-12-03 16:50:00 +000029
30# Class VinFile represents a video file used for input.
31#
32# It has the following methods:
33# init(filename)
34# initfp(fp, filename)
Guido van Rossum9a35d571992-08-20 11:51:47 +000035# reopen()
Guido van Rossum094a9de1991-12-03 16:50:00 +000036# rewind()
Guido van Rossum7268c931992-08-18 21:11:18 +000037# getnextframe()
38# skipnextframe()
39# (and many more)
Guido van Rossum094a9de1991-12-03 16:50:00 +000040#
41# The following read-only data members provide public information:
42# version
43# filename
44# width, height
45# packfactor
Guido van Rossum444339d1991-12-03 16:52:40 +000046# c0bits, c1bits, c2bits, chrompack
47# offset
48# format
Guido van Rossum094a9de1991-12-03 16:50:00 +000049#
50# These writable data members provide additional parametrization:
Guido van Rossum58b38cc1992-02-11 14:45:43 +000051# magnify
Guido van Rossum094a9de1991-12-03 16:50:00 +000052# xorigin, yorigin
Guido van Rossum2080b341992-02-28 15:59:23 +000053# fallback
Guido van Rossum094a9de1991-12-03 16:50:00 +000054
Guido van Rossum094a9de1991-12-03 16:50:00 +000055
Guido van Rossum094a9de1991-12-03 16:50:00 +000056
Guido van Rossum9ee7e151992-08-25 12:29:30 +000057# XXX it's a total mess now -- VFile is a new base class
58# XXX to support common functionality (e.g. showframe)
Guido van Rossum094a9de1991-12-03 16:50:00 +000059
Guido van Rossum9ee7e151992-08-25 12:29:30 +000060class VFile:
Guido van Rossum2080b341992-02-28 15:59:23 +000061
62 #
63 # getinfo returns all info pertaining to a film. The returned tuple
64 # can be passed to VoutFile.setinfo()
65 #
66 def getinfo(self):
67 return (self.format, self.width, self.height, self.packfactor,\
68 self.c0bits, self.c1bits, self.c2bits, self.offset, \
69 self.chrompack)
70
Guido van Rossum9a35d571992-08-20 11:51:47 +000071 # reopen() raises Error if the header is bad (which can only
Guido van Rossum094a9de1991-12-03 16:50:00 +000072 # happen if the file was written to since opened).
73
Guido van Rossum9a35d571992-08-20 11:51:47 +000074 def reopen(self):
Guido van Rossum094a9de1991-12-03 16:50:00 +000075 self.fp.seek(0)
76 x = self.initfp(self.fp, self.filename)
77
Guido van Rossum9ee7e151992-08-25 12:29:30 +000078 def setconvcolor(self):
Guido van Rossum094a9de1991-12-03 16:50:00 +000079 try:
Guido van Rossum9ee7e151992-08-25 12:29:30 +000080 self.convcolor = eval('conv_'+self.format)
Guido van Rossum094a9de1991-12-03 16:50:00 +000081 except:
Guido van Rossum9ee7e151992-08-25 12:29:30 +000082 raise Error, \
83 self.filename + ': unknown colorsys ' + self.format
Guido van Rossum094a9de1991-12-03 16:50:00 +000084
Guido van Rossum9a35d571992-08-20 11:51:47 +000085 def showframe(self, data, chromdata):
Guido van Rossum094a9de1991-12-03 16:50:00 +000086 w, h, pf = self.width, self.height, self.packfactor
Guido van Rossum444339d1991-12-03 16:52:40 +000087 if not self.colormapinited:
88 self.initcolormap()
Guido van Rossum094a9de1991-12-03 16:50:00 +000089 factor = self.magnify
90 if pf: factor = factor * pf
Guido van Rossum2080b341992-02-28 15:59:23 +000091 if chromdata and not self.skipchrom:
Guido van Rossum094a9de1991-12-03 16:50:00 +000092 cp = self.chrompack
93 cw = (w+cp-1)/cp
94 ch = (h+cp-1)/cp
95 gl.rectzoom(factor*cp, factor*cp)
96 gl.pixmode(GL.PM_SIZE, 16)
Guido van Rossum444339d1991-12-03 16:52:40 +000097 gl.writemask(self.mask - ((1 << self.c0bits) - 1))
Guido van Rossum094a9de1991-12-03 16:50:00 +000098 gl.lrectwrite(self.xorigin, self.yorigin, \
99 self.xorigin + cw - 1, self.yorigin + ch - 1, \
100 chromdata)
101 #
102 if pf:
Guido van Rossum444339d1991-12-03 16:52:40 +0000103 gl.writemask((1 << self.c0bits) - 1)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000104 gl.pixmode(GL.PM_SIZE, 8)
105 gl.rectzoom(factor, factor)
106 w = w/pf
107 h = h/pf
108 gl.lrectwrite(self.xorigin, self.yorigin, \
109 self.xorigin + w - 1, self.yorigin + h - 1, data)
110
111 def initcolormap(self):
Guido van Rossum094a9de1991-12-03 16:50:00 +0000112 self.colormapinited = 1
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000113 if self.format == 'rgb':
114 gl.RGBmode()
115 gl.gconfig()
Guido van Rossum33a8d421992-08-21 12:41:23 +0000116 gl.RGBcolor(200, 200, 200)
117 gl.clear()
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000118 return
119 gl.cmode()
120 gl.gconfig()
Guido van Rossum2080b341992-02-28 15:59:23 +0000121 self.skipchrom = 0
Guido van Rossum9a35d571992-08-20 11:51:47 +0000122 if not self.quiet:
123 sys.stderr.write('Initializing color map...')
Guido van Rossum2080b341992-02-28 15:59:23 +0000124 self.initcmap()
Guido van Rossume0be2b31992-09-01 14:45:57 +0000125 self.clear()
Guido van Rossum9a35d571992-08-20 11:51:47 +0000126 if not self.quiet:
127 sys.stderr.write(' Done.\n')
Guido van Rossume0be2b31992-09-01 14:45:57 +0000128
129 def clear(self):
Guido van Rossum444339d1991-12-03 16:52:40 +0000130 if self.offset == 0:
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000131 gl.color(0x800)
Guido van Rossum2080b341992-02-28 15:59:23 +0000132 gl.clear()
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000133 self.mask = 0x7ff
Guido van Rossum444339d1991-12-03 16:52:40 +0000134 else:
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000135 self.mask = 0xfff
Guido van Rossume0be2b31992-09-01 14:45:57 +0000136 gl.clear()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000137
Guido van Rossum2080b341992-02-28 15:59:23 +0000138 def initcmap(self):
139 maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE)
140 if maxbits > 11:
141 maxbits = 11
142 c0bits, c1bits, c2bits = self.c0bits, self.c1bits, self.c2bits
143 if c0bits+c1bits+c2bits > maxbits:
144 if self.fallback and c0bits < maxbits:
145 # Cannot display film in this mode, use mono
146 self.skipchrom = 1
147 c1bits = c2bits = 0
148 self.convcolor = conv_grey
Guido van Rossum094a9de1991-12-03 16:50:00 +0000149 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000150 raise Error, 'Sorry, '+`maxbits`+ \
151 ' bits max on this machine'
152 maxc0 = 1 << c0bits
153 maxc1 = 1 << c1bits
154 maxc2 = 1 << c2bits
155 if self.offset == 0 and maxbits == 11:
156 offset = 2048
157 else:
158 offset = self.offset
159 if maxbits <> 11:
160 offset = offset & ((1<<maxbits)-1)
161 #for i in range(512, MAXMAP):
162 # gl.mapcolor(i, 0, 0, 0)
163 #void = gl.qtest() # Should be gl.gflush()
164 for c0 in range(maxc0):
165 c0v = c0/float(maxc0-1)
166 for c1 in range(maxc1):
167 if maxc1 == 1:
168 c1v = 0
Guido van Rossum094a9de1991-12-03 16:50:00 +0000169 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000170 c1v = c1/float(maxc1-1)
171 for c2 in range(maxc2):
172 if maxc2 == 1:
173 c2v = 0
174 else:
175 c2v = c2/float(maxc2-1)
176 index = offset + c0 + (c1<<c0bits) + \
177 (c2 << (c0bits+c1bits))
178 rv, gv, bv = self.convcolor( \
179 c0v, c1v, c2v)
180 r, g, b = int(rv*255.0), \
181 int(gv*255.0), int(bv*255.0)
182 if index < MAXMAP:
183 gl.mapcolor(index, r, g, b)
184 void = gl.gflush()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000185
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000186
187
188class VinFile(VFile):
189
190 # init() and initfp() raise Error if the header is bad.
191 # init() raises whatever open() raises if the file can't be opened.
192
193 def init(self, filename):
194 if filename == '-':
195 return self.initfp(sys.stdin, filename)
196 return self.initfp(open(filename, 'r'), filename)
197
198 def initfp(self, fp, filename):
199 self.colormapinited = 0
200 self.magnify = 1.0
201 self.xorigin = self.yorigin = 0
202 self.fallback = 1
203 self.skipchrom = 0
204 self.fp = fp
205 self.filename = filename
206 self.quiet = 0
207 #
208 line = self.fp.readline()
209 if line == 'CMIF video 1.0\n':
210 self.version = 1.0
211 elif line == 'CMIF video 2.0\n':
212 self.version = 2.0
213 elif line == 'CMIF video 3.0\n':
214 self.version = 3.0
215 else:
216 raise Error, self.filename + ': bad video format'
217 #
218 if self.version < 2.0:
219 self.c0bits, self.c1bits, self.c2bits = 8, 0, 0
220 self.chrompack = 0
221 self.offset = 0
222 self.format = 'grey'
223 elif self.version == 2.0:
224 line = self.fp.readline()
225 try:
226 self.c0bits, self.c1bits, self.c2bits, \
227 self.chrompack = eval(line[:-1])
228 if self.c1bits or self.c2bits:
229 self.format = 'yiq'
230 else:
231 self.format = 'grey'
232 self.offset = 0
233 except:
234 raise Error, \
235 self.filename + ': bad 2.0 color info'
236 elif self.version == 3.0:
237 line = self.fp.readline()
238 try:
239 self.format, rest = eval(line[:-1])
240 if self.format == 'rgb':
241 self.offset = 0
242 self.c0bits = 0
243 self.c1bits = 0
244 self.c2bits = 0
245 self.chrompack = 0
246 elif self.format == 'grey':
247 self.offset = 0
248 self.c0bits = rest
249 self.c1bits = self.c2bits = \
250 self.chrompack = 0
251 else:
252 self.c0bits,self.c1bits,self.c2bits,\
253 self.chrompack,self.offset = rest
254 except:
255 raise Error, \
256 self.filename + ': bad 3.0 color info'
257
258 self.setconvcolor()
259 #
260 line = self.fp.readline()
261 try:
262 x = eval(line[:-1])
263 if self.version > 1.0 or len(x) == 3:
264 self.width, self.height, self.packfactor = x
265 if self.packfactor == 0:
266 self.format = 'rgb'
267 else:
268 sef.width, self.height = x
269 self.packfactor = 2
270 except:
271 raise Error, self.filename + ': bad (w,h,pf) info'
272 self.frameno = 0
273 self.framecache = []
274 self.hascache = 0
275 #
276 return self
277
278 def warmcache(self):
279 if self.hascache: return
280 n = 0
281 try:
282 while 1:
283 void = self.skipnextframe()
284 n = n + 1
285 except EOFError:
286 pass
287 if not self.hascache:
288 raise Error, 'Cannot warm cache'
289
290 def close(self):
291 self.fp.close()
292 self.fp = None
293
294 def rewind(self):
295 if self.hascache:
296 self.frameno = 0
297 else:
298 self.reopen()
299
300 def position(self):
301 if self.frameno >= len(self.framecache):
302 raise EOFError
303 self.fp.seek(self.framecache[self.frameno][0])
304
305 # getnextframe() raises EOFError (built-in) if there is no next frame,
306 # or if the next frame is broken.
307 # So to getnextframeheader(), getnextframedata() and skipnextframe().
308
309 def getnextframe(self):
310 time, size, chromsize = self.getnextframeheader()
311 data, chromdata = self.getnextframedata(size, chromsize)
312 return time, data, chromdata
313
314 def getnextframedata(self, size, chromsize):
315 if self.hascache:
316 self.position()
317 self.frameno = self.frameno + 1
318 data = self.fp.read(size)
319 if len(data) <> size: raise EOFError
320 if chromsize:
321 chromdata = self.fp.read(chromsize)
322 if len(chromdata) <> chromsize: raise EOFError
323 else:
324 chromdata = None
325 #
326 return data, chromdata
327
328 def skipnextframe(self):
329 time, size, chromsize = self.getnextframeheader()
330 self.skipnextframedata(size, chromsize)
331 return time
332
333 def skipnextframedata(self, size, chromsize):
334 if self.hascache:
335 self.frameno = self.frameno + 1
336 return
337 # Note that this won't raise EOFError for a partial frame.
338 try:
339 self.fp.seek(size + chromsize, 1) # Relative seek
340 except:
341 # Assume it's a pipe -- read the data to discard it
342 dummy = self.fp.read(size + chromsize)
343
344 def getnextframeheader(self):
345 if self.hascache:
346 if self.frameno >= len(self.framecache):
347 raise EOFError
348 return self.framecache[self.frameno][1]
349 line = self.fp.readline()
350 if not line:
351 self.hascache = 1
352 raise EOFError
353 #
354 w, h, pf = self.width, self.height, self.packfactor
355 try:
356 x = eval(line[:-1])
357 if type(x) in (type(0), type(0.0)):
358 time = x
359 if pf == 0:
360 size = w * h * 4
361 else:
362 size = (w/pf) * (h/pf)
363 elif len(x) == 2:
364 time, size = x
365 cp = self.chrompack
366 if cp:
367 cw = (w + cp - 1) / cp
368 ch = (h + cp - 1) / cp
369 chromsize = 2 * cw * ch
370 else:
371 chromsize = 0
372 else:
373 time, size, chromsize = x
374 except:
375 raise Error, self.filename + ': bad frame header'
376 cdata = (self.fp.tell(), (time, size, chromsize))
377 self.framecache.append(cdata)
378 return time, size, chromsize
379
380 def shownextframe(self):
381 time, data, chromdata = self.getnextframe()
382 self.showframe(data, chromdata)
383 return time
384
Guido van Rossum2080b341992-02-28 15:59:23 +0000385#
386# A set of routines to grab images from windows
387#
388def grab_rgb(w, h, pf):
389 if gl.getdisplaymode() <> DMRGB:
390 raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
391 if pf <> 1 and pf <> 0:
392 raise Error, 'Sorry, only grab with packfactor=1'
393 return gl.lrectread(0, 0, w-1, h-1), None
394
395def grab_rgb8(w, h, pf):
396 if gl.getdisplaymode() <> DMRGB:
397 raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
398 if pf <> 1 and pf <> 0:
399 raise Error, 'Sorry, can only grab with packfactor=1'
400 r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED)
401 g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN)
402 b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
403 if (r,g,b) <> (3,3,2):
404 raise Error, 'Sorry, can only grab rgb8 on 8-bit Indigo'
405 # Dirty Dirty here. Set buffer to cmap mode, grab image and set it back
406 gl.cmode()
407 gl.gconfig()
408 gl.pixmode(GL.PM_SIZE, 8)
409 data = gl.lrectread(0, 0, w-1, h-1)
410 data = data[:w*h] # BUG FIX for python lrectread
411 gl.RGBmode()
412 gl.gconfig()
413 gl.pixmode(GL.PM_SIZE, 32)
414 return data, None
415
416def grab_grey(w, h, pf):
417 raise Error, 'Sorry, grabbing grey not implemented'
Guido van Rossum094a9de1991-12-03 16:50:00 +0000418
Guido van Rossum2080b341992-02-28 15:59:23 +0000419def grab_yiq(w, h, pf):
420 raise Error, 'Sorry, grabbing yiq not implemented'
421
422def grab_hls(w, h, pf):
423 raise Error, 'Sorry, grabbing hls not implemented'
424
425def grab_hsv(w, h, pf):
426 raise Error, 'Sorry, grabbing hsv not implemented'
427
428#
429# The class VoutFile is not as well-designed (and tested) as VinFile.
430# Notably it will accept almost any garbage and write it to the video
431# output file
432#
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000433class VoutFile(VFile):
Guido van Rossum2080b341992-02-28 15:59:23 +0000434 def init(self, filename):
435 if filename == '-':
436 return self.initfp(sys.stdout, filename)
437 else:
438 return self.initfp(open(filename,'w'), filename)
439
440 def initfp(self, fp, filename):
441 self.fp = fp
442 self.format = 'grey'
443 self.width = self.height = 0
444 self.packfactor = 1
445 self.c0bits = 8
446 self.c1bits = self.c2bits = 0
447 self.offset = 0
448 self.chrompack = 0
449 self.headerwritten = 0
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000450 self.quiet = 0
451 self.magnify = 1
452 self.setconvcolor()
453 self.xorigin = self.yorigin = 0
Guido van Rossum2080b341992-02-28 15:59:23 +0000454 return self
455
456 def close(self):
457 self.fp.close()
Guido van Rossumbc0eb991992-08-18 17:00:51 +0000458 x = self.initfp(None, None)
Guido van Rossum2080b341992-02-28 15:59:23 +0000459
460 def setinfo(self, values):
461 self.format, self.width, self.height, self.packfactor,\
462 self.c0bits, self.c1bits, self.c2bits, self.offset, \
463 self.chrompack = values
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000464 self.setconvcolor()
Guido van Rossum2080b341992-02-28 15:59:23 +0000465
466 def writeheader(self):
467 self.headerwritten = 1
468 if self.format == 'rgb':
469 self.packfactor = 0
470 elif self.packfactor == 0:
471 self.packfactor = 1
472 self.fp.write('CMIF video 3.0\n')
473 if self.format == 'rgb':
474 data = ('rgb', 0)
475 elif self.format == 'grey':
Guido van Rossum9a35d571992-08-20 11:51:47 +0000476 data = ('grey', self.c0bits)
Guido van Rossum2080b341992-02-28 15:59:23 +0000477 else:
478 data = (self.format, (self.c0bits, self.c1bits, \
479 self.c2bits, self.chrompack, self.offset))
480 self.fp.write(`data`+'\n')
481 data = (self.width, self.height, self.packfactor)
482 self.fp.write(`data`+'\n')
483 try:
484 self._grabber = eval('grab_' + self.format)
485 except:
486 raise Error, 'unknown colorsys: ' + self.format
487
488 def writeframeheader(self, data):
489 if not self.headerwritten:
490 raise Error, 'Writing frame data before header'
491 # XXXX Should we sanity check here?
492 self.fp.write(`data`+'\n')
493
494 def writeframedata(self, data, chromdata):
495 # XXXX Check sizes here
496 self.fp.write(data)
497 if chromdata:
498 self.fp.write(chromdata)
499
500 def writeframe(self, time, data, chromdata):
501 if chromdata:
502 clen = len(chromdata)
503 else:
504 clen = 0
505 self.writeframeheader((time, len(data), clen))
506 self.writeframedata(data, chromdata)
507
508 def grabframe(self):
509 return self._grabber(self.width, self.height, self.packfactor)
510
Guido van Rossum094a9de1991-12-03 16:50:00 +0000511def test():
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000512 import sys, time
Guido van Rossum094a9de1991-12-03 16:50:00 +0000513 filename = 'film.video'
514 if sys.argv[1:]: filename = sys.argv[1]
515 vin = VinFile().init(filename)
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000516 print 'File: ', filename
Guido van Rossum444339d1991-12-03 16:52:40 +0000517 print 'Version: ', vin.version
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000518 print 'Size: ', vin.width, 'x', vin.height
519 print 'Pack: ', vin.packfactor, '; chrom:', vin.chrompack
520 print 'Bits: ', vin.c0bits, vin.c1bits, vin.c2bits
521 print 'Format: ', vin.format
522 print 'Offset: ', vin.offset
Guido van Rossum094a9de1991-12-03 16:50:00 +0000523 gl.foreground()
524 gl.prefsize(vin.width, vin.height)
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000525 wid = gl.winopen(filename)
526 vin.initcolormap()
527 t0 = time.millitimer()
528 while 1:
529 try:
530 t, data, chromdata = vin.getnextframe()
531 except EOFError:
532 break
533 dt = t + t0 - time.millitimer()
534 if dt > 0:
535 time.millisleep(dt)
536 vin.showframe(data, chromdata)
537 print 'Done.'
538 time.sleep(2)
Guido van Rossum2080b341992-02-28 15:59:23 +0000539