# Classes to read and write CMIF video files. # (For a description of the CMIF video format, see cmif-file.ms.) # Layers of functionality: # # VideoParams: maintain essential parameters of a video file # Displayer: display a frame in a window (with some extra parameters) # Grabber: grab a frame from a window # BasicVinFile: read a CMIF video file # BasicVoutFile: write a CMIF video file # VinFile: BasicVinFile + Displayer # VoutFile: BasicVoutFile + Displayer + Grabber # Imported modules import sys import gl import GL import colorsys # Exception raised for various occasions Error = 'VFile.Error' # file format errors CallError = 'VFile.CallError' # bad call # Constants returned by gl.getdisplaymode(), from DMRGB = 0 DMSINGLE = 1 DMDOUBLE = 2 DMRGBDOUBLE = 5 # Max nr. of colormap entries to use MAXMAP = 4096 - 256 # Parametrizations of colormap handling based on color system. # (These functions are used via eval with a constructed argument!) def conv_grey(l, x, y): return colorsys.yiq_to_rgb(l, 0, 0) def conv_yiq(y, i, q): return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5) def conv_hls(l, h, s): return colorsys.hls_to_rgb(h, l, s) def conv_hsv(v, h, s): return colorsys.hsv_to_rgb(h, s, v) def conv_rgb(r, g, b): raise Error, 'Attempt to make RGB colormap' def conv_rgb8(rgb, d1, d2): rgb = int(rgb*255.0) r = (rgb >> 5) & 0x07 g = (rgb ) & 0x07 b = (rgb >> 3) & 0x03 return (r/7.0, g/7.0, b/3.0) # Choose one of the above based upon a color system name def choose_conversion(format): try: return eval('conv_' + format) except: raise Error, 'Unknown color system: ' + `format` # Routines to grab data, per color system (only a few really supported). # (These functions are used via eval with a constructed argument!) def grab_rgb(w, h, pf): if gl.getdisplaymode() <> DMRGB: raise Error, 'Sorry, can only grab rgb in single-buf rgbmode' if pf <> 1 and pf <> 0: raise Error, 'Sorry, only grab rgb with packfactor 1' return gl.lrectread(0, 0, w-1, h-1), None def grab_rgb8(w, h, pf): if gl.getdisplaymode() <> DMRGB: raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode' if pf <> 1 and pf <> 0: raise Error, 'Sorry, can only grab rgb8 with packfactor 1' r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED) g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN) b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE) if (r, g, b) <> (3, 3, 2): raise Error, 'Sorry, can only grab rgb8 on 8-bit Indigo' # XXX Dirty Dirty here. # XXX Set buffer to cmap mode, grab image and set it back. # XXX (Shouldn't be necessary???) gl.cmode() gl.gconfig() gl.pixmode(GL.PM_SIZE, 8) data = gl.lrectread(0, 0, w-1, h-1) data = data[:w*h] # BUG FIX for python lrectread gl.RGBmode() gl.gconfig() gl.pixmode(GL.PM_SIZE, 32) return data, None def grab_grey(w, h, pf): raise Error, 'Sorry, grabbing grey not implemented' def grab_yiq(w, h, pf): raise Error, 'Sorry, grabbing yiq not implemented' def grab_hls(w, h, pf): raise Error, 'Sorry, grabbing hls not implemented' def grab_hsv(w, h, pf): raise Error, 'Sorry, grabbing hsv not implemented' # Choose one of the above based upon a color system name def choose_grabber(format): try: return eval('grab_' + format) except: raise Error, 'Unknown color system: ' + `format` # Base class to manage video format parameters class VideoParams: # Initialize an instance. # Set all parameters to something decent # (except width and height are set to zero) def init(self): # Essential parameters self.format = 'grey' # color system used # Choose from: 'rgb', 'rgb8', 'hsv', 'yiq', 'hls' self.width = 0 # width of frame self.height = 0 # height of frame self.packfactor = 1 # expansion using rectzoom # if packfactor == 0, data is one 32-bit word/pixel; # otherwise, data is one byte/pixel self.c0bits = 8 # bits in first color dimension self.c1bits = 0 # bits in second color dimension self.c2bits = 0 # bits in third color dimension self.offset = 0 # colormap index offset (XXX ???) self.chrompack = 0 # set if separate chrominance data return self # Set the frame width and height (e.g. from gl.getsize()) def setsize(self, size): self.width, self.height = size # Retrieve the frame width and height (e.g. for gl.prefsize()) def getsize(self): return (self.width, self.height) # Set all parameters. # This does limited validity checking; # if the check fails no parameters are changed def setinfo(self, values): (self.format, self.width, self.height, self.packfactor,\ self.c0bits, self.c1bits, self.c2bits, self.offset, \ self.chrompack) = values # Retrieve all parameters in a format suitable for a subsequent # call to setinfo() def getinfo(self): return (self.format, self.width, self.height, self.packfactor,\ self.c0bits, self.c1bits, self.c2bits, self.offset, \ self.chrompack) # Write the relevant bits to stdout def printinfo(self): print 'Format: ', self.format print 'Size: ', self.width, 'x', self.height print 'Pack: ', self.packfactor, '; chrom:', self.chrompack print 'Bits: ', self.c0bits, self.c1bits, self.c2bits print 'Offset: ', self.offset # Class to display video frames in a window. # It is the caller's responsibility to ensure that the correct window # is current when using showframe(), initcolormap() and clear() class Displayer(VideoParams): # Initialize an instance. # This does not need a current window def init(self): self = VideoParams.init(self) # User-settable parameters self.magnify = 1.0 # frame magnification factor self.xorigin = 0 # x frame offset self.yorigin = 0 # y frame offset (from bottom) self.quiet = 0 # if set, don't print messages self.fallback = 1 # allow fallback to grey # Internal flags self.colormapinited = 0 # must initialize window self.skipchrom = 0 # don't skip chrominance data return self # setinfo() must reset some internal flags def setinfo(self, values): VideoParams.setinfo(values) self.colormapinited = 0 self.skipchrom = 0 # Show one frame, initializing the window if necessary def showframe(self, data, chromdata): if not self.colormapinited: self.initcolormap() w, h, pf = self.width, self.height, self.packfactor factor = self.magnify if pf: factor = factor * pf if chromdata and not self.skipchrom: cp = self.chrompack cw = (w+cp-1)/cp ch = (h+cp-1)/cp gl.rectzoom(factor*cp, factor*cp) gl.pixmode(GL.PM_SIZE, 16) gl.writemask(self.mask - ((1 << self.c0bits) - 1)) gl.lrectwrite(self.xorigin, self.yorigin, \ self.xorigin + cw - 1, self.yorigin + ch - 1, \ chromdata) # if pf: gl.writemask((1 << self.c0bits) - 1) gl.pixmode(GL.PM_SIZE, 8) w = w/pf h = h/pf gl.rectzoom(factor, factor) gl.lrectwrite(self.xorigin, self.yorigin, \ self.xorigin + w - 1, self.yorigin + h - 1, data) # Initialize the window: set RGB or colormap mode as required, # fill in the colormap, and clear the window def initcolormap(self): if self.format == 'rgb': gl.RGBmode() gl.gconfig() self.colormapinited = 1 gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.clear() return gl.cmode() gl.gconfig() self.skipchrom = 0 if self.offset == 0: self.mask = 0x7ff else: self.mask = 0xfff if not self.quiet: sys.stderr.write('Initializing color map...') self._initcmap() self.colormapinited = 1 self.clear() if not self.quiet: sys.stderr.write(' Done.\n') # Clear the window def clear(self): if not self.colormapinited: raise CallError if self.offset == 0: gl.color(0x800) gl.clear() else: gl.clear() # Do the hard work for initializing the colormap def _initcmap(self): convcolor = choose_conversion(self.format) maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE) if maxbits > 11: maxbits = 11 c0bits, c1bits, c2bits = self.c0bits, self.c1bits, self.c2bits if c0bits+c1bits+c2bits > maxbits: if self.fallback and c0bits < maxbits: # Cannot display frames in this mode, use grey self.skipchrom = 1 c1bits = c2bits = 0 convcolor = choose_conversion('grey') else: raise Error, 'Sorry, '+`maxbits`+ \ ' bits max on this machine' maxc0 = 1 << c0bits maxc1 = 1 << c1bits maxc2 = 1 << c2bits if self.offset == 0 and maxbits == 11: offset = 2048 else: offset = self.offset if maxbits <> 11: offset = offset & ((1< 0: time.millisleep(dt) time.sleep(2)