Merged release21-maint changes.
[python/dscho.git] / Demo / sgi / video / Vrecb.py
blob5cdde9e6211f0bbb06f9fc47368b79e5da3369f2
1 #! /usr/bin/env python
3 # Capture a CMIF movie using the Indigo video library and board in burst mode
6 # User interface:
8 # Start the application. Resize the window to the desired movie size.
9 # Press the left mouse button to start recording, release it to end
10 # recording. You can record as many times as you wish, but each time
11 # you overwrite the output file(s), so only the last recording is
12 # kept.
14 # Press ESC or select the window manager Quit or Close window option
15 # to quit. If you quit before recording anything, the output file(s)
16 # are not touched.
19 import sys
20 sys.path.append('/ufs/guido/src/video')
21 import sv, SV
22 import VFile
23 import gl, GL, DEVICE
24 import al, AL
25 import time
26 import posix
27 import getopt
28 import string
29 import imageop
30 import sgi
33 # Usage and help functions (keep this up-to-date if you change the program!)
35 def usage():
36 print 'Usage: Vrecb [options] [moviefile [audiofile]]'
37 print
38 print 'Options:'
39 print '-a : record audio as well'
40 print '-r rate : capture 1 out of every "rate" frames', \
41 '(default and min 1)'
42 print '-w width : initial window width', \
43 '(default 256, use 0 for interactive placement)'
44 print '-d : drop fields if needed'
45 print '-g bits : greyscale (2, 4 or 8 bits)'
46 print '-G : 2-bit greyscale dithered'
47 print '-m : monochrome dithered'
48 print '-M value : monochrome thresholded with value'
49 print '-f : Capture fields (instead of frames)'
50 print '-n number : Capture this many frames (default 60)'
51 print '-N memsize : Capture frames fitting in this many kbytes'
52 print 'moviefile : here goes the movie data (default film.video)'
54 def help():
55 print 'Press the left mouse button to start recording.'
56 print 'Recording time is determined by the -n option.'
57 print 'You can record as many times as you wish, but each'
58 print 'recording overwrites the output file(s) -- only the'
59 print 'last recording is kept.'
60 print
61 print 'Press ESC or use the window manager Quit or Close window option'
62 print 'to quit. If you quit before recording anything, the output'
63 print 'file(s) are not touched.'
66 # Main program
68 def main():
69 format = SV.RGB8_FRAMES
70 audio = 0
71 rate = 1
72 width = 256
73 drop = 0
74 mono = 0
75 grey = 0
76 greybits = 0
77 monotreshold = -1
78 fields = 0
79 number = 0
80 memsize = 0
82 try:
83 opts, args = getopt.getopt(sys.argv[1:], 'ar:w:dg:mM:Gfn:N:')
84 except getopt.error, msg:
85 sys.stdout = sys.stderr
86 print 'Error:', msg, '\n'
87 usage()
88 sys.exit(2)
90 try:
91 for opt, arg in opts:
92 if opt == '-a':
93 audio = 1
94 if opt == '-r':
95 rate = string.atoi(arg)
96 if rate < 1:
97 sys.stderr.write('-r rate must be >= 1\n')
98 sys.exit(2)
99 elif opt == '-w':
100 width = string.atoi(arg)
101 elif opt == '-d':
102 drop = 1
103 elif opt == '-g':
104 grey = 1
105 greybits = string.atoi(arg)
106 if not greybits in (2,4,8):
107 sys.stderr.write( \
108 'Only 2, 4 or 8 bit greyscale supported\n')
109 sys.exit(2)
110 elif opt == '-G':
111 grey = 1
112 greybits = -2
113 elif opt == '-m':
114 mono = 1
115 elif opt == '-M':
116 mono = 1
117 monotreshold = string.atoi(arg)
118 elif opt == '-f':
119 fields = 1
120 elif opt == '-n':
121 number = string.atoi(arg)
122 elif opt == '-N':
123 memsize = string.atoi(arg)
124 if 0 < memsize < 1024:
125 memsize = memsize * 1024
126 if 0 < memsize < 1024*1024:
127 memsize = memsize * 1024
128 print 'memsize', memsize
129 except string.atoi_error:
130 sys.stdout = sys.stderr
131 print 'Option', opt, 'requires integer argument'
132 sys.exit(2)
134 if number <> 0 and memsize <> 0:
135 sys.stderr.write('-n and -N are mutually exclusive\n')
136 sys.exit(2)
137 if number == 0 and memsize == 0:
138 number = 60
140 if not fields:
141 print '-f option assumed until somebody fixes it'
142 fields = 1
144 if args[2:]:
145 sys.stderr.write('usage: Vrecb [options] [file [audiofile]]\n')
146 sys.exit(2)
148 if args:
149 filename = args[0]
150 else:
151 filename = 'film.video'
153 if args[1:] and not audio:
154 sys.stderr.write('-a turned on by appearance of 2nd file\n')
155 audio = 1
157 if audio:
158 if args[1:]:
159 audiofilename = args[1]
160 else:
161 audiofilename = 'film.aiff'
162 else:
163 audiofilename = None
165 v = sv.OpenVideo()
166 # Determine maximum window size based on signal standard
167 param = [SV.BROADCAST, 0]
168 v.GetParam(param)
169 if param[1] == SV.PAL:
170 x = SV.PAL_XMAX
171 y = SV.PAL_YMAX
172 elif param[1] == SV.NTSC:
173 x = SV.NTSC_XMAX
174 y = SV.NTSC_YMAX
175 else:
176 print 'Unknown video standard', param[1]
177 sys.exit(1)
179 gl.foreground()
180 gl.maxsize(x, y)
181 gl.keepaspect(x, y)
182 gl.stepunit(8, 6)
183 if width:
184 height = width*3/4
185 x1 = 150
186 x2 = x1 + width-1
187 y2 = 768-150
188 y1 = y2-height+1
189 gl.prefposition(x1, x2, y1, y2)
190 win = gl.winopen(filename)
191 if width:
192 gl.maxsize(x, y)
193 gl.keepaspect(x, y)
194 gl.stepunit(8, 6)
195 gl.winconstraints()
196 x, y = gl.getsize()
197 print x, 'x', y
198 if memsize:
199 number = calcnumber(x, y, grey or mono, memsize)
200 print number, 'frames'
201 v.SetSize(x, y)
203 if drop:
204 param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
205 else:
206 param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
207 if mono or grey:
208 param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
209 SV.INPUT_BYPASS, 1]
210 else:
211 param = param+[SV.COLOR, SV.DEFAULT_COLOR, SV.INPUT_BYPASS, 0]
213 v.BindGLWindow(win, SV.IN_REPLACE)
214 v.SetParam(param)
216 gl.qdevice(DEVICE.LEFTMOUSE)
217 gl.qdevice(DEVICE.WINQUIT)
218 gl.qdevice(DEVICE.WINSHUT)
219 gl.qdevice(DEVICE.ESCKEY)
221 help()
223 while 1:
224 dev, val = gl.qread()
225 if dev == DEVICE.LEFTMOUSE:
226 if val == 1:
227 info = format, x, y, number, rate
228 record(v, info, filename, audiofilename, \
229 mono, grey, \
230 greybits, monotreshold, fields)
231 elif dev == DEVICE.REDRAW:
232 # Window resize (or move)
233 x, y = gl.getsize()
234 print x, 'x', y
235 if memsize:
236 number = calcnumber(x, y, grey or mono, memsize)
237 print number, 'frames'
238 v.SetSize(x, y)
239 v.BindGLWindow(win, SV.IN_REPLACE)
240 elif dev in (DEVICE.ESCKEY, DEVICE.WINQUIT, DEVICE.WINSHUT):
241 # Quit
242 v.CloseVideo()
243 gl.winclose(win)
244 break
247 def calcnumber(x, y, grey, memsize):
248 pixels = x*y
249 pixels = pixels/2 # XXX always assume fields
250 if grey: n = memsize/pixels
251 else: n = memsize/(4*pixels)
252 return max(1, n)
255 # Record until the mouse is released (or any other GL event)
256 # XXX audio not yet supported
258 def record(v, info, filename, audiofilename, \
259 mono, grey, greybits, monotreshold, fields):
260 import thread
261 format, x, y, number, rate = info
262 fps = 59.64 # Fields per second
263 # XXX (Strange: need fps of Indigo monitor, not of PAL or NTSC!)
264 tpf = 1000.0 / fps # Time per field in msec
266 # Go grab
268 if audiofilename:
269 gl.wintitle('(start audio) ' + filename)
270 audiodone = thread.allocate_lock()
271 audiodone.acquire_lock()
272 audiostart = thread.allocate_lock()
273 audiostart.acquire_lock()
274 audiostop = []
275 initaudio(audiofilename, audiostop, audiostart, audiodone)
276 audiostart.acquire_lock()
277 gl.wintitle('(rec) ' + filename)
278 try:
279 ninfo, data, bitvec = v.CaptureBurst(info)
280 except sv.error, arg:
281 print 'CaptureBurst failed:', arg
282 print 'info:', info
283 gl.wintitle(filename)
284 return
285 gl.wintitle('(save) '+ filename)
287 # Check results
289 if info <> ninfo:
290 print 'Sorry, format changed.'
291 print 'Wanted:',info
292 print 'Got :',ninfo
293 gl.wintitle(filename)
294 return
295 # print bitvec
296 if x*y*number <> len(data):
297 print 'Funny data length: wanted',x,'*',y,'*', number,'=',\
298 x*y*number,'got',len(data)
299 gl.wintitle(filename)
300 return
302 # Save
304 if filename and audiofilename:
305 audiostop.append(None)
306 audiodone.acquire_lock()
307 if filename:
309 # Construct header and write it
311 try:
312 vout = VFile.VoutFile(filename)
313 except IOError, msg:
314 print filename, ':', msg
315 sys.exit(1)
316 if mono:
317 vout.format = 'mono'
318 elif grey and greybits == 8:
319 vout.format = 'grey'
320 elif grey:
321 vout.format = 'grey'+`abs(greybits)`
322 else:
323 vout.format = 'rgb8'
324 vout.width = x
325 vout.height = y
326 if fields:
327 vout.packfactor = (1,-2)
328 else:
329 print 'Sorry, can only save fields at the moment'
330 print '(i.e. you *must* use the -f option)'
331 gl.wintitle(filename)
332 return
333 vout.writeheader()
335 # Compute convertor, if needed
337 convertor = None
338 if grey:
339 if greybits == 2:
340 convertor = imageop.grey2grey2
341 elif greybits == 4:
342 convertor = imageop.grey2grey4
343 elif greybits == -2:
344 convertor = imageop.dither2grey2
345 fieldsize = x*y/2
346 nskipped = 0
347 realframeno = 0
348 tpf = 1000 / 50.0 #XXXX
349 # Trying to find the pattern in frame skipping
350 okstretch = 0
351 skipstretch = 0
352 for frameno in range(0, number*2):
353 if frameno <> 0 and \
354 bitvec[frameno] == bitvec[frameno-1]:
355 nskipped = nskipped + 1
356 if okstretch:
357 print okstretch, 'ok',
358 okstretch = 0
359 skipstretch = skipstretch + 1
360 continue
361 if skipstretch:
362 print skipstretch, 'skipped'
363 skipstretch = 0
364 okstretch = okstretch + 1
366 # Save field.
367 # XXXX Works only for fields and top-to-bottom
369 start = frameno*fieldsize
370 field = data[start:start+fieldsize]
371 if convertor:
372 field = convertor(field, len(field), 1)
373 elif mono and monotreshold >= 0:
374 field = imageop.grey2mono( \
375 field, len(field), 1, monotreshold)
376 elif mono:
377 field = imageop.dither2mono( \
378 field, len(field), 1)
379 realframeno = realframeno + 1
380 vout.writeframe(int(realframeno*tpf), field, None)
381 print okstretch, 'ok',
382 print skipstretch, 'skipped'
383 print 'Skipped', nskipped, 'duplicate frames'
384 vout.close()
386 gl.wintitle('(done) ' + filename)
387 # Initialize audio recording
389 AQSIZE = 8*8000 # XXX should be a user option
391 def initaudio(filename, stop, start, done):
392 import thread, aifc
393 afile = aifc.open(filename, 'w')
394 afile.setnchannels(AL.MONO)
395 afile.setsampwidth(AL.SAMPLE_8)
396 params = [AL.INPUT_RATE, 0]
397 al.getparams(AL.DEFAULT_DEVICE, params)
398 print 'audio sampling rate =', params[1]
399 afile.setframerate(params[1])
400 c = al.newconfig()
401 c.setchannels(AL.MONO)
402 c.setqueuesize(AQSIZE)
403 c.setwidth(AL.SAMPLE_8)
404 aport = al.openport(filename, 'r', c)
405 thread.start_new_thread(audiorecord, (afile, aport, stop, start, done))
408 # Thread to record audio samples
410 def audiorecord(afile, aport, stop, start, done):
411 start.release_lock()
412 leeway = 4
413 while leeway > 0:
414 if stop:
415 leeway = leeway - 1
416 data = aport.readsamps(AQSIZE/8)
417 afile.writesampsraw(data)
418 del data
419 afile.close()
420 print 'Done writing audio'
421 done.release_lock()
424 # Don't forget to call the main program
426 try:
427 main()
428 except KeyboardInterrupt:
429 print '[Interrupt]'