home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Demo / sgi / video / Vb.py < prev    next >
Text File  |  1996-11-27  |  24KB  |  964 lines

  1. #! /usr/bin/env python
  2.  
  3. # Video bag of tricks: record video(+audio) in various formats and modes
  4.  
  5. # XXX To do:
  6. # - audio
  7. # - improve user interface
  8. # - help button?
  9. # - command line options to set initial settings
  10. # - save settings in a file
  11. # - ...?
  12.  
  13. import sys
  14. import time
  15. import getopt
  16. import string
  17. import os
  18. sts = os.system('makemap')        # Must be before "import fl" to work
  19. import sgi
  20. import gl
  21. import GL
  22. import DEVICE
  23. import fl
  24. import FL
  25. import flp
  26. import watchcursor
  27. import sv
  28. import SV
  29. import VFile
  30. import VGrabber
  31. import imageop
  32. sys.path.append('/ufs/jack/src/av/vcr')
  33. import VCR
  34. try:
  35.     import cl
  36.     import CL
  37. except ImportError:
  38.     cl = None
  39.  
  40. ARROW = 0
  41. WATCH = 1
  42. watchcursor.defwatch(WATCH)
  43.  
  44. def main():
  45. ##    fl.set_graphics_mode(0, 1)
  46.     vb = VideoBagOfTricks()
  47.     while 1:
  48.         dummy = fl.do_forms()
  49.         [dummy]
  50.  
  51. StopCapture = 'StopCapture'
  52.  
  53. VideoFormatLabels = ['Video off', 'rgb8', 'grey8', 'grey4', 'grey2', \
  54.       'grey2 dith', 'mono dith', 'mono thresh', 'rgb24', 'rgb24-jpeg', \
  55.       'compress']
  56. VideoFormats = ['', 'rgb8', 'grey', 'grey4', 'grey2', \
  57.       'grey2', 'mono', 'mono', 'rgb', 'jpeg', 'compress']
  58.  
  59. VideoModeLabels = ['Continuous', 'Burst', 'Single frame', 'VCR sync']
  60. [VM_CONT, VM_BURST, VM_SINGLE, VM_VCR] = range(1, 5)
  61.  
  62. AudioFormatLabels = ['Audio off', \
  63.       '16 bit mono', '16 bit stereo', '8 bit mono', '8 bit stereo']
  64. [A_OFF, A_16_MONO, A_16_STEREO, A_8_MONO, A_8_STEREO] = range(1, 6)
  65.  
  66. VcrSpeedLabels = ['normal', '1/3', '1/5', '1/10', '1/30', 'single-step']
  67. VcrSpeeds = [None, 5, 4, 3, 2, 1, 0]
  68.  
  69. RgbSizeLabels = ['full', 'quarter', 'sixteenth']
  70.  
  71. # init file stuff:
  72. if os.environ.has_key('HOME'):
  73.     HOME=os.environ['HOME']
  74. else:
  75.     HOME='.'
  76. VB_INIT_FILE=HOME + '/.Vb_init'
  77.  
  78. VB_INIT_KEYS=['vfile', 'vmode', 'mono_thresh', 'vformat', 'comp_scheme', \
  79.       'rgb24_size', 'afile', 'aformat']
  80.  
  81. class VideoBagOfTricks:
  82.  
  83.     # Init/close stuff
  84.  
  85.     def __init__(self):
  86.         self.window = None
  87.         formdef = flp.parse_form('VbForm', 'form')
  88.         flp.create_full_form(self, formdef)
  89.         self.setdefaults()
  90.         if self.vmode <> VM_CONT:
  91.             self.g_cont.hide_object()
  92.         if self.vmode <> VM_BURST:
  93.             self.g_burst.hide_object()
  94.         if self.vmode <> VM_SINGLE:
  95.             self.g_single.hide_object()
  96.         if self.vmode <> VM_VCR:
  97.             self.g_vcr.hide_object()
  98.         if self.vformat <> 'compress':
  99.             self.g_compress.hide_object()
  100.  
  101.         self.openvideo()
  102.         self.makewindow()
  103.         self.bindvideo()
  104.         if self.use_24:
  105.             self.optfullsizewindow()
  106.         self.showform()
  107.         fl.set_event_call_back(self.do_event)
  108.  
  109.     def close(self):
  110.         self.close_video()
  111.         self.close_audio()
  112.         self.savedefaults()
  113.         raise SystemExit, 0
  114.  
  115.     def showform(self):
  116.         # Get position of video window
  117.         gl.winset(self.window)
  118.         x, y = gl.getorigin()
  119.         width, height = gl.getsize()
  120.         # Calculate position of form window
  121.         x1 = x + width + 10
  122.         x2 = x1 + int(self.form.w) - 1
  123.         y2 = y + height - 1
  124.         y1 = y2 - int(self.form.h) + 1
  125.         # Position and show form window
  126.         gl.prefposition(x1, x2, y1, y2)
  127.         self.form.show_form(FL.PLACE_FREE, FL.TRUE, 'Vb Control')
  128.  
  129.     def getdefaultdefaults(self):
  130.         # Video defaults
  131.         self.vfile = 'film.video'
  132.         self.vmode = VM_CONT
  133.         self.mono_thresh = 128
  134.         self.vformat = 'rgb8'
  135.         self.comp_scheme = 'Uncompressed'
  136.         self.rgb24_size = 1
  137.         # Missing: drop, rate, maxmem, nframes, rate, vcrspeed
  138.         # Audio defaults:
  139.         self.afile = 'film.aiff'
  140.         self.aformat = A_OFF
  141.  
  142.     def getdefaults(self):
  143.         self.getdefaultdefaults()
  144.         # XXXX Read defaults file and override.
  145.         try:
  146.             fp = open(VB_INIT_FILE, 'r')
  147.         except IOError:
  148.             print 'Vb: no init file'
  149.             self.initcont = {}
  150.             return
  151.         data = fp.read(1000000)
  152.         try:
  153.             self.initcont = eval(data)
  154.         except:
  155.             print 'Vb: Ill-formatted init file'
  156.             self.initcont = {}
  157.         for k in self.initcont.keys():
  158.             if hasattr(self, k):
  159.                 setattr(self, k, self.initcont[k])
  160.  
  161.     def savedefaults(self):
  162.         newdb = {}
  163.         for k in VB_INIT_KEYS:
  164.             newdb[k] = getattr(self, k)
  165.         if newdb <> self.initcont:
  166.             try:
  167.                 fp = open(VB_INIT_FILE, 'w')
  168.             except IOError:
  169.                 print 'Vb: Cannot create', VB_INIT_FILE
  170.                 return
  171.             fp.write(`newdb`)
  172.             fp.close()
  173.             
  174.     def setdefaults(self):
  175.         self.getdefaults()
  176.         self.vcr = None
  177.         self.vout = None
  178.         self.capturing = 0
  179.         self.c_vformat.clear_choice()
  180.         for label in VideoFormatLabels:
  181.             self.c_vformat.addto_choice(label)
  182.         self.c_vformat.set_choice(1 + VideoFormats.index(self.vformat))
  183.         self.c_vmode.clear_choice()
  184.         for label in VideoModeLabels:
  185.             self.c_vmode.addto_choice(label)
  186.         self.c_vmode.set_choice(self.vmode)
  187.         self.get_vformat()
  188.         self.b_drop.set_button(1)
  189.         self.in_rate.set_input('2')
  190.         self.in_maxmem.set_input('1.0')
  191.         self.in_nframes.set_input('0')
  192.         self.in_nframes_vcr.set_input('1')
  193.         self.in_rate_vcr.set_input('1')
  194.         self.c_vcrspeed.clear_choice()
  195.         for label in VcrSpeedLabels:
  196.             self.c_vcrspeed.addto_choice(label)
  197.         self.c_vcrspeed.set_choice(4)
  198.         self.c_rgb24_size.clear_choice()
  199.         for label in RgbSizeLabels:
  200.             self.c_rgb24_size.addto_choice(label)
  201.         self.c_rgb24_size.set_choice(self.rgb24_size)
  202.         if cl:
  203.             algs = cl.QueryAlgorithms(CL.VIDEO)
  204.             self.all_comp_schemes = []
  205.             for i in range(0, len(algs), 2):
  206.                 if algs[i+1] in (CL.COMPRESSOR, CL.CODEC):
  207.                     self.all_comp_schemes.append(algs[i])
  208.             self.c_cformat.clear_choice()
  209.             for label in self.all_comp_schemes:
  210.                 self.c_cformat.addto_choice(label)
  211.             i = self.all_comp_schemes.index(self.comp_scheme)
  212.             self.c_cformat.set_choice(i+1)
  213.         # Audio defaults
  214.         self.aout = None
  215.         self.aport = None
  216.         self.c_aformat.clear_choice()
  217.         for label in AudioFormatLabels:
  218.             self.c_aformat.addto_choice(label)
  219.         self.c_aformat.set_choice(self.aformat)
  220.         self.get_aformat()
  221.  
  222.     def openvideo(self):
  223.         try:
  224.             self.video = sv.OpenVideo()
  225.         except sv.error, msg:
  226.             print 'Error opening video:', msg
  227.             self.video = None
  228.         param = [SV.BROADCAST, SV.PAL]
  229.         if self.video: self.video.GetParam(param)
  230.         if param[1] == SV.PAL:
  231.             x = SV.PAL_XMAX
  232.             y = SV.PAL_YMAX
  233.         elif param[1] == SV.NTSC:
  234.             x = SV.NTSC_XMAX
  235.             y = SV.NTSC_YMAX
  236.         else:
  237.             print 'Unknown video standard:', param[1]
  238.             sys.exit(1)
  239.         self.maxx, self.maxy = x, y
  240.         self.curx = 256
  241.         self.cury = 256*3/4
  242.  
  243.     def makewindow(self):
  244.         x, y = self.maxx, self.maxy
  245.         gl.foreground()
  246.         gl.maxsize(x, y)
  247.         gl.keepaspect(x, y)
  248.         gl.stepunit(8, 6)
  249.         width = self.curx
  250.         height = self.cury
  251.         if width and height:
  252.             # Place the window at (150, 150) from top left
  253.             # (the video board likes this location...)
  254.             x1 = 150
  255.             x2 = x1+width-1
  256.             SCRHEIGHT = 768
  257.             y2 = SCRHEIGHT-1-150
  258.             y1 = y2-height+1
  259.             gl.prefposition(x1, x2, y1, y2)
  260.         self.window = gl.winopen('Vb video')
  261.         self.settitle()
  262.         if width:
  263.             gl.maxsize(x, y)
  264.             gl.keepaspect(x, y)
  265.             gl.stepunit(8, 6)
  266.             gl.winconstraints()
  267.         gl.qdevice(DEVICE.LEFTMOUSE)
  268.         gl.qdevice(DEVICE.WINQUIT)
  269.         gl.qdevice(DEVICE.WINSHUT)
  270.  
  271.     def optfullsizewindow(self):
  272.         if not self.window:
  273.             return
  274.         gl.winset(self.window)
  275.         if self.use_24:
  276.             x, y = self.maxx, self.maxy
  277.         else:
  278.             x, y = self.curx, self.cury
  279.         left, bottom = gl.getorigin()
  280.         width, height = gl.getsize()
  281.         bottom = bottom+height-y
  282.         gl.prefposition(left, left+x-1, bottom, bottom+y-1)
  283.         gl.winconstraints()
  284.         if not self.use_24:
  285.             gl.keepaspect(x, y)
  286.             gl.stepunit(8, 6)
  287.             gl.maxsize(self.maxx, self.maxy)
  288.             gl.winconstraints()
  289.         self.bindvideo()
  290.  
  291.     def bindvideo(self):
  292.         if not self.video: return
  293.         x, y = gl.getsize()
  294.         if not self.use_24:
  295.             self.curx, self.cury = x, y
  296.         self.video.SetSize(x, y)
  297.         drop = self.b_drop.get_button()
  298.         if drop:
  299.             param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
  300.         else:
  301.             param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
  302.         if self.rgb:
  303.             param = param+[SV.COLOR, SV.DEFAULT_COLOR, \
  304.                        SV.DITHER, 1, \
  305.                        SV.INPUT_BYPASS, 0]
  306.         else:
  307.             param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
  308.                        SV.INPUT_BYPASS, 1]
  309.         self.video.BindGLWindow(self.window, SV.IN_REPLACE)
  310.         self.video.SetParam(param)
  311.  
  312.     def rebindvideo(self):
  313.         gl.winset(self.window)
  314.         self.bindvideo()
  315.  
  316.     def reset(self):
  317.         self.close_video()
  318.         self.close_audio()
  319.         if self.vcr:
  320.             try:
  321.                 ok = self.vcr.still()
  322.             except VCR.error:
  323.                 pass
  324.             self.vcr = None
  325.         self.b_capture.set_button(0)
  326.  
  327.     # Event handler (catches resize of video window)
  328.  
  329.     def do_event(self, dev, val):
  330.         #print 'Event:', dev, val
  331.         if dev in (DEVICE.WINSHUT, DEVICE.WINQUIT):
  332.             self.close()
  333.         if dev == DEVICE.REDRAW and val == self.window:
  334.             self.rebindvideo()
  335.             self.settitle()
  336.  
  337.     # Video controls: format, mode, file
  338.  
  339.     def cb_vformat(self, *args):
  340.         self.reset()
  341.         self.get_vformat()
  342.         if self.mono_use_thresh:
  343.             s = `self.mono_thresh`
  344.             s = fl.show_input('Please enter mono threshold', s)
  345.             if s:
  346.                 try:
  347.                     self.mono_thresh = string.atoi(s)
  348.                 except string.atoi_error:
  349.                     fl.show_message('Bad input, using', \
  350.                           `self.mono_thresh`, '')
  351.         self.rebindvideo()
  352.  
  353.     def cb_cformat(self, *args):
  354.         i = self.c_cformat.get_choice()
  355.         self.comp_scheme = self.all_comp_schemes[i-1]
  356.  
  357.         
  358.     def cb_vmode(self, *args):
  359.         if self.vcr:
  360.             self.vcr = None
  361.         self.vmode = self.c_vmode.get_choice()
  362.         self.form.freeze_form()
  363.         self.g_cont.hide_object()
  364.         self.g_burst.hide_object()
  365.         self.g_single.hide_object()
  366.         self.g_vcr.hide_object()
  367.         if self.vmode == VM_CONT:
  368.             self.g_cont.show_object()
  369.         elif self.vmode == VM_BURST:
  370.             self.g_burst.show_object()
  371.         elif self.vmode == VM_SINGLE:
  372.             self.g_single.show_object()
  373.         elif self.vmode == VM_VCR:
  374.             self.g_vcr.show_object()
  375.         self.form.unfreeze_form()
  376.  
  377.     def cb_vfile(self, *args):
  378.         filename = self.vfile
  379.         hd, tl = os.path.split(filename)
  380.         filename = fl.file_selector('Video save file:', hd, '', tl)
  381.         if filename:
  382.             self.reset()
  383.             hd, tl = os.path.split(filename)
  384.             if hd == os.getcwd():
  385.                 filename = tl
  386.             self.vfile = filename
  387.  
  388.     # Video mode specific video controls
  389.  
  390.     def cb_rate(self, *args):
  391.         pass
  392.  
  393.     def cb_drop(self, *args):
  394.         self.rebindvideo()
  395.  
  396.     def cb_maxmem(self, *args):
  397.         pass
  398.  
  399.     def cb_nframes(self, *args):
  400.         pass
  401.  
  402.     def cb_fps(self, *args):
  403.         pass
  404.  
  405.     def cb_nframes_vcr(self, *args):
  406.         pass
  407.  
  408.     def cb_rate_vcr(self, *args):
  409.         pass
  410.  
  411.     def cb_vcrspeed(self, *args):
  412.         pass
  413.  
  414.     def cb_rgb24_size(self, *args):
  415.         i = self.c_rgb24_size.get_choice()
  416.         if i:
  417.             self.rgb24_size = i
  418.  
  419.     # Audio controls: format, file
  420.  
  421.     def cb_aformat(self, *args):
  422.         self.get_aformat()
  423.  
  424.     def cb_afile(self, *args):
  425.         filename = self.afile
  426.         hd, tl = os.path.split(filename)
  427.         filename = fl.file_selector('Audio save file:', hd, '', tl)
  428.         if filename:
  429.             self.reset()
  430.             hd, tl = os.path.split(filename)
  431.             if hd == os.getcwd():
  432.                 filename = tl
  433.             self.afile = filename
  434.  
  435.     # General controls: capture, reset, play, quit
  436.  
  437.     def cb_capture(self, *args):
  438.         if self.capturing:
  439.             raise StopCapture
  440.         if not self.b_capture.get_button():
  441.             return
  442.         if not self.video or not self.vformat:
  443.             gl.ringbell()
  444.             return
  445.         if self.vmode == VM_CONT:
  446.             self.cont_capture()
  447.         elif self.vmode == VM_BURST:
  448.             self.burst_capture()
  449.         elif self.vmode == VM_SINGLE:
  450.             self.single_capture(None, None)
  451.         elif self.vmode == VM_VCR:
  452.             self.vcr_capture()
  453.  
  454.     def cb_reset(self, *args):
  455.         self.reset()
  456.  
  457.     def cb_play(self, *args):
  458.         self.reset()
  459.         sts = os.system('Vplay -q ' + self.vfile + ' &')
  460.  
  461.     def cb_quit(self, *args):
  462.         self.close()
  463.  
  464.     # Capture routines
  465.  
  466.     def burst_capture(self):
  467.         self.setwatch()
  468.         gl.winset(self.window)
  469.         x, y = gl.getsize()
  470.         if self.use_24:
  471.             fl.show_message('Sorry, no 24 bit continuous capture yet', '', '')
  472.             return
  473.         vformat = SV.RGB8_FRAMES
  474.         nframes = self.getint(self.in_nframes, 0)
  475.         if nframes == 0:
  476.             maxmem = self.getint(self.in_maxmem, 1.0)
  477.             memsize = int(maxmem * 1024 * 1024)
  478.             nframes = self.calcnframes(memsize)
  479.         info = (vformat, x, y, nframes, 1)
  480.         try:
  481.             info2, data, bitvec = self.video.CaptureBurst(info)
  482.         except sv.error, msg:
  483.             self.b_capture.set_button(0)
  484.             self.setarrow()
  485.             fl.show_message('Capture error:', str(msg), '')
  486.             return
  487.         if info <> info2: print info, '<>', info2
  488.         self.save_burst(info2, data, bitvec)
  489.         self.setarrow()
  490.  
  491.     def calcnframes(self, memsize):
  492.         gl.winset(self.window)
  493.         x, y = gl.getsize()
  494.         pixels = x*y
  495.         pixels = pixels/2    # XXX always assume fields
  496.         if self.mono or self.grey:
  497.             n = memsize/pixels
  498.         else:
  499.             n = memsize/(4*pixels)
  500.         return max(1, n)
  501.  
  502.     def save_burst(self, info, data, bitvec):
  503.         (vformat, x, y, nframes, rate) = info
  504.         self.open_if_closed()
  505.         fieldsize = x*y/2
  506.         nskipped = 0
  507.         realframeno = 0
  508.         tpf = 1000 / 50.0     # XXX
  509.         for frameno in range(0, nframes*2):
  510.             if frameno <> 0 and \
  511.                   bitvec[frameno] == bitvec[frameno-1]:
  512.                 nskipped = nskipped + 1
  513.                 continue
  514.             #
  515.             # Save field.
  516.             # XXX Works only for fields and top-to-bottom
  517.             #
  518.             start = frameno*fieldsize
  519.             field = data[start:start+fieldsize]
  520.             realframeno = realframeno + 1
  521.             fn = int(realframeno*tpf)
  522.             if not self.write_frame(fn, field):
  523.                 break
  524.  
  525.     def cont_capture(self):
  526.         saved_label = self.b_capture.label
  527.         self.b_capture.label = 'Stop\n' + saved_label
  528.         self.open_if_closed()
  529.         self.init_cont()
  530.         fps = 59.64        # Fields per second
  531.         # XXX (fps of Indigo monitor, not of PAL or NTSC!)
  532.         tpf = 1000.0 / fps    # Time per field in msec
  533.         self.capturing = 1
  534.         self.start_audio()
  535.         while 1:
  536.             try:
  537.                 void = fl.check_forms()
  538.             except StopCapture:
  539.                 break
  540.             try:
  541.                 cd, id = self.video.GetCaptureData()
  542.             except sv.error:
  543.                 sgi.nap(1)
  544.                 continue
  545.             id = id + 2*self.rate
  546.             data = cd.InterleaveFields(1)
  547.             cd.UnlockCaptureData()
  548.             t = id*tpf
  549.             if not self.write_frame(t, data):
  550.                 break
  551.         self.stop_audio()
  552.         self.capturing = 0
  553.         self.end_cont()
  554.         if self.aout:
  555.             # If recording audio, can't capture multiple sequences
  556.             self.reset()
  557.         self.b_capture.label = saved_label
  558.  
  559.     def single_capture(self, stepfunc, timecode):
  560.         self.open_if_closed()
  561.         self.init_cont()
  562.         while 1:
  563.             try:
  564.                 cd, id = self.video.GetCaptureData()
  565.                 break
  566.             except sv.error:
  567.                 pass
  568.             sgi.nap(1)
  569.             if stepfunc:        # This might step the video
  570.                 d=stepfunc()    # to the next frame
  571.         if not self.use_24:
  572.             data = cd.InterleaveFields(1)
  573.         else:
  574.             x, y = self.vout.getsize()
  575.             if self.use_compress:
  576.                 if self.rgb24_size == 1:
  577.                     data = cd.YUVtoYUV422DC(0)
  578.                 elif self.rgb24_size == 2:
  579.                     data = cd.YUVtoYUV422DC_quarter(1)
  580.                     x = x/2
  581.                     y = y/2
  582.                 elif self.rgb24_size == 3:
  583.                     data = cd.YUVtoYUV422DC_sixteenth(1)
  584.                     x = x/4
  585.                     y = y/4
  586.             else:
  587.                 data = cd.YUVtoRGB(1)
  588.                 if self.maxx*self.maxy*4 <> len(data):
  589.                     print 'maxx,maxy,exp,got=', self.maxx,
  590.                     print self.maxy,self.maxx*self.maxy*4,
  591.                     print len(data)
  592.                     fl.showmessage('Wrong sized data')
  593.                     return 0
  594.                 if self.rgb24_size <> 1:
  595.                     data = imageop.scale(data, 4, \
  596.                           self.maxx, self.maxy, x, y)
  597.             if self.use_jpeg:
  598.                 import jpeg
  599.                 data = jpeg.compress(data, x, y, 4)
  600.             if self.use_compress:
  601.                 data = self.compressor.Compress(1, data)
  602.         cd.UnlockCaptureData()
  603.         self.end_cont()
  604.         if timecode == None:
  605.             timecode = (self.nframes+1) * (1000/25)
  606.         return self.write_frame(timecode, data)
  607.  
  608.     def vcr_capture(self):
  609.         if not self.vcr:
  610.             try:
  611.                 print 'Connecting to VCR ...'
  612.                 self.vcr = VCR.VCR()
  613.                 print 'Waiting for VCR to come online ...'
  614.                 self.vcr.initvcr()
  615.                 print 'Preparing VCR ...'
  616.                 if not (self.vcr.fmmode('dnr') and \
  617.                       self.vcr.dmcontrol('digital slow')):
  618.                     self.vcr_error('digital slow failed')
  619.                     return
  620.                 print 'VCR OK.'
  621.             except VCR.error, msg:
  622.                 self.vcr = None
  623.                 self.vcr_error(msg)
  624.                 return
  625.         if not self.vcr.still():
  626.             self.vcr_error('still failed')
  627.             return
  628.         self.open_if_closed()
  629.         rate = self.getint(self.in_rate_vcr, 1)
  630.         rate = max(rate, 1)
  631.         vcrspeed = self.c_vcrspeed.get_choice()
  632.         vcrspeed = VcrSpeeds[vcrspeed]
  633.         if vcrspeed == 0:
  634.             stepfunc = self.vcr.step
  635.         else:
  636.             stepfunc = None
  637.         self.speed_factor = rate
  638.         addr = start_addr = self.vcr.sense()
  639.         if not self.single_capture(None, 0):
  640.             return
  641.         print 'captured %02d:%02d:%02d:%02d' % self.vcr.addr2tc(addr)
  642.         count = self.getint(self.in_nframes_vcr, 1) - 1
  643.         if count <= 0:
  644.             while rate > 0:
  645.                 if not self.vcr.step():
  646.                     self.vcr_error('step failed')
  647.                 here = self.vcr.sense()
  648.                 if here > addr:
  649.                     rate = rate - (here - addr)
  650.                 addr = here
  651.             return
  652.         if not self.vcr.fwdshuttle(vcrspeed):
  653.             self.vcr_error('fwd shuttle failed')
  654.             return
  655.         cycle = 0
  656.         while count > 0:
  657.             try:
  658.                 here = self.vcr.sense()
  659.             except VCR.error, msg:
  660.                 self.vcr_error(msg)
  661.                 break
  662.             if here <> addr:
  663.                 if here <> addr+1:
  664.                     print 'Missed', here-addr-1,
  665.                     print 'frame' + 's'*(here-addr-1 <> 1)
  666.                 cycle = (cycle+1) % rate
  667.                 if cycle == 0:
  668.                     tc = (here-start_addr)*40
  669.                     if not self.single_capture(stepfunc, \
  670.                           tc):
  671.                         break
  672.                     print 'captured %02d:%02d:%02d:%02d' \
  673.                           % self.vcr.addr2tc(here)
  674.                     count = count -1
  675.                 addr = here
  676.         if self.vcr and not self.vcr.still():
  677.             self.vcr_error('still failed')
  678.  
  679.     def vcr_error(self, msg):
  680.         self.reset()
  681.         fl.show_message('VCR error:', str(msg), '')
  682.  
  683.     # Init/end continuous capture mode
  684.  
  685.     def init_cont(self):
  686.         qsize = 1
  687.         if self.vmode == VM_CONT:
  688.             self.rate = self.getint(self.in_rate, 2)
  689.         else:
  690.             self.rate = 2
  691.         x, y = self.vout.getsize()
  692.         if self.use_24:
  693.             info = (SV.YUV411_FRAMES, x, y, qsize, self.rate)
  694.         else:
  695.             info = (SV.RGB8_FRAMES, x, y, qsize, self.rate)
  696.         info2 = self.video.InitContinuousCapture(info)
  697.         if info2 <> info:
  698.             # XXX This is really only debug info
  699.             print 'Info mismatch: requested', info, 'got', info2
  700.  
  701.     def end_cont(self):
  702.         self.video.EndContinuousCapture()
  703.  
  704.     # Misc stuff
  705.  
  706.     def settitle(self):
  707.         gl.winset(self.window)
  708.         x, y = gl.getsize()
  709.         title = 'Vb ' + self.vfile + ' (%dx%d)' % (x, y)
  710.         gl.wintitle(title)
  711.  
  712.     def get_vformat(self):
  713.         i = self.c_vformat.get_choice()
  714.         label = VideoFormatLabels[i-1]
  715.         format = VideoFormats[i-1]
  716.         if format == 'compress' and cl == None:
  717.             fl.show_message('Sorry, no compression library support')
  718.             format = ''
  719.             label = 'Video off'
  720.         self.vformat = format
  721.         if self.vformat == '':
  722.             self.form.freeze_form()
  723.             self.g_video.hide_object()
  724.             self.g_cont.hide_object()
  725.             self.g_burst.hide_object()
  726.             self.g_single.hide_object()
  727.             self.form.unfreeze_form()
  728.         else:
  729.             self.g_video.show_object()
  730.             if self.vmode == VM_CONT:
  731.                 self.g_cont.show_object()
  732.             elif self.vmode == VM_BURST:
  733.                 self.g_burst.show_object()
  734.             elif self.vmode == VM_SINGLE:
  735.                 self.g_single.show_object()
  736.         #
  737.         self.rgb = (format[:3] == 'rgb' or format == 'compress')
  738.         self.mono = (format == 'mono')
  739.         self.grey = (format[:4] == 'grey')
  740.         self.use_24 = (format in ('rgb', 'jpeg', 'compress'))
  741.         if self.use_24:
  742.             self.g_rgb24.show_object()
  743.         else:
  744.             self.g_rgb24.hide_object()
  745.         self.use_jpeg = (format == 'jpeg')
  746.         self.mono_use_thresh = (label == 'mono thresh')
  747.         self.use_compress = (format == 'compress')
  748.         if self.use_compress:
  749.             self.g_compress.show_object()
  750.         else:
  751.             self.g_compress.hide_object()
  752.         s = format[4:]
  753.         if self.grey and s:
  754.             self.greybits = string.atoi(s)
  755.         else:
  756.             self.greybits = 8
  757.         if label == 'grey2 dith':
  758.             self.greybits = -2
  759.         #
  760.         convertor = None
  761.         if self.grey:
  762.             if self.greybits == 2:
  763.                 convertor = imageop.grey2grey2
  764.             elif self.greybits == 4:
  765.                 convertor = imageop.grey2grey4
  766.             elif self.greybits == -2:
  767.                 convertor = imageop.dither2grey2
  768.         self.convertor = convertor
  769.         self.optfullsizewindow()
  770.  
  771.     def get_aformat(self):
  772.         self.reset()
  773.         self.aformat = self.c_aformat.get_choice()
  774.         if self.aformat == A_OFF:
  775.             self.g_audio.hide_object()
  776.         else:
  777.             self.g_audio.show_object()
  778.  
  779.     def init_compressor(self, w, h):
  780.         self.compressor = None
  781.         scheme = cl.QuerySchemeFromName(CL.VIDEO, self.comp_scheme)
  782.         self.compressor = cl.OpenCompressor(scheme)
  783.         parambuf = [CL.IMAGE_WIDTH, w, \
  784.               CL.IMAGE_HEIGHT, h, \
  785.               CL.ORIGINAL_FORMAT, CL.YUV422DC]
  786.         self.compressor.SetParams(parambuf)
  787.         return self.compressor.Compress(0, '')
  788.  
  789.     def open_if_closed(self):
  790.         if not self.vout:
  791.             self.open_video()
  792.         if not self.aout:
  793.             self.open_audio()
  794.  
  795.     # File I/O handling
  796.  
  797.     def open_video(self):
  798.         self.close_video()
  799.         gl.winset(self.window)
  800.         x, y = gl.getsize()
  801.         if self.use_24:
  802.             if self.rgb24_size == 2:
  803.                 x, y = x/2, y/2
  804.             elif self.rgb24_size == 3:
  805.                 x, y = x/4, y/4
  806.         vout = VFile.VoutFile(self.vfile)
  807.         vout.setformat(self.vformat)
  808.         if self.vformat == 'compress':
  809.             cheader = self.init_compressor(x, y)
  810.             vout.setcompressheader(cheader)
  811.         vout.setsize(x, y)
  812.         if self.vmode == VM_BURST:
  813.             vout.setpf((1, -2))
  814.         vout.writeheader()
  815.         self.vout = vout
  816.         self.nframes = 0
  817.         self.speed_factor = 1
  818.         self.t_nframes.label = `self.nframes`
  819.  
  820.     def write_frame(self, t, data):
  821.         t = t * self.speed_factor
  822.         if not self.vout:
  823.             gl.ringbell()
  824.             return 0
  825.         if self.convertor:
  826.             data = self.convertor(data, len(data), 1)
  827.         elif self.mono:
  828.             if self.mono_use_thresh:
  829.                 data = imageop.grey2mono(data, \
  830.                       len(data), 1,\
  831.                       self.mono_thresh)
  832.             else:
  833.                 data = imageop.dither2mono(data, \
  834.                       len(data), 1)
  835.         try:
  836.             self.vout.writeframe(int(t), data, None)
  837.         except IOError, msg:
  838.             self.reset()
  839.             if msg == (0, 'Error 0'):
  840.                 msg = 'disk full??'
  841.             fl.show_message('IOError', str(msg), '')
  842.             return 0
  843.         self.nframes = self.nframes + 1
  844.         self.t_nframes.label = `self.nframes`
  845.         return 1
  846.  
  847.     def close_video(self):
  848.         if not self.vout:
  849.             return
  850.         self.nframes = 0
  851.         self.t_nframes.label = ''
  852.         try:
  853.             self.vout.close()
  854.         except IOError, msg:
  855.             if msg == (0, 'Error 0'):
  856.                 msg = 'disk full??'
  857.             fl.show_message('IOError', str(msg), '')
  858.         self.vout = None
  859.         self.compressor = None
  860.  
  861.     # Watch cursor handling
  862.  
  863.     def setwatch(self):
  864.         gl.winset(self.form.window)
  865.         gl.setcursor(WATCH, 0, 0)
  866.         gl.winset(self.window)
  867.         gl.setcursor(WATCH, 0, 0)
  868.  
  869.     def setarrow(self):
  870.         gl.winset(self.form.window)
  871.         gl.setcursor(ARROW, 0, 0)
  872.         gl.winset(self.window)
  873.         gl.setcursor(ARROW, 0, 0)
  874.  
  875.     # Numeric field handling
  876.  
  877.     def getint(self, field, default):
  878.         try:
  879.             value = string.atoi(field.get_input())
  880.         except string.atoi_error:
  881.             value = default
  882.         field.set_input(`value`)
  883.         return value
  884.  
  885.     def getfloat(self, field, default):
  886.         try:
  887.             value = float(eval(field.get_input()))
  888.         except:
  889.             value = float(default)
  890.         field.set_input(`value`)
  891.         return value
  892.  
  893.     # Audio stuff
  894.  
  895.     def open_audio(self):
  896.         if self.aformat == A_OFF:
  897.             return
  898.         import aifc
  899.         import al
  900.         import AL
  901.         import thread
  902.         self.close_audio()
  903.         params = [AL.INPUT_RATE, 0]
  904.         al.getparams(AL.DEFAULT_DEVICE, params)
  905.         rate = params[1]
  906.         self.aout = aifc.open(self.afile, 'w')
  907.         if self.aformat in (A_16_STEREO, A_8_STEREO):
  908.             nch = AL.STEREO
  909.         else:
  910.             nch = AL.MONO
  911.         if self.aformat in (A_16_STEREO, A_16_MONO):
  912.             width = AL.SAMPLE_16
  913.         else:
  914.             width = AL.SAMPLE_8
  915.         self.aout.setnchannels(nch)
  916.         self.aout.setsampwidth(width)
  917.         self.aout.setframerate(rate)
  918.         c = al.newconfig()
  919.         c.setqueuesize(8000)
  920.         c.setchannels(nch)
  921.         c.setwidth(width)
  922.         self.aport = al.openport('Vb audio record', 'r', c)
  923.         self.audio_stop = 0
  924.         self.audio_ok = 0
  925.         self.audio_busy = 1
  926.         thread.start_new_thread(self.record_audio, ())
  927.  
  928.     def start_audio(self):
  929.         if self.aformat == A_OFF:
  930.             return
  931.         self.audio_ok = 1
  932.  
  933.     def record_audio(self, *args):
  934.         # This function runs in a separate thread
  935.         # Currently no semaphores are used
  936.         while not self.audio_stop:
  937.             data = self.aport.readsamps(4000)
  938.             if self.audio_ok:
  939.                 self.aout.writeframes(data)
  940.             data = None
  941.         self.audio_busy = 0
  942.  
  943.     def stop_audio(self):
  944.         self.audio_ok = 0
  945.  
  946.     def close_audio(self):
  947.         if self.aout:
  948.             self.audio_ok = 0
  949.             self.audio_stop = 1
  950.             while self.audio_busy:
  951.                 time.sleep(0.1)
  952.             self.aout.close()
  953.             self.aout = None
  954.         if self.aport:
  955.             self.aport.closeport()
  956.             self.aport = None
  957.  
  958.  
  959. try:
  960.     main()
  961. except KeyboardInterrupt:
  962.     print '[Interrupt]'
  963.     sys.exit(1)
  964.