Commits

Jack Jansen  committed 5745e55

Added mono, grey2 and grey4 formats

  • Participants
  • Parent commits c01c95e
  • Branches legacy-trunk

Comments (0)

Files changed (5)

File Demo/sgi/video/LiveVideoIn.py

 		if realvw < vw:
 			realvw = vw
 		self.realwidth, self.realheight = v.QuerySize(realvw, vh)
-		if not type in ('rgb8', 'grey', 'mono'):
-			raise 'Incorrent video data type'
+		if not type in ('rgb8', 'grey', 'mono', 'grey2', 'grey4'):
+			raise 'Incorrent video data type', type
 		self.type = type
-		if type in ('grey', 'mono'):
+		if type in ('grey', 'grey4', 'grey2', 'mono'):
 			v.SetParam([SV.COLOR, SV.MONO, SV.INPUT_BYPASS, 1])
 		else:
 			v.SetParam([SV.COLOR, SV.DEFAULT_COLOR, \
 			if self.type == 'mono':
 				self.data = imageop.dither2mono(self.data, \
 					  self.width, self.height)
+			elif self.type == 'grey2':
+				self.data = imageop.dither2grey2(self.data, \
+					  self.width, self.height)
+			elif self.type == 'grey4':
+				self.data = imageop.grey2grey4(self.data, \
+					  self.width, self.height)
 		data = self.data[self.dataoffset:self.dataoffset+self.pktsize]
 		lpos = self.lpos
 		self.dataoffset = self.dataoffset + self.pktsize
 		if self.type == 'mono':
 			self.lpos = self.lpos + self.lpp*8
+		elif self.type == 'grey2':
+			self.lpos = self.lpos + self.lpp*4
+		elif self.type == 'grey4':
+			self.lpos = self.lpos + self.lpp*2
 		else:
 			self.lpos = self.lpos + self.lpp
 		return lpos, data

File Demo/sgi/video/LiveVideoOut.py

 		self.vw = vw
 		self.vh = vh
 		self.disp = Displayer().init()
-		if not type in ('rgb8', 'grey', 'mono'):
-			raise 'Incorrent live video output type'
+		if not type in ('rgb8', 'grey', 'mono', 'grey2', 'grey4'):
+			raise 'Incorrent live video output type', type
 		info = (type, vw, vh, 1, 8, 0, 0, 0, 0)
 		self.disp.setinfo(info)
 		self.wid = wid
 			# Unfortunately size-check is difficult for
 			# packed video
 			nline = (len(data)*8)/self.vw
+		elif self.disp.format == 'grey2':
+			nline = (len(data)*4)/self.vw
+		elif self.disp.format == 'grey4':
+			nline = (len(data)*2)/self.vw
 		else:
 			nline = len(data)/self.vw
 			if nline*self.vw <> len(data):
 
 	def close(self):
 		pass
+
+	# Call this to set optional mirroring of video
+	def setmirror(self, mirrored):
+		f, w, h, pf, c0, c1, c2, o, cp = self.disp.getinfo()
+		if type(pf) == type(()):
+			xpf, ypf = pf
+		else:
+			xpf = ypf = pf
+		xpf = abs(xpf)
+		if mirrored:
+			xpf = -xpf
+		info = (f, w, h, (xpf, ypf), c0, c1, c2, o, cp)
+		self.disp.setinfo(inf0)

File Demo/sgi/video/VFile.py

 import gl
 import GL
 import colorsys
+import imageop
 
 
 # Exception raised for various occasions
 def conv_grey(l, x, y):
 	return colorsys.yiq_to_rgb(l, 0, 0)
 
+def conv_grey4(l, x, y):
+	return colorsys.yiq_to_rgb(l*17, 0, 0)
+
+def conv_mono(l, x, y):
+	return colorsys.yiq_to_rgb(l*255, 0, 0)
+
 def conv_yiq(y, i, q):
 	return colorsys.yiq_to_rgb(y, (i-0.5)*1.2, q-0.5)
 
 	raise Error, 'Attempt to make RGB colormap (jpeg)'
 
 conv_jpeggrey = conv_grey
-conv_mono = conv_grey
+conv_grey2 = conv_grey
 
 
 # Choose one of the above based upon a color system name
 	b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
 	return (r, g, b) == (3, 3, 2)
 
+#
+# Predicate function to see whether this machine supports pixmode(PM_SIZE)
+# with values 1 or 4.
+#
+# XXX Temporarily disabled, since it is unclear which machines support
+# XXX which pixelsizes.
+#
+def support_packed_pixels():
+	return 0   # To be architecture-dependent
 
 # Routines to grab data, per color system (only a few really supported).
 # (These functions are used via eval with a constructed argument!)
 		# Essential parameters
 		self.format = 'grey'	# color system used
 		# Choose from: grey, rgb, rgb8, hsv, yiq, hls, jpeg, jpeggrey,
-		#              mono
+		#              mono, grey2, grey4
 		self.width = 0		# width of frame
 		self.height = 0		# height of frame
 		self.packfactor = 1	# expansion using rectzoom
 		print 'Bits:    ', self.c0bits, self.c1bits, self.c2bits
 		print 'Offset:  ', self.offset
 
+	# Calculate data size, if possible
+	def calcframesize(self):
+		if self.format == 'rgb':
+			return self.width*self.height*4
+		if self.format in ('jpeg', 'jpeggrey'):
+			raise CallError
+		if type(self.packfactor) == type(()):
+			xpf, ypf = self.packfactor
+		else:
+			xpf = ypf = self.packfactor
+		if ypf < 0: ypf = -ypf
+		size = (self.width/xpf)*(self.height/ypf)
+		if self.format == 'grey4':
+			size = (size+1)/2
+		elif self.format == 'grey2':
+			size = (size+3)/4
+		elif self.format == 'mono':
+			size = (size+7)/8
+		return size
+
 
 # Class to display video frames in a window.
 # It is the caller's responsibility to ensure that the correct window
 		self.skipchrom = 0	# don't skip chrominance data
 		self.color0 = None	# magic, used by clearto()
 		self.fixcolor0 = 0	# don't need to fix color0
+		self.mustunpack = (not support_packed_pixels())
 		return self
 
 	# setinfo() must reset some internal flags
 
 	def showpartframe(self, data, chromdata, (x,y,w,h)):
 		pf = self.packfactor
+		pmsize = 8
+		if pf:
+			if type(pf) == type(()):
+				xpf, ypf = pf
+			else:
+				xpf = ypf = pf
+			if ypf < 0:
+				gl.pixmode(GL.PM_TTOB, 1)
+				ypf = -ypf
+			if xpf < 0:
+				gl.pixmode(GL.PM_RTOL, 1)
+				xpf = -xpf
+		else:
+			xpf = ypf = 1
 		if self.format in ('jpeg', 'jpeggrey'):
 			import jpeg
 			data, width, height, bytes = jpeg.decompress(data)
 			if self.format == 'jpeg':
 				b = 4
-				p = 1
+				xp = yp = 1
 			else:
 				b = 1
-				p = pf
-			if (width, height, bytes) <> (w/p, h/p, b):
+				xp = xpf
+				yp = ypf
+			if (width, height, bytes) <> (w/xp, h/yp, b):
 				raise Error, 'jpeg data has wrong size'
-		elif self.format == 'mono':
-			import imageop
-			data = imageop.mono2grey(data, w, h, 0x20, 0xdf)
+		elif self.format in ('mono', 'grey4'):
+			if self.mustunpack:
+				if self.format == 'mono':
+					data = imageop.mono2grey(data, \
+						  w/xpf, h/ypf, 0x20, 0xdf)
+				elif self.format == 'grey4':
+					data = imageop.grey42grey(data, \
+						  w/xpf, h/ypf)
+			else:
+				# We don't need to unpack, the hardware
+				# can do it.
+				if self.format == 'mono':
+					pmsize = 1
+				else:
+					pmsize = 4
+		elif self.format == 'grey2':
+			data = imageop.grey22grey(data, w/xpf, h/ypf)
 		if not self.colormapinited:
 			self.initcolormap()
 		if self.fixcolor0:
 			gl.mapcolor(self.color0)
 			self.fixcolor0 = 0
-		factor = self.magnify
-		if pf: factor = factor * pf
+		xfactor = yfactor = self.magnify
+		if pf:
+			xfactor = xfactor * xpf
+			yfactor = yfactor * ypf
 		if chromdata and not self.skipchrom:
 			cp = self.chrompack
-			cx = int(x*factor*cp) + self.xorigin
-			cy = int(y*factor*cp) + self.yorigin
+			cx = int(x*xfactor*cp) + self.xorigin
+			cy = int(y*yfactor*cp) + self.yorigin
 			cw = (w+cp-1)/cp
 			ch = (h+cp-1)/cp
-			gl.rectzoom(factor*cp, factor*cp)
+			gl.rectzoom(xfactor*cp, yfactor*cp)
 			gl.pixmode(GL.PM_SIZE, 16)
 			gl.writemask(self.mask - ((1 << self.c0bits) - 1))
 			gl.lrectwrite(cx, cy, cx + cw - 1, cy + ch - 1, \
 		#
 		if pf:
 			gl.writemask((1 << self.c0bits) - 1)
-			gl.pixmode(GL.PM_SIZE, 8)
-			w = w/pf
-			h = h/pf
-			x = x/pf
-			y = y/pf
-		gl.rectzoom(factor, factor)
-		x = int(x*factor)+self.xorigin
-		y = int(y*factor)+self.yorigin
+			gl.pixmode(GL.PM_SIZE, pmsize)
+			w = w/xpf
+			h = h/ypf
+			x = x/xpf
+			y = y/ypf
+		gl.rectzoom(xfactor, yfactor)
+		x = int(x*xfactor)+self.xorigin
+		y = int(y*yfactor)+self.yorigin
 		gl.lrectwrite(x, y, x + w - 1, y + h - 1, data)
 		gl.gflush()
 
 	# by clear() and clearto()
 
 	def _initcmap(self):
-		convcolor = choose_conversion(self.format)
+		if self.format in ('mono', 'grey4') and self.mustunpack:
+			convcolor = conv_grey
+		else:
+			convcolor = choose_conversion(self.format)
 		maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE)
 		if maxbits > 11:
 			maxbits = 11
 
 
 # Read a CMIF video file header.
-# Return (version, values) where version is 0.0, 1.0, 2.0 or 3.0,
+# Return (version, values) where version is 0.0, 1.0, 2.0 or 3.[01],
 # and values is ready for setinfo().
 # Raise Error if there is an error in the info
 
 		version = 2.0
 	elif line == 'CMIF video 3.0\n':
 		version = 3.0
+	elif line == 'CMIF video 3.1\n':
+		version = 3.1
 	else:
 		# XXX Could be version 0.0 without identifying header
 		raise Error, \
 		else:
 			format = 'grey'
 		offset = 0
-	elif version == 3.0:
+	elif version in (3.0, 3.1):
 		line = fp.readline()
 		try:
 			format, rest = eval(line[:-1])
 		except:
-			raise Error, filename + ': Bad 3.0 color info'
+			raise Error, filename + ': Bad 3.[01] color info'
 		if format == 'xrgb8':
 			format = 'rgb8' # rgb8 upside-down, for X
 		if format in ('rgb', 'jpeg'):
 			c0bits = c1bits = c2bits = 0
 			chrompack = 0
 			offset = 0
-		elif format in ('grey', 'jpeggrey', 'mono'):
+		elif format in ('grey', 'jpeggrey', 'mono', 'grey2', 'grey4'):
 			c0bits = rest
 			c1bits = c2bits = 0
 			chrompack = 0
 			try:
 			    c0bits, c1bits, c2bits, chrompack, offset = rest
 			except:
-			    raise Error, filename + ': Bad 3.0 color info'
+			    raise Error, filename + ': Bad 3.[01] color info'
 	#
 	# Get frame geometry info
 	#
 		packfactor = 2
 	else:
 		raise Error, filename + ': Bad (w,h,pf) info'
-	if packfactor > 1:
+	if type(packfactor) == type(()):
+		xpf, ypf = packfactor
+		xpf = abs(xpf)
+		ypf = abs(ypf)
+		width = (width/xpf) * xpf
+		height = (height/ypf) * ypf
+	elif packfactor > 1:
 		width = (width / packfactor) * packfactor
 		height = (height / packfactor) * packfactor
 	#
 	try:
 		t, datasize, chromdatasize = x = eval(line[:-1])
 	except:
-		raise Error, 'Bad 3.0 frame header'
+		raise Error, 'Bad 3.[01] frame header'
 	return x
 
 
-# Write a CMIF video file header (always version 3.0)
+# Write a CMIF video file header (always version 3.1)
 
 def writefileheader(fp, values):
 	(format, width, height, packfactor, \
 	#
 	# Write identifying header
 	#
-	fp.write('CMIF video 3.0\n')
+	fp.write('CMIF video 3.1\n')
 	#
 	# Write color encoding info
 	#
 	if format in ('rgb', 'jpeg'):
 		data = (format, 0)
-	elif format in ('grey', 'jpeggrey', 'mono'):
+	elif format in ('grey', 'jpeggrey', 'mono', 'grey2', 'grey4'):
 		data = (format, c0bits)
 	else:
 		data = (format, (c0bits, c1bits, c2bits, chrompack, offset))
 			self._readframeheader = readv1frameheader
 		elif self.version == 2.0:
 			self._readframeheader = readv2frameheader
-		elif self.version == 3.0:
+		elif self.version in (3.0, 3.1):
 			self._readframeheader = readv3frameheader
 		else:
 			raise Error, \
 		self.fp.close()
 		del self.fp
 
+	def prealloc(self, nframes):
+		if not self.headerwritten: raise CallError
+		data = '\xff' * self.calcframesize()
+		pos = self.fp.tell()
+		for i in range(nframes):
+			self.fp.write(data)
+		self.fp.seek(pos)
+
 	def setinfo(self, values):
 		if self.headerwritten: raise CallError
 		VideoParams.setinfo(self, values)

File Demo/sgi/video/Vrec.py

 # -w width      : initial window width (default interactive placement)
 # -n            : Don't write to file, only timing info
 # -d		: drop fields if needed
-# -g		: greyscale
+# -g bits	: greyscale (2, 4 or 8 bits)
+# -G            : 2-bit greyscale dithered
 # -m		: monochrome dithered
 # -M value	: monochrome tresholded with value
+# -f		: Capture fields (in stead of frames)
+# -P frames	: preallocate space for 'frames' frames
 # 
 # moviefile     : here goes the movie data (default film.video);
 #                 the format is documented in cmif-film.ms
 import getopt
 import string
 import imageop
+import sgi
 
 # Main program
 
 	drop = 0
 	mono = 0
 	grey = 0
+	greybits = 0
 	monotreshold = -1
+	fields = 0
+	preallocspace = 0
 
-	opts, args = getopt.getopt(sys.argv[1:], 'aq:r:w:ndgmM:')
+	opts, args = getopt.getopt(sys.argv[1:], 'aq:r:w:ndg:mM:GfP:')
 	for opt, arg in opts:
 		if opt == '-a':
 			audio = 1
 			drop = 1
 		elif opt == '-g':
 			grey = 1
+			greybits = string.atoi(arg)
+			if not greybits in (2,4,8):
+				print 'Only 2, 4 or 8 bit greyscale supported'
+		elif opt == '-G':
+			grey = 1
+			greybits = -2
 		elif opt == '-m':
 			mono = 1
 		elif opt == '-M':
 			mono = 1
 			monotreshold = string.atoi(arg)
+		elif opt == '-f':
+			fields = 1
+		elif opt == '-P':
+			preallocspace = string.atoi(arg)
 
 	if args[2:]:
 		sys.stderr.write('usage: Vrec [options] [file [audiofile]]\n')
 			if val == 1:
 				info = format, x, y, qsize, rate
 				record(v, info, filename, audiofilename,\
-					  mono, grey, monotreshold)
+					  mono, grey, greybits, monotreshold, \
+					  fields, preallocspace)
 		elif dev == DEVICE.REDRAW:
 			# Window resize (or move)
 			x, y = gl.getsize()
 # Record until the mouse is released (or any other GL event)
 # XXX audio not yet supported
 
-def record(v, info, filename, audiofilename, mono, grey, monotreshold):
+def record(v, info, filename, audiofilename, mono, grey, greybits, \
+	  monotreshold, fields, preallocspace):
 	import thread
 	format, x, y, qsize, rate = info
 	fps = 59.64 # Fields per second
 		vout = VFile.VoutFile().init(filename)
 		if mono:
 			vout.format = 'mono'
+		elif grey and greybits == 8:
+			vout.format = 'grey'
 		elif grey:
-			vout.format = 'grey'
+			vout.format = 'grey'+`abs(greybits)`
 		else:
 			vout.format = 'rgb8'
 		vout.width = x
 		vout.height = y
+		if fields:
+			vout.packfactor = (1,-2)
 		vout.writeheader()
+		if preallocspace:
+			print 'Preallocating space...'
+			vout.prealloc(preallocspace)
+			print 'done.'
 		MAXSIZE = 20 # XXX should be a user option
 		import Queue
 		queue = Queue.Queue().init(MAXSIZE)
 		done = thread.allocate_lock()
 		done.acquire_lock()
+		convertor = None
+		if grey:
+			if greybits == 2:
+				convertor = imageop.grey2grey2
+			elif greybits == 4:
+				convertor = imageop.grey2grey4
+			elif greybits == -2:
+				convertor = imageop.dither2grey2
 		thread.start_new_thread(saveframes, \
-			  (vout, queue, done, mono, monotreshold))
+			  (vout, queue, done, mono, monotreshold, convertor))
 		if audiofilename:
 			audiodone = thread.allocate_lock()
 			audiodone.acquire_lock()
 	lastid = 0
 	t0 = time.millitimer()
 	count = 0
-	timestamps = []
 	ids = []
 	v.InitContinuousCapture(info)
 	while not gl.qtest():
 		try:
 			cd, id = v.GetCaptureData()
 		except sv.error:
-			time.millisleep(10) # XXX is this necessary?
+			#time.millisleep(10) # XXX is this necessary?
+			sgi.nap(1)	# XXX Try by Jack
 			continue
-		timestamps.append(time.millitimer())
 		ids.append(id)
 		
 		id = id + 2*rate
 ##		if id <> lastid + 2*rate:
 ##			print lastid, id
 		lastid = id
-		data = cd.InterleaveFields(1)
-		cd.UnlockCaptureData()
 		count = count+1
-		if filename:
-			queue.put((data, int(id*tpf)))
+		if fields:
+			data1, data2 = cd.GetFields()
+			cd.UnlockCaptureData()
+			if filename:
+				queue.put((data1, int(id*tpf)))
+				queue.put((data2, int((id+1)*tpf)))
+		else:
+			data = cd.InterleaveFields(1)
+			cd.UnlockCaptureData()
+			if filename:
+				queue.put((data, int(id*tpf)))
 	t1 = time.millitimer()
 	gl.wintitle('(busy) ' + filename)
 	print lastid, 'fields in', t1-t0, 'msec',
 		print count*200.0/lastid, '%,',
 		print count*rate*200.0/lastid, '% of wanted rate',
 	print
-	t0 = timestamps[0]
-	del timestamps[0]
-	print 'Times:',
-	for t1 in timestamps:
-		print t1-t0,
-		t0 = t1
-	print
 	print 'Ids:',
 	t0 = ids[0]
 	del ids[0]
 
 # Thread to save the frames to the file
 
-def saveframes(vout, queue, done, mono, monotreshold):
+def saveframes(vout, queue, done, mono, monotreshold, convertor):
 	while 1:
 		x = queue.get()
 		if not x:
 			break
 		data, t = x
-		if mono and monotreshold >= 0:
+		if convertor:
+			data = convertor(data, len(data), 1)
+		elif mono and monotreshold >= 0:
 			data = imageop.grey2mono(data, len(data), 1,\
 				  monotreshold)
 		elif mono:
 			data = imageop.dither2mono(data, len(data), 1)
 		vout.writeframe(t, data, None)
-		del data
 	sys.stderr.write('Done writing video\n')
 	vout.close()
 	done.release_lock()

File Demo/sgi/video/Vrecc.py

+#! /ufs/guido/bin/sgi/python-405
+#! /ufs/guido/bin/sgi/python
+
+# Capture a continuous CMIF movie using the Indigo video library and board
+
+
+# Usage:
+#
+# makemovie [-r rate] [-w width] [moviefile]
+
+
+# Options:
+#
+# -r rate       : capture 1 out of every 'rate' frames (default 1)
+# -w width      : initial window width (default interactive placement)
+# -d		: drop fields if needed
+# -g bits	: greyscale (2, 4 or 8 bits)
+# -G            : 2-bit greyscale dithered
+# -m		: monochrome dithered
+# -M value	: monochrome tresholded with value
+# -f		: Capture fields (in stead of frames)
+# -n number     : Capture 'number' fields (default 60)
+# 
+# moviefile     : here goes the movie data (default film.video);
+#                 the format is documented in cmif-film.ms
+
+
+# User interface:
+#
+# Start the application.  Resize the window to the desired movie size.
+# Press the left mouse button to start recording, release it to end
+# recording.  You can record as many times as you wish, but each time
+# you overwrite the output file(s), so only the last recording is
+# kept.
+#
+# Press ESC or select the window manager Quit or Close window option
+# to quit.  If you quit before recording anything, the output file(s)
+# are not touched.
+
+
+import sys
+sys.path.append('/ufs/guido/src/video')
+import sv, SV
+import VFile
+import gl, GL, DEVICE
+import al, AL
+import time
+import posix
+import getopt
+import string
+import imageop
+import sgi
+
+# Main program
+
+def main():
+	format = SV.RGB8_FRAMES
+	rate = 1
+	width = 0
+	drop = 0
+	mono = 0
+	grey = 0
+	greybits = 0
+	monotreshold = -1
+	fields = 0
+	number = 60
+
+	opts, args = getopt.getopt(sys.argv[1:], 'r:w:dg:mM:Gfn:')
+	for opt, arg in opts:
+		if opt == '-r':
+			rate = string.atoi(arg)
+			if rate < 2:
+				sys.stderr.write('-r rate must be >= 2\n')
+				sys.exit(2)
+		elif opt == '-w':
+			width = string.atoi(arg)
+		elif opt == '-d':
+			drop = 1
+		elif opt == '-g':
+			grey = 1
+			greybits = string.atoi(arg)
+			if not greybits in (2,4,8):
+				print 'Only 2, 4 or 8 bit greyscale supported'
+		elif opt == '-G':
+			grey = 1
+			greybits = -2
+		elif opt == '-m':
+			mono = 1
+		elif opt == '-M':
+			mono = 1
+			monotreshold = string.atoi(arg)
+		elif opt == '-f':
+			fields = 1
+		elif opt == '-n':
+			number = string.atoi(arg)
+
+	if args[2:]:
+		sys.stderr.write('usage: Vrec [options] [file]\n')
+		sys.exit(2)
+
+	if args:
+		filename = args[0]
+	else:
+		filename = 'film.video'
+
+	v = sv.OpenVideo()
+	# Determine maximum window size based on signal standard
+	param = [SV.BROADCAST, 0]
+	v.GetParam(param)
+	if param[1] == SV.PAL:
+		x = SV.PAL_XMAX
+		y = SV.PAL_YMAX
+	elif param[1] == SV.NTSC:
+		x = SV.NTSC_XMAX
+		y = SV.NTSC_YMAX
+	else:
+		print 'Unknown video standard', param[1]
+		sys.exit(1)
+
+	gl.foreground()
+	gl.maxsize(x, y)
+	gl.keepaspect(x, y)
+	gl.stepunit(8, 6)
+	if width:
+		gl.prefsize(width, width*3/4)
+	win = gl.winopen(filename)
+	if width:
+		gl.maxsize(x, y)
+		gl.keepaspect(x, y)
+		gl.stepunit(8, 6)
+		gl.winconstraints()
+	x, y = gl.getsize()
+	print x, 'x', y
+
+	v.SetSize(x, y)
+
+	if drop:
+		param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
+	else:
+		param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
+	if mono or grey:
+		param = param+[SV.COLOR, SV.MONO, SV.INPUT_BYPASS, 1]
+	else:
+		param = param+[SV.COLOR, SV.DEFAULT_COLOR, SV.INPUT_BYPASS, 0]
+	v.SetParam(param)
+
+	v.BindGLWindow(win, SV.IN_REPLACE)
+
+	gl.qdevice(DEVICE.LEFTMOUSE)
+	gl.qdevice(DEVICE.WINQUIT)
+	gl.qdevice(DEVICE.WINSHUT)
+	gl.qdevice(DEVICE.ESCKEY)
+
+	print 'Press left mouse to start recording'
+
+	while 1:
+		dev, val = gl.qread()
+		if dev == DEVICE.LEFTMOUSE:
+			if val == 1:
+				info = format, x, y, number, rate
+				record(v, info, filename, mono, grey, \
+					  greybits, monotreshold, fields)
+		elif dev == DEVICE.REDRAW:
+			# Window resize (or move)
+			x, y = gl.getsize()
+			print x, 'x', y
+			v.SetSize(x, y)
+			v.BindGLWindow(win, SV.IN_REPLACE)
+		elif dev in (DEVICE.ESCKEY, DEVICE.WINQUIT, DEVICE.WINSHUT):
+			# Quit
+			v.CloseVideo()
+			gl.winclose(win)
+			break
+
+
+# Record until the mouse is released (or any other GL event)
+# XXX audio not yet supported
+
+def record(v, info, filename, mono, grey, greybits, monotreshold, fields):
+	import thread
+	format, x, y, number, rate = info
+	fps = 59.64 # Fields per second
+	# XXX (Strange: need fps of Indigo monitor, not of PAL or NTSC!)
+	tpf = 1000.0 / fps # Time per field in msec
+	#
+	# Go grab
+	#
+	gl.wintitle('(rec) ' + filename)
+	try:
+		ninfo, data, bitvec = v.CaptureBurst(info)
+	except sv.error, arg:
+		print 'CaptureBurst failed:', arg
+		print 'info:', info
+		gl.wintitle(filename)
+		return
+	gl.wintitle('(save) '+ filename)
+	#
+	# Check results
+	#
+	if info <> ninfo:
+		print 'Sorry, format changed.'
+		print 'Wanted:',info
+		print 'Got   :',ninfo
+		gl.wintitle(filename)
+		return
+	# print bitvec
+	if x*y*number <> len(data):
+		print 'Funny data length: wanted',x,'*',y,'*', number,'=',\
+			  x*y*number,'got',len(data)
+		gl.wintitle(filename)
+		return
+	#
+	# Save
+	#
+	if filename:
+		#
+		# Construct header and write it
+		#
+		vout = VFile.VoutFile().init(filename)
+		if mono:
+			vout.format = 'mono'
+		elif grey and greybits == 8:
+			vout.format = 'grey'
+		elif grey:
+			vout.format = 'grey'+`abs(greybits)`
+		else:
+			vout.format = 'rgb8'
+		vout.width = x
+		vout.height = y
+		if fields:
+			vout.packfactor = (1,-2)
+		else:
+			print 'Sorry, can only save fields at the moment'
+			gl.wintitle(filename)
+			return
+		vout.writeheader()
+		#
+		# Compute convertor, if needed
+		#
+		convertor = None
+		if grey:
+			if greybits == 2:
+				convertor = imageop.grey2grey2
+			elif greybits == 4:
+				convertor = imageop.grey2grey4
+			elif greybits == -2:
+				convertor = imageop.dither2grey2
+		fieldsize = x*y/2
+		nskipped = 0
+		realframeno = 0
+		tpf = 1000 / 50.0     #XXXX
+		for frameno in range(0, number*2):
+			if frameno <> 0 and \
+				  bitvec[frameno] == bitvec[frameno-1]:
+				nskipped = nskipped + 1
+				continue
+			#
+			# Save field.
+			# XXXX Works only for fields and top-to-bottom
+			#
+			start = frameno*fieldsize
+			field = data[start:start+fieldsize]
+			if convertor:
+				field = convertor(field, x, y)
+			elif mono and monotreshold >= 0:
+				field = imageop.grey2mono(field, x, y, \
+					  1, monotreshold)
+			elif mono:
+				field = imageop.dither2mono(field, x, y)
+			vout.writeframe(int(realframeno*tpf), field, None)
+		print 'Skipped',nskipped,'duplicate frames'
+		vout.close()
+			
+	gl.wintitle('(done) ' + filename)
+
+# Don't forget to call the main program
+
+try:
+	main()
+except KeyboardInterrupt:
+	print '[Interrupt]'