blob: 0f723063749681a4feb659b527c60ab4c8567ccc [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
14
15
16# Imported modules
Guido van Rossum094a9de1991-12-03 16:50:00 +000017
18import sys
19import gl
20import GL
Guido van Rossum444339d1991-12-03 16:52:40 +000021import colorsys
Guido van Rossum094a9de1991-12-03 16:50:00 +000022
Guido van Rossum094a9de1991-12-03 16:50:00 +000023
Guido van Rossum4045c2f1992-09-07 09:24:17 +000024# Exception raised for various occasions
25
26Error = 'VFile.Error' # file format errors
27CallError = 'VFile.CallError' # bad call
28
29
30# Constants returned by gl.getdisplaymode(), from <gl/get.h>
31
Guido van Rossum2080b341992-02-28 15:59:23 +000032DMRGB = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +000033DMSINGLE = 1
34DMDOUBLE = 2
35DMRGBDOUBLE = 5
36
37
38# Max nr. of colormap entries to use
Guido van Rossum2080b341992-02-28 15:59:23 +000039
Guido van Rossum58b38cc1992-02-11 14:45:43 +000040MAXMAP = 4096 - 256
41
Guido van Rossum4045c2f1992-09-07 09:24:17 +000042
43# Parametrizations of colormap handling based on color system.
44# (These functions are used via eval with a constructed argument!)
45
46def conv_grey(l, x, y):
47 return colorsys.yiq_to_rgb(l, 0, 0)
48
49def conv_yiq(y, i, q):
50 return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5)
51
52def conv_hls(l, h, s):
53 return colorsys.hls_to_rgb(h, l, s)
54
55def conv_hsv(v, h, s):
56 return colorsys.hsv_to_rgb(h, s, v)
57
58def conv_rgb(r, g, b):
Guido van Rossum58b38cc1992-02-11 14:45:43 +000059 raise Error, 'Attempt to make RGB colormap'
Guido van Rossum4045c2f1992-09-07 09:24:17 +000060
61def conv_rgb8(rgb, d1, d2):
Guido van Rossum2080b341992-02-28 15:59:23 +000062 rgb = int(rgb*255.0)
63 r = (rgb >> 5) & 0x07
64 g = (rgb ) & 0x07
65 b = (rgb >> 3) & 0x03
66 return (r/7.0, g/7.0, b/3.0)
Guido van Rossum094a9de1991-12-03 16:50:00 +000067
Guido van Rossum4045c2f1992-09-07 09:24:17 +000068
69# Choose one of the above based upon a color system name
70
71def choose_conversion(format):
72 try:
73 return eval('conv_' + format)
74 except:
75 raise Error, 'Unknown color system: ' + `format`
Guido van Rossum094a9de1991-12-03 16:50:00 +000076
Guido van Rossum094a9de1991-12-03 16:50:00 +000077
Guido van Rossum4045c2f1992-09-07 09:24:17 +000078# Routines to grab data, per color system (only a few really supported).
79# (These functions are used via eval with a constructed argument!)
Guido van Rossum094a9de1991-12-03 16:50:00 +000080
Guido van Rossum4045c2f1992-09-07 09:24:17 +000081def grab_rgb(w, h, pf):
82 if gl.getdisplaymode() <> DMRGB:
83 raise Error, 'Sorry, can only grab rgb in single-buf rgbmode'
84 if pf <> 1 and pf <> 0:
85 raise Error, 'Sorry, only grab rgb with packfactor 1'
86 return gl.lrectread(0, 0, w-1, h-1), None
Guido van Rossum094a9de1991-12-03 16:50:00 +000087
Guido van Rossum4045c2f1992-09-07 09:24:17 +000088def grab_rgb8(w, h, pf):
89 if gl.getdisplaymode() <> DMRGB:
90 raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode'
91 if pf <> 1 and pf <> 0:
92 raise Error, 'Sorry, can only grab rgb8 with packfactor 1'
93 r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED)
94 g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN)
95 b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
96 if (r, g, b) <> (3, 3, 2):
97 raise Error, 'Sorry, can only grab rgb8 on 8-bit Indigo'
98 # XXX Dirty Dirty here.
99 # XXX Set buffer to cmap mode, grab image and set it back.
100 # XXX (Shouldn't be necessary???)
101 gl.cmode()
102 gl.gconfig()
103 gl.pixmode(GL.PM_SIZE, 8)
104 data = gl.lrectread(0, 0, w-1, h-1)
105 data = data[:w*h] # BUG FIX for python lrectread
106 gl.RGBmode()
107 gl.gconfig()
108 gl.pixmode(GL.PM_SIZE, 32)
109 return data, None
Guido van Rossum2080b341992-02-28 15:59:23 +0000110
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000111def grab_grey(w, h, pf):
112 raise Error, 'Sorry, grabbing grey not implemented'
113
114def grab_yiq(w, h, pf):
115 raise Error, 'Sorry, grabbing yiq not implemented'
116
117def grab_hls(w, h, pf):
118 raise Error, 'Sorry, grabbing hls not implemented'
119
120def grab_hsv(w, h, pf):
121 raise Error, 'Sorry, grabbing hsv not implemented'
122
123
124# Choose one of the above based upon a color system name
125
126def choose_grabber(format):
127 try:
128 return eval('grab_' + format)
129 except:
130 raise Error, 'Unknown color system: ' + `format`
131
132
133# Base class to manage video format parameters
134
135class VideoParams:
136
137 # Initialize an instance.
138 # Set all parameters to something decent
139 # (except width and height are set to zero)
140
141 def init(self):
142 # Essential parameters
143 self.format = 'grey' # color system used
144 # Choose from: 'rgb', 'rgb8', 'hsv', 'yiq', 'hls'
145 self.width = 0 # width of frame
146 self.height = 0 # height of frame
147 self.packfactor = 1 # expansion using rectzoom
148 # if packfactor == 0, data is one 32-bit word/pixel;
149 # otherwise, data is one byte/pixel
150 self.c0bits = 8 # bits in first color dimension
151 self.c1bits = 0 # bits in second color dimension
152 self.c2bits = 0 # bits in third color dimension
153 self.offset = 0 # colormap index offset (XXX ???)
154 self.chrompack = 0 # set if separate chrominance data
155 return self
156
157 # Set the frame width and height (e.g. from gl.getsize())
158
159 def setsize(self, size):
160 self.width, self.height = size
161
162 # Retrieve the frame width and height (e.g. for gl.prefsize())
163
164 def getsize(self):
165 return (self.width, self.height)
166
167 # Set all parameters.
168 # This does limited validity checking;
169 # if the check fails no parameters are changed
170
171 def setinfo(self, values):
172 (self.format, self.width, self.height, self.packfactor,\
173 self.c0bits, self.c1bits, self.c2bits, self.offset, \
174 self.chrompack) = values
175
176 # Retrieve all parameters in a format suitable for a subsequent
177 # call to setinfo()
178
Guido van Rossum2080b341992-02-28 15:59:23 +0000179 def getinfo(self):
180 return (self.format, self.width, self.height, self.packfactor,\
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000181 self.c0bits, self.c1bits, self.c2bits, self.offset, \
182 self.chrompack)
Guido van Rossum2080b341992-02-28 15:59:23 +0000183
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000184 # Write the relevant bits to stdout
Guido van Rossum094a9de1991-12-03 16:50:00 +0000185
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000186 def printinfo(self):
187 print 'Format: ', self.format
188 print 'Size: ', self.width, 'x', self.height
189 print 'Pack: ', self.packfactor, '; chrom:', self.chrompack
190 print 'Bits: ', self.c0bits, self.c1bits, self.c2bits
191 print 'Offset: ', self.offset
Guido van Rossum094a9de1991-12-03 16:50:00 +0000192
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000193
194# Class to display video frames in a window.
195# It is the caller's responsibility to ensure that the correct window
196# is current when using showframe(), initcolormap() and clear()
197
198class Displayer(VideoParams):
199
200 # Initialize an instance.
201 # This does not need a current window
202
203 def init(self):
204 self = VideoParams.init(self)
205 # User-settable parameters
206 self.magnify = 1.0 # frame magnification factor
207 self.xorigin = 0 # x frame offset
208 self.yorigin = 0 # y frame offset (from bottom)
209 self.quiet = 0 # if set, don't print messages
210 self.fallback = 1 # allow fallback to grey
211 # Internal flags
212 self.colormapinited = 0 # must initialize window
213 self.skipchrom = 0 # don't skip chrominance data
214 return self
215
216 # setinfo() must reset some internal flags
217
218 def setinfo(self, values):
219 VideoParams.setinfo(values)
220 self.colormapinited = 0
221 self.skipchrom = 0
222
223 # Show one frame, initializing the window if necessary
Guido van Rossum094a9de1991-12-03 16:50:00 +0000224
Guido van Rossum9a35d571992-08-20 11:51:47 +0000225 def showframe(self, data, chromdata):
Guido van Rossum444339d1991-12-03 16:52:40 +0000226 if not self.colormapinited:
227 self.initcolormap()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000228 w, h, pf = self.width, self.height, self.packfactor
Guido van Rossum094a9de1991-12-03 16:50:00 +0000229 factor = self.magnify
230 if pf: factor = factor * pf
Guido van Rossum2080b341992-02-28 15:59:23 +0000231 if chromdata and not self.skipchrom:
Guido van Rossum094a9de1991-12-03 16:50:00 +0000232 cp = self.chrompack
233 cw = (w+cp-1)/cp
234 ch = (h+cp-1)/cp
235 gl.rectzoom(factor*cp, factor*cp)
236 gl.pixmode(GL.PM_SIZE, 16)
Guido van Rossum444339d1991-12-03 16:52:40 +0000237 gl.writemask(self.mask - ((1 << self.c0bits) - 1))
Guido van Rossum094a9de1991-12-03 16:50:00 +0000238 gl.lrectwrite(self.xorigin, self.yorigin, \
239 self.xorigin + cw - 1, self.yorigin + ch - 1, \
240 chromdata)
241 #
242 if pf:
Guido van Rossum444339d1991-12-03 16:52:40 +0000243 gl.writemask((1 << self.c0bits) - 1)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000244 gl.pixmode(GL.PM_SIZE, 8)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000245 w = w/pf
246 h = h/pf
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000247 gl.rectzoom(factor, factor)
Guido van Rossum094a9de1991-12-03 16:50:00 +0000248 gl.lrectwrite(self.xorigin, self.yorigin, \
249 self.xorigin + w - 1, self.yorigin + h - 1, data)
250
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000251 # Initialize the window: set RGB or colormap mode as required,
252 # fill in the colormap, and clear the window
253
Guido van Rossum094a9de1991-12-03 16:50:00 +0000254 def initcolormap(self):
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000255 if self.format == 'rgb':
256 gl.RGBmode()
257 gl.gconfig()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000258 self.colormapinited = 1
259 gl.RGBcolor(200, 200, 200) # XXX rather light grey
Guido van Rossum33a8d421992-08-21 12:41:23 +0000260 gl.clear()
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000261 return
262 gl.cmode()
263 gl.gconfig()
Guido van Rossum2080b341992-02-28 15:59:23 +0000264 self.skipchrom = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000265 if self.offset == 0:
266 self.mask = 0x7ff
267 else:
268 self.mask = 0xfff
Guido van Rossum9a35d571992-08-20 11:51:47 +0000269 if not self.quiet:
270 sys.stderr.write('Initializing color map...')
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000271 self._initcmap()
272 self.colormapinited = 1
Guido van Rossume0be2b31992-09-01 14:45:57 +0000273 self.clear()
Guido van Rossum9a35d571992-08-20 11:51:47 +0000274 if not self.quiet:
275 sys.stderr.write(' Done.\n')
Guido van Rossume0be2b31992-09-01 14:45:57 +0000276
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000277 # Clear the window
278
Guido van Rossume0be2b31992-09-01 14:45:57 +0000279 def clear(self):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000280 if not self.colormapinited: raise CallError
Guido van Rossum444339d1991-12-03 16:52:40 +0000281 if self.offset == 0:
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000282 gl.color(0x800)
Guido van Rossum2080b341992-02-28 15:59:23 +0000283 gl.clear()
Guido van Rossum444339d1991-12-03 16:52:40 +0000284 else:
Guido van Rossume0be2b31992-09-01 14:45:57 +0000285 gl.clear()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000286
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000287 # Do the hard work for initializing the colormap
288
289 def _initcmap(self):
290 convcolor = choose_conversion(self.format)
Guido van Rossum2080b341992-02-28 15:59:23 +0000291 maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE)
292 if maxbits > 11:
293 maxbits = 11
294 c0bits, c1bits, c2bits = self.c0bits, self.c1bits, self.c2bits
295 if c0bits+c1bits+c2bits > maxbits:
296 if self.fallback and c0bits < maxbits:
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000297 # Cannot display frames in this mode, use grey
Guido van Rossum2080b341992-02-28 15:59:23 +0000298 self.skipchrom = 1
299 c1bits = c2bits = 0
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000300 convcolor = choose_conversion('grey')
Guido van Rossum094a9de1991-12-03 16:50:00 +0000301 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000302 raise Error, 'Sorry, '+`maxbits`+ \
303 ' bits max on this machine'
304 maxc0 = 1 << c0bits
305 maxc1 = 1 << c1bits
306 maxc2 = 1 << c2bits
307 if self.offset == 0 and maxbits == 11:
308 offset = 2048
309 else:
310 offset = self.offset
311 if maxbits <> 11:
312 offset = offset & ((1<<maxbits)-1)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000313 # XXX why is this here?
314 # for i in range(512, MAXMAP):
Guido van Rossum2080b341992-02-28 15:59:23 +0000315 # gl.mapcolor(i, 0, 0, 0)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000316 # gl.gflush()
Guido van Rossum2080b341992-02-28 15:59:23 +0000317 for c0 in range(maxc0):
318 c0v = c0/float(maxc0-1)
319 for c1 in range(maxc1):
320 if maxc1 == 1:
321 c1v = 0
Guido van Rossum094a9de1991-12-03 16:50:00 +0000322 else:
Guido van Rossum2080b341992-02-28 15:59:23 +0000323 c1v = c1/float(maxc1-1)
324 for c2 in range(maxc2):
325 if maxc2 == 1:
326 c2v = 0
327 else:
328 c2v = c2/float(maxc2-1)
329 index = offset + c0 + (c1<<c0bits) + \
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000330 (c2 << (c0bits+c1bits))
Guido van Rossum2080b341992-02-28 15:59:23 +0000331 if index < MAXMAP:
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000332 rv, gv, bv = \
333 convcolor(c0v, c1v, c2v)
334 r, g, b = int(rv*255.0), \
335 int(gv*255.0), \
336 int(bv*255.0)
Guido van Rossum2080b341992-02-28 15:59:23 +0000337 gl.mapcolor(index, r, g, b)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000338 gl.gflush() # send the colormap changes to the X server
Guido van Rossum094a9de1991-12-03 16:50:00 +0000339
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000340
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000341# Class to grab frames from a window.
342# (This has fewer user-settable parameters than Displayer.)
343# It is the caller's responsibility to initialize the window and to
344# ensure that it is current when using grabframe()
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000345
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000346class Grabber(VideoParams):
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000347
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000348 # XXX The init() method of VideoParams is just fine, for now
Guido van Rossum9ee7e151992-08-25 12:29:30 +0000349
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000350 # Grab a frame.
351 # Return (data, chromdata) just like getnextframe().
Guido van Rossum2080b341992-02-28 15:59:23 +0000352
353 def grabframe(self):
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000354 grabber = choose_grabber(self.format)
355 return grabber(self.width, self.height, self.packfactor)
356
357
358# Read a CMIF video file header.
359# Return (version, values) where version is 0.0, 1.0, 2.0 or 3.0,
360# and values is ready for setinfo().
361# Raise Error if there is an error in the info
362
363def readfileheader(fp, filename):
364 #
365 # Get identifying header
366 #
367 line = fp.readline(20)
368 if line == 'CMIF video 0.0\n':
369 version = 0.0
370 elif line == 'CMIF video 1.0\n':
371 version = 1.0
372 elif line == 'CMIF video 2.0\n':
373 version = 2.0
374 elif line == 'CMIF video 3.0\n':
375 version = 3.0
376 else:
377 # XXX Could be version 0.0 without identifying header
378 raise Error, \
379 filename + ': Unrecognized file header: ' + `line`[:20]
380 #
381 # Get color encoding info
382 #
383 if version <= 1.0:
384 format = 'grey'
385 c0bits, c1bits, c2bits = 8, 0, 0
386 chrompack = 0
387 offset = 0
388 elif version == 2.0:
389 line = fp.readline()
390 try:
391 c0bits, c1bits, c2bits, chrompack = eval(line[:-1])
392 except:
393 raise Error, filename + ': Bad 2.0 color info'
394 if c1bits or c2bits:
395 format = 'yiq'
396 else:
397 format = 'grey'
398 offset = 0
399 elif version == 3.0:
400 line = fp.readline()
401 try:
402 format, rest = eval(line[:-1])
403 except:
404 raise Error, filename + ': Bad 3.0 color info'
405 if format == 'rgb':
406 c0bits = c1bits = c2bits = 0
407 chrompack = 0
408 offset = 0
409 elif format == 'grey':
410 c0bits = rest
411 c1bits = c2bits = 0
412 chrompack = 0
413 offset = 0
414 else:
415 try:
416 c0bits, c1bits, c2bits, chrompack, offset = rest
417 except:
418 raise Error, filename + ': Bad 3.0 color info'
419 #
420 # Get frame geometry info
421 #
422 line = fp.readline()
423 try:
424 x = eval(line[:-1])
425 except:
426 raise Error, filename + ': Bad (w,h,pf) info'
427 if len(x) == 3:
428 width, height, packfactor = x
429 if packfactor == 0 and version < 3.0:
430 format = 'rgb'
431 elif len(x) == 2 and version <= 1.0:
432 width, height = x
433 packfactor = 2
434 else:
435 raise Error, filename + ': Bad (w,h,pf) info'
436 #
437 # Return (version, values)
438 #
439 values = (format, width, height, packfactor, \
440 c0bits, c1bits, c2bits, offset, chrompack)
441 return (version, values)
442
443
444# Read a *frame* header -- separate functions per version.
445# Return (timecode, datasize, chromdatasize).
446# Raise EOFError if end of data is reached.
447# Raise Error if data is bad.
448
449def readv0frameheader(fp):
450 line = fp.readline()
451 if not line or line == '\n': raise EOFError
452 try:
453 t = eval(line[:-1])
454 except:
455 raise Error, 'Bad 0.0 frame header'
456 return (t, 0, 0)
457
458def readv1frameheader(fp):
459 line = fp.readline()
460 if not line or line == '\n': raise EOFError
461 try:
462 t, datasize = eval(line[:-1])
463 except:
464 raise Error, 'Bad 1.0 frame header'
465 return (t, datasize, 0)
466
467def readv2frameheader(fp):
468 line = fp.readline()
469 if not line or line == '\n': raise EOFError
470 try:
471 t, datasize = eval(line[:-1])
472 except:
473 raise Error, 'Bad 2.0 frame header'
474 return (t, datasize, 0)
475
476def readv3frameheader(fp):
477 line = fp.readline()
478 if not line or line == '\n': raise EOFError
479 try:
480 t, datasize, chromdatasize = x = eval(line[:-1])
481 except:
482 raise Error, 'Bad 3.0 frame header'
483 return x
484
485
486# Write a CMIF video file header (always version 3.0)
487
488def writefileheader(fp, values):
489 (format, width, height, packfactor, \
490 c0bits, c1bits, c2bits, offset, chrompack) = values
491 #
492 # Write identifying header
493 #
494 fp.write('CMIF video 3.0\n')
495 #
496 # Write color encoding info
497 #
498 if format == 'rgb':
499 data = ('rgb', 0)
500 elif format == 'grey':
501 data = ('grey', c0bits)
502 else:
503 data = (format, (c0bits, c1bits, c2bits, chrompack, offset))
504 fp.write(`data`+'\n')
505 #
506 # Write frame geometry info
507 #
508 if format == 'rgb':
509 packfactor = 0
510 elif packfactor == 0:
511 packfactor = 1
512 data = (width, height, packfactor)
513 fp.write(`data`+'\n')
514
515
516# Basic class for reading CMIF video files
517
518class BasicVinFile(VideoParams):
519
520 def init(self, filename):
521 if filename == '-':
522 fp = sys.stdin
523 else:
524 fp = open(filename, 'r')
525 return self.initfp(fp, filename)
526
527 def initfp(self, fp, filename):
528 self = VideoParams.init(self)
529 self.fp = fp
530 self.filename = filename
531 self.version, values = readfileheader(fp, filename)
532 VideoParams.setinfo(self, values)
533 if self.version == 0.0:
534 w, h, pf = self.width, self.height, self.packfactor
535 if pf == 0:
536 self._datasize = w*h*4
537 else:
538 self._datasize = (w/pf) * (h/pf)
539 self._readframeheader = self._readv0frameheader
540 elif self.version == 1.0:
541 self._readframeheader = readv1frameheader
542 elif self.version == 2.0:
543 self._readframeheader = readv2frameheader
544 elif self.version == 3.0:
545 self._readframeheader = readv3frameheader
546 else:
547 raise Error, \
548 filename + ': Bad version: ' + `self.version`
549 self.framecount = 0
550 self.atframeheader = 1
551 try:
552 self.startpos = self.fp.tell()
553 self.canseek = 1
554 except IOError:
555 self.startpos = -1
556 self.canseek = 0
557 return self
558
559 def _readv0frameheader(self, fp):
560 t, ds, cs = readv0frameheader(fp)
561 ds = self._datasize
562 return (t, ds, cs)
563
564 def close(self):
565 self.fp.close()
566 del self.fp
567 del self._readframeheader
568
569 def setinfo(self, values):
570 raise CallError # Can't change info of input file!
571
572 def setsize(self, size):
573 raise CallError # Can't change info of input file!
574
575 def rewind(self):
576 if not self.canseek:
577 raise Error, self.filename + ': can\'t seek'
578 self.fp.seek(self.startpos)
579 self.framecount = 0
580 self.atframeheader = 1
581
582 def warmcache(self):
583 pass
584
585 def printinfo(self):
586 print 'File: ', self.filename
587 print 'Version: ', self.version
588 VideoParams.printinfo(self)
589
590 def getnextframe(self):
591 t, ds, cs = self.getnextframeheader()
592 data, cdata = self.getnextframedata(ds, cs)
593 return (t, data, cdata)
594
595 def skipnextframe(self):
596 t, ds, cs = self.getnextframeheader()
597 self.skipnextframedata(ds, cs)
598 return t
599
600 def getnextframeheader(self):
601 if not self.atframeheader: raise CallError
602 self.atframeheader = 0
603 try:
604 return self._readframeheader(self.fp)
605 except Error, msg:
606 # Patch up the error message
607 raise Error, self.filename + ': ' + msg
608
609 def getnextframedata(self, ds, cs):
610 if self.atframeheader: raise CallError
611 if ds:
612 data = self.fp.read(ds)
613 if len(data) < ds: raise EOFError
614 else:
615 data = ''
616 if cs:
617 cdata = self.fp.read(cs)
618 if len(cdata) < cs: raise EOFerror
619 else:
620 cdata = ''
621 self.atframeheader = 1
622 self.framecount = self.framecount + 1
623 return (data, cdata)
624
625 def skipnextframedata(self, ds, cs):
626 if self.atframeheader: raise CallError
627 # Note that this won't raise EOFError for a partial frame
628 # since there is no easy way to tell whether a seek
629 # ended up beyond the end of the file
630 if self.canseek:
631 self.fp.seek(ds + cs, 1) # Relative seek
632 else:
633 dummy = self.fp.read(ds + cs)
634 del dummy
635 self.atframeheader = 1
636 self.framecount = self.framecount + 1
637
638
639class BasicVoutFile(VideoParams):
640
641 def init(self, filename):
642 if filename == '-':
643 fp = sys.stdout
644 else:
645 fp = open(filename, 'w')
646 return self.initfp(fp, filename)
647
648 def initfp(self, fp, filename):
649 self = VideoParams.init(self)
650 self.fp = fp
651 self.filename = filename
652 self.version = 3.0 # In case anyone inquires
653 self.headerwritten = 0
654 return self
655
656 def flush(self):
657 self.fp.flush()
658
659 def close(self):
660 self.fp.close()
661 del self.fp
662
663 def setinfo(self, values):
664 if self.headerwritten: raise CallError
665 VideoParams.setinfo(self, values)
666
667 def writeheader(self):
668 if self.headerwritten: raise CallError
669 writefileheader(self.fp, self.getinfo())
670 self.headerwritten = 1
671 self.atheader = 1
672 self.framecount = 0
673
674 def rewind(self):
675 self.fp.seek(0)
676 self.headerwritten = 0
677 self.atheader = 1
678 self.framecount = 0
679
680 def printinfo(self):
681 print 'File: ', self.filename
682 VideoParams.printinfo(self)
683
684 def writeframe(self, t, data, cdata):
685 if data: ds = len(data)
686 else: ds = 0
687 if cdata: cs = len(cdata)
688 else: cs = 0
689 self.writeframeheader(t, ds, cs)
690 self.writeframedata(data, cdata)
691
692 def writeframeheader(self, t, ds, cs):
693 if not self.headerwritten: self.writeheader()
694 if not self.atheader: raise CallError
695 self.fp.write(`(t, ds, cs)` + '\n')
696 self.atheader = 0
697
698 def writeframedata(self, data, cdata):
699 if not self.headerwritten or self.atheader: raise CallError
700 if data: self.fp.write(data)
701 if cdata: self.fp.write(cdata)
702 self.atheader = 1
703 self.framecount = self.framecount + 1
704
705
706# Classes that combine files with displayers and/or grabbers:
707
708class VinFile(BasicVinFile, Displayer):
709
710 def initfp(self, fp, filename):
711 self = Displayer.init(self)
712 return BasicVinFile.initfp(self, fp, filename)
713
714 def shownextframe(self):
715 t, data, cdata = self.getnextframe()
716 self.showframe(data, cdata)
717 return t
718
719
720class VoutFile(BasicVoutFile, Displayer, Grabber):
721
722 def initfp(self, fp, filename):
723 self = Displayer.init(self)
724## self = Grabber.init(self) # XXX not needed
725 return BasicVoutFile.initfp(self, fp, filename)
726
727
728# Simple test program (VinFile only)
729
Guido van Rossum094a9de1991-12-03 16:50:00 +0000730def test():
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000731 import time
Guido van Rossum094a9de1991-12-03 16:50:00 +0000732 if sys.argv[1:]: filename = sys.argv[1]
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000733 else: filename = 'film.video'
Guido van Rossum094a9de1991-12-03 16:50:00 +0000734 vin = VinFile().init(filename)
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000735 vin.printinfo()
Guido van Rossum094a9de1991-12-03 16:50:00 +0000736 gl.foreground()
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000737 gl.prefsize(vin.getsize())
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000738 wid = gl.winopen(filename)
739 vin.initcolormap()
740 t0 = time.millitimer()
741 while 1:
Guido van Rossum4045c2f1992-09-07 09:24:17 +0000742 try: t = vin.shownextframe()
743 except EOFError: break
744 dt = t0 + t - time.millitimer()
745 if dt > 0: time.millisleep(dt)
Guido van Rossum58b38cc1992-02-11 14:45:43 +0000746 time.sleep(2)