#! /ufs/guido/bin/sgi/python

# Video bag of tricks: record video(+audio) in various formats and modes

# XXX To do:
# - audio
# - improve user interface
# - help button?
# - command line options to set initial settings
# - save settings in a file
# - ...?

import sys
import time
import getopt
import string
import os
sts = os.system('makemap')		# Must be before "import fl" to work
import sgi
import gl
import GL
import DEVICE
import fl
import FL
import flp
import watchcursor
import sv
import SV
import VFile
import VGrabber
import imageop
sys.path.append('/ufs/jack/src/av/vcr')
import VCR
try:
	import cl
	import CL
except ImportError:
	cl = None

ARROW = 0
WATCH = 1
watchcursor.defwatch(WATCH)

def main():
##	fl.set_graphics_mode(0, 1)
	vb = VideoBagOfTricks()
	while 1:
		dummy = fl.do_forms()
		[dummy]

StopCapture = 'StopCapture'

VideoFormatLabels = ['Video off', 'rgb8', 'grey8', 'grey4', 'grey2', \
	  'grey2 dith', 'mono dith', 'mono thresh', 'rgb24', 'rgb24-jpeg', \
	  'compress']
VideoFormats = ['', 'rgb8', 'grey', 'grey4', 'grey2', \
	  'grey2', 'mono', 'mono', 'rgb', 'jpeg', 'compress']

VideoModeLabels = ['Continuous', 'Burst', 'Single frame', 'VCR sync']
[VM_CONT, VM_BURST, VM_SINGLE, VM_VCR] = range(1, 5)

AudioFormatLabels = ['Audio off', \
	  '16 bit mono', '16 bit stereo', '8 bit mono', '8 bit stereo']
[A_OFF, A_16_MONO, A_16_STEREO, A_8_MONO, A_8_STEREO] = range(1, 6)

VcrSpeedLabels = ['normal', '1/3', '1/5', '1/10', '1/30', 'single-step']
VcrSpeeds = [None, 5, 4, 3, 2, 1, 0]

RgbSizeLabels = ['full', 'quarter', 'sixteenth']

# init file stuff:
if os.environ.has_key('HOME'):
	HOME=os.environ['HOME']
else:
	HOME='.'
VB_INIT_FILE=HOME + '/.Vb_init'

VB_INIT_KEYS=['vfile', 'vmode', 'mono_thresh', 'vformat', 'comp_scheme', \
	  'rgb24_size', 'afile', 'aformat']

class VideoBagOfTricks:

	# Init/close stuff

	def __init__(self):
		self.window = None
		formdef = flp.parse_form('VbForm', 'form')
		flp.create_full_form(self, formdef)
		self.setdefaults()
		if self.vmode <> VM_CONT:
			self.g_cont.hide_object()
		if self.vmode <> VM_BURST:
			self.g_burst.hide_object()
		if self.vmode <> VM_SINGLE:
			self.g_single.hide_object()
		if self.vmode <> VM_VCR:
			self.g_vcr.hide_object()
		if self.vformat <> 'compress':
			self.g_compress.hide_object()

		self.openvideo()
		self.makewindow()
		self.bindvideo()
		if self.use_24:
			self.optfullsizewindow()
		self.showform()
		fl.set_event_call_back(self.do_event)

	def close(self):
		self.close_video()
		self.close_audio()
		self.savedefaults()
		raise SystemExit, 0

	def showform(self):
		# Get position of video window
		gl.winset(self.window)
		x, y = gl.getorigin()
		width, height = gl.getsize()
		# Calculate position of form window
		x1 = x + width + 10
		x2 = x1 + int(self.form.w) - 1
		y2 = y + height - 1
		y1 = y2 - int(self.form.h) + 1
		# Position and show form window
		gl.prefposition(x1, x2, y1, y2)
		self.form.show_form(FL.PLACE_FREE, FL.TRUE, 'Vb Control')

	def getdefaultdefaults(self):
		# Video defaults
		self.vfile = 'film.video'
		self.vmode = VM_CONT
		self.mono_thresh = 128
		self.vformat = 'rgb8'
		self.comp_scheme = 'Uncompressed'
		self.rgb24_size = 1
		# Missing: drop, rate, maxmem, nframes, rate, vcrspeed
		# Audio defaults:
		self.afile = 'film.aiff'
		self.aformat = A_OFF

	def getdefaults(self):
		self.getdefaultdefaults()
		# XXXX Read defaults file and override.
		try:
			fp = open(VB_INIT_FILE, 'r')
		except IOError:
			print 'Vb: no init file'
			self.initcont = {}
			return
		data = fp.read(1000000)
		try:
			self.initcont = eval(data)
		except:
			print 'Vb: Ill-formatted init file'
			self.initcont = {}
		for k in self.initcont.keys():
			if hasattr(self, k):
				setattr(self, k, self.initcont[k])

	def savedefaults(self):
		newdb = {}
		for k in VB_INIT_KEYS:
			newdb[k] = getattr(self, k)
		if newdb <> self.initcont:
			try:
				fp = open(VB_INIT_FILE, 'w')
			except IOError:
				print 'Vb: Cannot create', VB_INIT_FILE
				return
			fp.write(`newdb`)
			fp.close()
			
	def setdefaults(self):
		self.getdefaults()
		self.vcr = None
		self.vout = None
		self.capturing = 0
		self.c_vformat.clear_choice()
		for label in VideoFormatLabels:
			self.c_vformat.addto_choice(label)
		self.c_vformat.set_choice(1 + VideoFormats.index(self.vformat))
		self.c_vmode.clear_choice()
		for label in VideoModeLabels:
			self.c_vmode.addto_choice(label)
		self.c_vmode.set_choice(self.vmode)
		self.get_vformat()
		self.b_drop.set_button(1)
		self.in_rate.set_input('2')
		self.in_maxmem.set_input('1.0')
		self.in_nframes.set_input('0')
		self.in_nframes_vcr.set_input('1')
		self.in_rate_vcr.set_input('1')
		self.c_vcrspeed.clear_choice()
		for label in VcrSpeedLabels:
			self.c_vcrspeed.addto_choice(label)
		self.c_vcrspeed.set_choice(4)
		self.c_rgb24_size.clear_choice()
		for label in RgbSizeLabels:
			self.c_rgb24_size.addto_choice(label)
		self.c_rgb24_size.set_choice(self.rgb24_size)
		if cl:
			algs = cl.QueryAlgorithms(CL.VIDEO)
			self.all_comp_schemes = []
			for i in range(0, len(algs), 2):
				if algs[i+1] in (CL.COMPRESSOR, CL.CODEC):
					self.all_comp_schemes.append(algs[i])
			self.c_cformat.clear_choice()
			for label in self.all_comp_schemes:
				self.c_cformat.addto_choice(label)
			i = self.all_comp_schemes.index(self.comp_scheme)
			self.c_cformat.set_choice(i+1)
		# Audio defaults
		self.aout = None
		self.aport = None
		self.c_aformat.clear_choice()
		for label in AudioFormatLabels:
			self.c_aformat.addto_choice(label)
		self.c_aformat.set_choice(self.aformat)
		self.get_aformat()

	def openvideo(self):
		try:
			self.video = sv.OpenVideo()
		except sv.error, msg:
			print 'Error opening video:', msg
			self.video = None
		param = [SV.BROADCAST, SV.PAL]
		if self.video: self.video.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)
		self.maxx, self.maxy = x, y
		self.curx = 256
		self.cury = 256*3/4

	def makewindow(self):
		x, y = self.maxx, self.maxy
		gl.foreground()
		gl.maxsize(x, y)
		gl.keepaspect(x, y)
		gl.stepunit(8, 6)
		width = self.curx
		height = self.cury
		if width and height:
			# Place the window at (150, 150) from top left
			# (the video board likes this location...)
			x1 = 150
			x2 = x1+width-1
			SCRHEIGHT = 768
			y2 = SCRHEIGHT-1-150
			y1 = y2-height+1
			gl.prefposition(x1, x2, y1, y2)
		self.window = gl.winopen('Vb video')
		self.settitle()
		if width:
			gl.maxsize(x, y)
			gl.keepaspect(x, y)
			gl.stepunit(8, 6)
			gl.winconstraints()
		gl.qdevice(DEVICE.LEFTMOUSE)
		gl.qdevice(DEVICE.WINQUIT)
		gl.qdevice(DEVICE.WINSHUT)

	def optfullsizewindow(self):
		if not self.window:
			return
		gl.winset(self.window)
		if self.use_24:
			x, y = self.maxx, self.maxy
		else:
			x, y = self.curx, self.cury
		left, bottom = gl.getorigin()
		width, height = gl.getsize()
		bottom = bottom+height-y
		gl.prefposition(left, left+x-1, bottom, bottom+y-1)
		gl.winconstraints()
		if not self.use_24:
			gl.keepaspect(x, y)
			gl.stepunit(8, 6)
			gl.maxsize(self.maxx, self.maxy)
			gl.winconstraints()
		self.bindvideo()

	def bindvideo(self):
		if not self.video: return
		x, y = gl.getsize()
		if not self.use_24:
			self.curx, self.cury = x, y
		self.video.SetSize(x, y)
		drop = self.b_drop.get_button()
		if drop:
			param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
		else:
			param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
		if self.rgb:
			param = param+[SV.COLOR, SV.DEFAULT_COLOR, \
				       SV.DITHER, 1, \
				       SV.INPUT_BYPASS, 0]
		else:
			param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
				       SV.INPUT_BYPASS, 1]
		self.video.BindGLWindow(self.window, SV.IN_REPLACE)
		self.video.SetParam(param)

	def rebindvideo(self):
		gl.winset(self.window)
		self.bindvideo()

	def reset(self):
		self.close_video()
		self.close_audio()
		if self.vcr:
			try:
				ok = self.vcr.still()
			except VCR.error:
				pass
			self.vcr = None
		self.b_capture.set_button(0)

	# Event handler (catches resize of video window)

	def do_event(self, dev, val):
		#print 'Event:', dev, val
		if dev in (DEVICE.WINSHUT, DEVICE.WINQUIT):
			self.close()
		if dev == DEVICE.REDRAW and val == self.window:
			self.rebindvideo()
			self.settitle()

	# Video controls: format, mode, file

	def cb_vformat(self, *args):
		self.reset()
		self.get_vformat()
		if self.mono_use_thresh:
			s = `self.mono_thresh`
			s = fl.show_input('Please enter mono threshold', s)
			if s:
				try:
					self.mono_thresh = string.atoi(s)
				except string.atoi_error:
					fl.show_message('Bad input, using', \
						  `self.mono_thresh`, '')
		self.rebindvideo()

	def cb_cformat(self, *args):
		i = self.c_cformat.get_choice()
		self.comp_scheme = self.all_comp_schemes[i-1]

		
	def cb_vmode(self, *args):
		if self.vcr:
			self.vcr = None
		self.vmode = self.c_vmode.get_choice()
		self.form.freeze_form()
		self.g_cont.hide_object()
		self.g_burst.hide_object()
		self.g_single.hide_object()
		self.g_vcr.hide_object()
		if self.vmode == VM_CONT:
			self.g_cont.show_object()
		elif self.vmode == VM_BURST:
			self.g_burst.show_object()
		elif self.vmode == VM_SINGLE:
			self.g_single.show_object()
		elif self.vmode == VM_VCR:
			self.g_vcr.show_object()
		self.form.unfreeze_form()

	def cb_vfile(self, *args):
		filename = self.vfile
		hd, tl = os.path.split(filename)
		filename = fl.file_selector('Video save file:', hd, '', tl)
		if filename:
			self.reset()
			hd, tl = os.path.split(filename)
			if hd == os.getcwd():
				filename = tl
			self.vfile = filename

	# Video mode specific video controls

	def cb_rate(self, *args):
		pass

	def cb_drop(self, *args):
		self.rebindvideo()

	def cb_maxmem(self, *args):
		pass

	def cb_nframes(self, *args):
		pass

	def cb_fps(self, *args):
		pass

	def cb_nframes_vcr(self, *args):
		pass

	def cb_rate_vcr(self, *args):
		pass

	def cb_vcrspeed(self, *args):
		pass

	def cb_rgb24_size(self, *args):
		i = self.c_rgb24_size.get_choice()
		if i:
			self.rgb24_size = i

	# Audio controls: format, file

	def cb_aformat(self, *args):
		self.get_aformat()

	def cb_afile(self, *args):
		filename = self.afile
		hd, tl = os.path.split(filename)
		filename = fl.file_selector('Audio save file:', hd, '', tl)
		if filename:
			self.reset()
			hd, tl = os.path.split(filename)
			if hd == os.getcwd():
				filename = tl
			self.afile = filename

	# General controls: capture, reset, play, quit

	def cb_capture(self, *args):
		if self.capturing:
			raise StopCapture
		if not self.b_capture.get_button():
			return
		if not self.video or not self.vformat:
			gl.ringbell()
			return
		if self.vmode == VM_CONT:
			self.cont_capture()
		elif self.vmode == VM_BURST:
			self.burst_capture()
		elif self.vmode == VM_SINGLE:
			self.single_capture(None, None)
		elif self.vmode == VM_VCR:
			self.vcr_capture()

	def cb_reset(self, *args):
		self.reset()

	def cb_play(self, *args):
		self.reset()
		sts = os.system('Vplay -q ' + self.vfile + ' &')

	def cb_quit(self, *args):
		self.close()

	# Capture routines

	def burst_capture(self):
		self.setwatch()
		gl.winset(self.window)
		x, y = gl.getsize()
		if self.use_24:
			fl.show_message('Sorry, no 24 bit continuous capture yet', '', '')
			return
		vformat = SV.RGB8_FRAMES
		nframes = self.getint(self.in_nframes, 0)
		if nframes == 0:
			maxmem = self.getint(self.in_maxmem, 1.0)
			memsize = int(maxmem * 1024 * 1024)
			nframes = self.calcnframes(memsize)
		info = (vformat, x, y, nframes, 1)
		try:
			info2, data, bitvec = self.video.CaptureBurst(info)
		except sv.error, msg:
			self.b_capture.set_button(0)
			self.setarrow()
			fl.show_message('Capture error:', str(msg), '')
			return
		if info <> info2: print info, '<>', info2
		self.save_burst(info2, data, bitvec)
		self.setarrow()

	def calcnframes(self, memsize):
		gl.winset(self.window)
		x, y = gl.getsize()
		pixels = x*y
		pixels = pixels/2	# XXX always assume fields
		if self.mono or self.grey:
			n = memsize/pixels
		else:
			n = memsize/(4*pixels)
		return max(1, n)

	def save_burst(self, info, data, bitvec):
		(vformat, x, y, nframes, rate) = info
		self.open_if_closed()
		fieldsize = x*y/2
		nskipped = 0
		realframeno = 0
		tpf = 1000 / 50.0     # XXX
		for frameno in range(0, nframes*2):
			if frameno <> 0 and \
				  bitvec[frameno] == bitvec[frameno-1]:
				nskipped = nskipped + 1
				continue
			#
			# Save field.
			# XXX Works only for fields and top-to-bottom
			#
			start = frameno*fieldsize
			field = data[start:start+fieldsize]
			realframeno = realframeno + 1
			fn = int(realframeno*tpf)
			if not self.write_frame(fn, field):
				break

	def cont_capture(self):
		saved_label = self.b_capture.label
		self.b_capture.label = 'Stop\n' + saved_label
		self.open_if_closed()
		self.init_cont()
		fps = 59.64		# Fields per second
		# XXX (fps of Indigo monitor, not of PAL or NTSC!)
		tpf = 1000.0 / fps	# Time per field in msec
		self.capturing = 1
		self.start_audio()
		while 1:
			try:
				void = fl.check_forms()
			except StopCapture:
				break
			try:
				cd, id = self.video.GetCaptureData()
			except sv.error:
				sgi.nap(1)
				continue
			id = id + 2*self.rate
			data = cd.InterleaveFields(1)
			cd.UnlockCaptureData()
			t = id*tpf
			if not self.write_frame(t, data):
				break
		self.stop_audio()
		self.capturing = 0
		self.end_cont()
		if self.aout:
			# If recording audio, can't capture multiple sequences
			self.reset()
		self.b_capture.label = saved_label

	def single_capture(self, stepfunc, timecode):
		self.open_if_closed()
		self.init_cont()
		while 1:
			try:
				cd, id = self.video.GetCaptureData()
				break
			except sv.error:
				pass
			sgi.nap(1)
			if stepfunc:		# This might step the video
				d=stepfunc()	# to the next frame
		if not self.use_24:
			data = cd.InterleaveFields(1)
		else:
			x, y = self.vout.getsize()
			if self.use_compress:
				if self.rgb24_size == 1:
					data = cd.YUVtoYUV422DC(0)
				elif self.rgb24_size == 2:
					data = cd.YUVtoYUV422DC_quarter(1)
					x = x/2
					y = y/2
				elif self.rgb24_size == 3:
					data = cd.YUVtoYUV422DC_sixteenth(1)
					x = x/4
					y = y/4
			else:
				data = cd.YUVtoRGB(1)
				if self.maxx*self.maxy*4 <> len(data):
					print 'maxx,maxy,exp,got=', self.maxx,
					print self.maxy,self.maxx*self.maxy*4,
					print len(data)
					fl.showmessage('Wrong sized data')
					return 0
				if self.rgb24_size <> 1:
					data = imageop.scale(data, 4, \
						  self.maxx, self.maxy, x, y)
			if self.use_jpeg:
				import jpeg
				data = jpeg.compress(data, x, y, 4)
			if self.use_compress:
				data = self.compressor.Compress(1, data)
		cd.UnlockCaptureData()
		self.end_cont()
		if timecode == None:
			timecode = (self.nframes+1) * (1000/25)
		return self.write_frame(timecode, data)

	def vcr_capture(self):
		if not self.vcr:
			try:
				print 'Connecting to VCR ...'
				self.vcr = VCR.VCR()
				print 'Waiting for VCR to come online ...'
				self.vcr.initvcr()
				print 'Preparing VCR ...'
				if not (self.vcr.fmmode('dnr') and \
					  self.vcr.dmcontrol('digital slow')):
					self.vcr_error('digital slow failed')
					return
				print 'VCR OK.'
			except VCR.error, msg:
				self.vcr = None
				self.vcr_error(msg)
				return
		if not self.vcr.still():
			self.vcr_error('still failed')
			return
		self.open_if_closed()
		rate = self.getint(self.in_rate_vcr, 1)
		rate = max(rate, 1)
		vcrspeed = self.c_vcrspeed.get_choice()
		vcrspeed = VcrSpeeds[vcrspeed]
		if vcrspeed == 0:
			stepfunc = self.vcr.step
		else:
			stepfunc = None
		self.speed_factor = rate
		addr = start_addr = self.vcr.sense()
		if not self.single_capture(None, 0):
			return
		print 'captured %02d:%02d:%02d:%02d' % self.vcr.addr2tc(addr)
		count = self.getint(self.in_nframes_vcr, 1) - 1
		if count <= 0:
			while rate > 0:
				if not self.vcr.step():
					self.vcr_error('step failed')
				here = self.vcr.sense()
				if here > addr:
					rate = rate - (here - addr)
				addr = here
			return
		if not self.vcr.fwdshuttle(vcrspeed):
			self.vcr_error('fwd shuttle failed')
			return
		cycle = 0
		while count > 0:
			try:
				here = self.vcr.sense()
			except VCR.error, msg:
				self.vcr_error(msg)
				break
			if here <> addr:
				if here <> addr+1:
					print 'Missed', here-addr-1,
					print 'frame' + 's'*(here-addr-1 <> 1)
				cycle = (cycle+1) % rate
				if cycle == 0:
					tc = (here-start_addr)*40
					if not self.single_capture(stepfunc, \
						  tc):
						break
					print 'captured %02d:%02d:%02d:%02d' \
						  % self.vcr.addr2tc(here)
					count = count -1
				addr = here
		if self.vcr and not self.vcr.still():
			self.vcr_error('still failed')

	def vcr_error(self, msg):
		self.reset()
		fl.show_message('VCR error:', str(msg), '')

	# Init/end continuous capture mode

	def init_cont(self):
		qsize = 1
		if self.vmode == VM_CONT:
			self.rate = self.getint(self.in_rate, 2)
		else:
			self.rate = 2
		x, y = self.vout.getsize()
		if self.use_24:
			info = (SV.YUV411_FRAMES, x, y, qsize, self.rate)
		else:
			info = (SV.RGB8_FRAMES, x, y, qsize, self.rate)
		info2 = self.video.InitContinuousCapture(info)
		if info2 <> info:
			# XXX This is really only debug info
			print 'Info mismatch: requested', info, 'got', info2

	def end_cont(self):
		self.video.EndContinuousCapture()

	# Misc stuff

	def settitle(self):
		gl.winset(self.window)
		x, y = gl.getsize()
		title = 'Vb ' + self.vfile + ' (%dx%d)' % (x, y)
		gl.wintitle(title)

	def get_vformat(self):
		i = self.c_vformat.get_choice()
		label = VideoFormatLabels[i-1]
		format = VideoFormats[i-1]
		if format == 'compress' and cl == None:
			fl.show_message('Sorry, no compression library support')
			format = ''
			label = 'Video off'
		self.vformat = format
		if self.vformat == '':
			self.form.freeze_form()
			self.g_video.hide_object()
			self.g_cont.hide_object()
			self.g_burst.hide_object()
			self.g_single.hide_object()
			self.form.unfreeze_form()
		else:
			self.g_video.show_object()
			if self.vmode == VM_CONT:
				self.g_cont.show_object()
			elif self.vmode == VM_BURST:
				self.g_burst.show_object()
			elif self.vmode == VM_SINGLE:
				self.g_single.show_object()
		#
		self.rgb = (format[:3] == 'rgb' or format == 'compress')
		self.mono = (format == 'mono')
		self.grey = (format[:4] == 'grey')
		self.use_24 = (format in ('rgb', 'jpeg', 'compress'))
		if self.use_24:
			self.g_rgb24.show_object()
		else:
			self.g_rgb24.hide_object()
		self.use_jpeg = (format == 'jpeg')
		self.mono_use_thresh = (label == 'mono thresh')
		self.use_compress = (format == 'compress')
		if self.use_compress:
			self.g_compress.show_object()
		else:
			self.g_compress.hide_object()
		s = format[4:]
		if self.grey and s:
			self.greybits = string.atoi(s)
		else:
			self.greybits = 8
		if label == 'grey2 dith':
			self.greybits = -2
		#
		convertor = None
		if self.grey:
			if self.greybits == 2:
				convertor = imageop.grey2grey2
			elif self.greybits == 4:
				convertor = imageop.grey2grey4
			elif self.greybits == -2:
				convertor = imageop.dither2grey2
		self.convertor = convertor
		self.optfullsizewindow()

	def get_aformat(self):
		self.reset()
		self.aformat = self.c_aformat.get_choice()
		if self.aformat == A_OFF:
			self.g_audio.hide_object()
		else:
			self.g_audio.show_object()

	def init_compressor(self, w, h):
		self.compressor = None
		scheme = cl.QuerySchemeFromName(CL.VIDEO, self.comp_scheme)
		self.compressor = cl.OpenCompressor(scheme)
		parambuf = [CL.IMAGE_WIDTH, w, \
			  CL.IMAGE_HEIGHT, h, \
			  CL.ORIGINAL_FORMAT, CL.YUV422DC]
		self.compressor.SetParams(parambuf)
		return self.compressor.Compress(0, '')

	def open_if_closed(self):
		if not self.vout:
			self.open_video()
		if not self.aout:
			self.open_audio()

	# File I/O handling

	def open_video(self):
		self.close_video()
		gl.winset(self.window)
		x, y = gl.getsize()
		if self.use_24:
			if self.rgb24_size == 2:
				x, y = x/2, y/2
			elif self.rgb24_size == 3:
				x, y = x/4, y/4
		vout = VFile.VoutFile(self.vfile)
		vout.setformat(self.vformat)
		if self.vformat == 'compress':
			cheader = self.init_compressor(x, y)
			vout.setcompressheader(cheader)
		vout.setsize(x, y)
		if self.vmode == VM_BURST:
			vout.setpf((1, -2))
		vout.writeheader()
		self.vout = vout
		self.nframes = 0
		self.speed_factor = 1
		self.t_nframes.label = `self.nframes`

	def write_frame(self, t, data):
		t = t * self.speed_factor
		if not self.vout:
			gl.ringbell()
			return 0
		if self.convertor:
			data = self.convertor(data, len(data), 1)
		elif self.mono:
			if self.mono_use_thresh:
				data = imageop.grey2mono(data, \
					  len(data), 1,\
					  self.mono_thresh)
			else:
				data = imageop.dither2mono(data, \
					  len(data), 1)
		try:
			self.vout.writeframe(int(t), data, None)
		except IOError, msg:
			self.reset()
			if msg == (0, 'Error 0'):
				msg = 'disk full??'
			fl.show_message('IOError', str(msg), '')
			return 0
		self.nframes = self.nframes + 1
		self.t_nframes.label = `self.nframes`
		return 1

	def close_video(self):
		if not self.vout:
			return
		self.nframes = 0
		self.t_nframes.label = ''
		try:
			self.vout.close()
		except IOError, msg:
			if msg == (0, 'Error 0'):
				msg = 'disk full??'
			fl.show_message('IOError', str(msg), '')
		self.vout = None
		self.compressor = None

	# Watch cursor handling

	def setwatch(self):
		gl.winset(self.form.window)
		gl.setcursor(WATCH, 0, 0)
		gl.winset(self.window)
		gl.setcursor(WATCH, 0, 0)

	def setarrow(self):
		gl.winset(self.form.window)
		gl.setcursor(ARROW, 0, 0)
		gl.winset(self.window)
		gl.setcursor(ARROW, 0, 0)

	# Numeric field handling

	def getint(self, field, default):
		try:
			value = string.atoi(field.get_input())
		except string.atoi_error:
			value = default
		field.set_input(`value`)
		return value

	def getfloat(self, field, default):
		try:
			value = float(eval(field.get_input()))
		except:
			value = float(default)
		field.set_input(`value`)
		return value

	# Audio stuff

	def open_audio(self):
		if self.aformat == A_OFF:
			return
		import aifc
		import al
		import AL
		import thread
		self.close_audio()
		params = [AL.INPUT_RATE, 0]
		al.getparams(AL.DEFAULT_DEVICE, params)
		rate = params[1]
		self.aout = aifc.open(self.afile, 'w')
		if self.aformat in (A_16_STEREO, A_8_STEREO):
			nch = AL.STEREO
		else:
			nch = AL.MONO
		if self.aformat in (A_16_STEREO, A_16_MONO):
			width = AL.SAMPLE_16
		else:
			width = AL.SAMPLE_8
		self.aout.setnchannels(nch)
		self.aout.setsampwidth(width)
		self.aout.setframerate(rate)
		c = al.newconfig()
		c.setqueuesize(8000)
		c.setchannels(nch)
		c.setwidth(width)
		self.aport = al.openport('Vb audio record', 'r', c)
		self.audio_stop = 0
		self.audio_ok = 0
		self.audio_busy = 1
		thread.start_new_thread(self.record_audio, ())

	def start_audio(self):
		if self.aformat == A_OFF:
			return
		self.audio_ok = 1

	def record_audio(self, *args):
		# This function runs in a separate thread
		# Currently no semaphores are used
		while not self.audio_stop:
			data = self.aport.readsamps(4000)
			if self.audio_ok:
				self.aout.writeframes(data)
			data = None
		self.audio_busy = 0

	def stop_audio(self):
		self.audio_ok = 0

	def close_audio(self):
		if self.aout:
			self.audio_ok = 0
			self.audio_stop = 1
			while self.audio_busy:
				time.sleep(0.1)
			self.aout.close()
			self.aout = None
		if self.aport:
			self.aport.closeport()
			self.aport = None


try:
	main()
except KeyboardInterrupt:
	print '[Interrupt]'
	sys.exit(1)
