99
1010Error = 'VFile.Error' # Exception
1111
12+ # Missing from GL.py:
13+ DMRGB = 0
14+
1215MAXMAP = 4096 - 256
1316
1417def conv_grey (l ,x ,y ): return colorsys .yiq_to_rgb (l ,0 ,0 )
@@ -17,6 +20,12 @@ def conv_hls (l,h,s): return colorsys.hls_to_rgb(h,l,s)
1720def conv_hsv (v ,h ,s ): return colorsys .hsv_to_rgb (h ,s ,v )
1821def conv_rgb (r ,g ,b ):
1922 raise Error , 'Attempt to make RGB colormap'
23+ def 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 )
2029
2130# Class VinFile represents a video file used for input.
2231#
@@ -39,6 +48,7 @@ def conv_rgb (r,g,b):
3948# These writable data members provide additional parametrization:
4049# magnify
4150# xorigin, yorigin
51+ # fallback
4252
4353class VinFile ():
4454
@@ -54,6 +64,8 @@ def initfp(self, (fp, filename)):
5464 self .colormapinited = 0
5565 self .magnify = 1.0
5666 self .xorigin = self .yorigin = 0
67+ self .fallback = 1
68+ self .skipchrom = 0
5769 self .fp = fp
5870 self .filename = filename
5971 #
@@ -90,7 +102,11 @@ def initfp(self, (fp, filename)):
90102 try :
91103 self .format , rest = eval (line [:- 1 ])
92104 if self .format == 'rgb' :
93- pass
105+ self .offset = 0
106+ self .c0bits = 0
107+ self .c1bits = 0
108+ self .c2bits = 0
109+ self .chrompack = 0
94110 elif self .format == 'grey' :
95111 self .offset = 0
96112 self .c0bits = rest
@@ -121,16 +137,46 @@ def initfp(self, (fp, filename)):
121137 self .packfactor = 2
122138 except :
123139 raise Error , self .filename + ': bad (w,h,pf) info'
140+ self .frameno = 0
141+ self .framecache = []
142+ self .hascache = 0
124143 #
125144 return self
126145
146+ def warmcache (self ):
147+ if self .hascache : return
148+ n = 0
149+ try :
150+ while 1 :
151+ void = self .skipnextframe ()
152+ n = n + 1
153+ except EOFError :
154+ pass
155+ if not self .hascache :
156+ raise Error , 'Cannot warm cache'
157+
158+
159+ #
160+ # getinfo returns all info pertaining to a film. The returned tuple
161+ # can be passed to VoutFile.setinfo()
162+ #
163+ def getinfo (self ):
164+ return (self .format , self .width , self .height , self .packfactor ,\
165+ self .c0bits , self .c1bits , self .c2bits , self .offset , \
166+ self .chrompack )
167+
127168 # rewind() raises Error if the header is bad (which can only
128169 # happen if the file was written to since opened).
129170
130171 def rewind (self ):
131172 self .fp .seek (0 )
132173 x = self .initfp (self .fp , self .filename )
133174
175+ def position (self ):
176+ if self .frameno >= len (self .framecache ):
177+ raise EOFError
178+ self .fp .seek (self .framecache [self .frameno ][0 ])
179+
134180 # getnextframe() raises EOFError (built-in) if there is no next frame,
135181 # or if the next frame is broken.
136182 # So to getnextframeheader(), getnextframedata() and skipnextframe().
@@ -141,6 +187,9 @@ def getnextframe(self):
141187 return time , data , chromdata
142188
143189 def getnextframedata (self , (size , chromsize )):
190+ if self .hascache :
191+ self .position ()
192+ self .frameno = self .frameno + 1
144193 data = self .fp .read (size )
145194 if len (data ) <> size : raise EOFError
146195 if chromsize :
@@ -157,6 +206,9 @@ def skipnextframe(self):
157206 return time
158207
159208 def skipnextframedata (self , (size , chromsize )):
209+ if self .hascache :
210+ self .frameno = self .frameno + 1
211+ return
160212 # Note that this won't raise EOFError for a partial frame.
161213 try :
162214 self .fp .seek (size + chromsize , 1 ) # Relative seek
@@ -165,8 +217,13 @@ def skipnextframedata(self, (size, chromsize)):
165217 dummy = self .fp .read (size + chromsize )
166218
167219 def getnextframeheader (self ):
220+ if self .hascache :
221+ if self .frameno >= len (self .framecache ):
222+ raise EOFError
223+ return self .framecache [self .frameno ][1 ]
168224 line = self .fp .readline ()
169225 if not line :
226+ self .hascache = 1
170227 raise EOFError
171228 #
172229 w , h , pf = self .width , self .height , self .packfactor
@@ -191,6 +248,8 @@ def getnextframeheader(self):
191248 time , size , chromsize = x
192249 except :
193250 raise Error , self .filename + ': bad frame header'
251+ cdata = (self .fp .tell (), (time , size , chromsize ))
252+ self .framecache .append (cdata )
194253 return time , size , chromsize
195254
196255 def shownextframe (self ):
@@ -204,7 +263,7 @@ def showframe(self, (data, chromdata)):
204263 self .initcolormap ()
205264 factor = self .magnify
206265 if pf : factor = factor * pf
207- if chromdata :
266+ if chromdata and not self . skipchrom :
208267 cp = self .chrompack
209268 cw = (w + cp - 1 )/ cp
210269 ch = (h + cp - 1 )/ cp
@@ -232,49 +291,188 @@ def initcolormap(self):
232291 return
233292 gl .cmode ()
234293 gl .gconfig ()
294+ self .skipchrom = 0
235295 sys .stderr .write ('Initializing color map...' )
236- initcmap (self .convcolor , self .c0bits , self .c1bits , \
237- self .c2bits , self .chrompack , self .offset )
296+ self .initcmap ()
238297 sys .stderr .write (' Done.\n ' )
239298 if self .offset == 0 :
240299 gl .color (0x800 )
300+ gl .clear ()
241301 self .mask = 0x7ff
242302 else :
243303 self .mask = 0xfff
244304 gl .clear ()
245305
246306
247- def initcmap (convcolor , c0bits , c1bits , c2bits , chrompack , offset ):
248- if c0bits + c1bits + c2bits > 11 :
249- raise Error , 'Sorry, 11 bits max'
250- maxc0 = 1 << c0bits
251- maxc1 = 1 << c1bits
252- maxc2 = 1 << c2bits
253- if offset == 0 :
254- offset = 2048
255- for i in range (offset , MAXMAP ):
256- gl .mapcolor (i , 0 , 255 , 0 )
257- for c0 in range (maxc0 ):
258- c0v = c0 / float (maxc0 - 1 )
259- for c1 in range (maxc1 ):
260- if maxc1 == 1 :
261- c1v = 0
307+ def initcmap (self ):
308+ maxbits = gl .getgdesc (GL .GD_BITS_NORM_SNG_CMODE )
309+ if maxbits > 11 :
310+ maxbits = 11
311+ c0bits , c1bits , c2bits = self .c0bits , self .c1bits , self .c2bits
312+ if c0bits + c1bits + c2bits > maxbits :
313+ if self .fallback and c0bits < maxbits :
314+ # Cannot display film in this mode, use mono
315+ self .skipchrom = 1
316+ c1bits = c2bits = 0
317+ self .convcolor = conv_grey
262318 else :
263- c1v = c1 / float (maxc1 - 1 )
264- for c2 in range (maxc2 ):
265- if maxc2 == 1 :
266- c2v = 0
319+ raise Error , 'Sorry, ' + `maxbits` + \
320+ ' bits max on this machine'
321+ maxc0 = 1 << c0bits
322+ maxc1 = 1 << c1bits
323+ maxc2 = 1 << c2bits
324+ if self .offset == 0 and maxbits == 11 :
325+ offset = 2048
326+ else :
327+ offset = self .offset
328+ if maxbits <> 11 :
329+ offset = offset & ((1 << maxbits )- 1 )
330+ #for i in range(512, MAXMAP):
331+ # gl.mapcolor(i, 0, 0, 0)
332+ #void = gl.qtest() # Should be gl.gflush()
333+ for c0 in range (maxc0 ):
334+ c0v = c0 / float (maxc0 - 1 )
335+ for c1 in range (maxc1 ):
336+ if maxc1 == 1 :
337+ c1v = 0
267338 else :
268- c2v = c2 / float (maxc2 - 1 )
269- index = offset + c0 + \
270- (c1 << c0bits ) + (c2 << (c0bits + c1bits ))
271- rv , gv , bv = convcolor (c0v , c1v , c2v )
272- r , g , b = \
273- int (rv * 255.0 ), int (gv * 255.0 ), int (bv * 255.0 )
274- if index < MAXMAP :
275- gl .mapcolor (index , r , g , b )
339+ c1v = c1 / float (maxc1 - 1 )
340+ for c2 in range (maxc2 ):
341+ if maxc2 == 1 :
342+ c2v = 0
343+ else :
344+ c2v = c2 / float (maxc2 - 1 )
345+ index = offset + c0 + (c1 << c0bits ) + \
346+ (c2 << (c0bits + c1bits ))
347+ rv , gv , bv = self .convcolor ( \
348+ c0v , c1v , c2v )
349+ r , g , b = int (rv * 255.0 ), \
350+ int (gv * 255.0 ), int (bv * 255.0 )
351+ if index < MAXMAP :
352+ gl .mapcolor (index , r , g , b )
353+ void = gl .gflush ()
354+
355+ #
356+ # A set of routines to grab images from windows
357+ #
358+ def grab_rgb (w , h , pf ):
359+ if gl .getdisplaymode () <> DMRGB :
360+ raise Error , 'Sorry, can only grab rgb in single-buf rgbmode'
361+ if pf <> 1 and pf <> 0 :
362+ raise Error , 'Sorry, only grab with packfactor=1'
363+ return gl .lrectread (0 , 0 , w - 1 , h - 1 ), None
364+
365+ def grab_rgb8 (w , h , pf ):
366+ if gl .getdisplaymode () <> DMRGB :
367+ raise Error , 'Sorry, can only grab rgb in single-buf rgbmode'
368+ if pf <> 1 and pf <> 0 :
369+ raise Error , 'Sorry, can only grab with packfactor=1'
370+ r = gl .getgdesc (GL .GD_BITS_NORM_SNG_RED )
371+ g = gl .getgdesc (GL .GD_BITS_NORM_SNG_GREEN )
372+ b = gl .getgdesc (GL .GD_BITS_NORM_SNG_BLUE )
373+ if (r ,g ,b ) <> (3 ,3 ,2 ):
374+ raise Error , 'Sorry, can only grab rgb8 on 8-bit Indigo'
375+ # Dirty Dirty here. Set buffer to cmap mode, grab image and set it back
376+ gl .cmode ()
377+ gl .gconfig ()
378+ gl .pixmode (GL .PM_SIZE , 8 )
379+ data = gl .lrectread (0 , 0 , w - 1 , h - 1 )
380+ data = data [:w * h ] # BUG FIX for python lrectread
381+ gl .RGBmode ()
382+ gl .gconfig ()
383+ gl .pixmode (GL .PM_SIZE , 32 )
384+ return data , None
385+
386+ def grab_grey (w , h , pf ):
387+ raise Error , 'Sorry, grabbing grey not implemented'
388+
389+ def grab_yiq (w , h , pf ):
390+ raise Error , 'Sorry, grabbing yiq not implemented'
391+
392+ def grab_hls (w , h , pf ):
393+ raise Error , 'Sorry, grabbing hls not implemented'
394+
395+ def grab_hsv (w , h , pf ):
396+ raise Error , 'Sorry, grabbing hsv not implemented'
397+
398+ #
399+ # The class VoutFile is not as well-designed (and tested) as VinFile.
400+ # Notably it will accept almost any garbage and write it to the video
401+ # output file
402+ #
403+ class VoutFile ():
404+ def init (self , filename ):
405+ if filename == '-' :
406+ return self .initfp (sys .stdout , filename )
407+ else :
408+ return self .initfp (open (filename ,'w' ), filename )
409+
410+ def initfp (self , fp , filename ):
411+ self .fp = fp
412+ self .format = 'grey'
413+ self .width = self .height = 0
414+ self .packfactor = 1
415+ self .c0bits = 8
416+ self .c1bits = self .c2bits = 0
417+ self .offset = 0
418+ self .chrompack = 0
419+ self .headerwritten = 0
420+ return self
421+
422+ def close (self ):
423+ self .fp .close ()
424+ self .initfp (None , None )
425+
426+ def setinfo (self , values ):
427+ self .format , self .width , self .height , self .packfactor ,\
428+ self .c0bits , self .c1bits , self .c2bits , self .offset , \
429+ self .chrompack = values
430+
431+ def writeheader (self ):
432+ self .headerwritten = 1
433+ if self .format == 'rgb' :
434+ self .packfactor = 0
435+ elif self .packfactor == 0 :
436+ self .packfactor = 1
437+ self .fp .write ('CMIF video 3.0\n ' )
438+ if self .format == 'rgb' :
439+ data = ('rgb' , 0 )
440+ elif self .format == 'grey' :
441+ data = ('grey' , 0 )
442+ else :
443+ data = (self .format , (self .c0bits , self .c1bits , \
444+ self .c2bits , self .chrompack , self .offset ))
445+ self .fp .write (`data` + '\n ' )
446+ data = (self .width , self .height , self .packfactor )
447+ self .fp .write (`data` + '\n ' )
448+ try :
449+ self ._grabber = eval ('grab_' + self .format )
450+ except :
451+ raise Error , 'unknown colorsys: ' + self .format
452+
453+ def writeframeheader (self , data ):
454+ if not self .headerwritten :
455+ raise Error , 'Writing frame data before header'
456+ # XXXX Should we sanity check here?
457+ self .fp .write (`data` + '\n ' )
276458
459+ def writeframedata (self , data , chromdata ):
460+ # XXXX Check sizes here
461+ self .fp .write (data )
462+ if chromdata :
463+ self .fp .write (chromdata )
277464
465+ def writeframe (self , time , data , chromdata ):
466+ if chromdata :
467+ clen = len (chromdata )
468+ else :
469+ clen = 0
470+ self .writeframeheader ((time , len (data ), clen ))
471+ self .writeframedata (data , chromdata )
472+
473+ def grabframe (self ):
474+ return self ._grabber (self .width , self .height , self .packfactor )
475+
278476def test ():
279477 import sys , time
280478 filename = 'film.video'
@@ -303,3 +501,4 @@ def test():
303501 vin .showframe (data , chromdata )
304502 print 'Done.'
305503 time .sleep (2 )
504+
0 commit comments