| #! /usr/bin/env 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 |
| 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) |