Files for 2.1b1 distribution.
[python/dscho.git] / Demo / sgi / video / Vrec.py
blob11039bbfd13e4ea54c17a71b129ab06a8c321883
1 #! /usr/bin/env python
2 #! /ufs/guido/bin/sgi/python-405
4 # Capture a CMIF movie using the Indigo video library and board
6 # The CMIF video file format is documented in cmif-film.ms.
7 # Audio data is recorded in AIFF format, using the input sampling
8 # rate, source and volume set by the audio panel, in mono, 8
9 # bits/sample.
12 # Usage and help functions (keep this up-to-date if you change the program!)
14 def usage():
15 print 'Usage: Vrec [options] [moviefile [audiofile]]'
16 print
17 print 'Options:'
18 print '-a : record audio as well'
19 print '-q queuesize : set the capture queue size (default 2)'
20 print '-r rate : capture 1 out of every "rate" frames', \
21 '(default and min 2)'
22 print '-w width : initial window width', \
23 '(default 256, use 0 for interactive placement)'
24 print '-n : Don\'t write to file, only timing info'
25 print '-d : drop fields if needed'
26 print '-g bits : greyscale (2, 4 or 8 bits)'
27 print '-G : 2-bit greyscale dithered'
28 print '-m : monochrome dithered'
29 print '-M value : monochrome thresholded with value'
30 print '-f : Capture fields (in stead of frames)'
31 print '-P frames : preallocate space for "frames" frames'
32 print 'moviefile : here goes the movie data (default film.video)'
33 print 'audiofile : with -a, here goes the audio data', \
34 '(default film.aiff)'
36 def help():
37 print 'Press the left mouse button to start recording, release it to'
38 print 'end recording. You can record as many times as you wish, but'
39 print 'each recording overwrites the output file(s) -- only the last'
40 print 'recording is kept.'
41 print
42 print 'Press ESC or use the window manager Quit or Close window option'
43 print 'to quit. If you quit before recording anything, the output'
44 print 'file(s) are not touched.'
47 # Imported modules
49 import sys
50 sys.path.append('/ufs/guido/src/video')
51 import sv, SV
52 import VFile
53 import gl, GL, DEVICE
54 import al, AL
55 import time
56 import posix
57 import getopt
58 import string
59 import imageop
60 import sgi
63 # Main program
65 def main():
66 format = SV.RGB8_FRAMES
67 qsize = 2
68 audio = 0
69 rate = 2
70 width = 0
71 norecord = 0
72 drop = 0
73 mono = 0
74 grey = 0
75 greybits = 0
76 monotreshold = -1
77 fields = 0
78 preallocspace = 0
80 # Parse command line
81 try:
82 opts, args = getopt.getopt(sys.argv[1:], 'aq:r:w:ndg:mM:GfP:')
83 except getopt.error, msg:
84 sys.stdout = sys.stderr
85 print 'Error:', msg, '\n'
86 usage()
87 sys.exit(2)
89 # Interpret options
90 try:
91 for opt, arg in opts:
92 if opt == '-a':
93 audio = 1
94 elif opt == '-q':
95 qsize = string.atoi(arg)
96 elif opt == '-r':
97 rate = string.atoi(arg)
98 if rate < 2:
99 sys.stderr.write( \
100 '-r rate must be >= 2\n')
101 sys.exit(2)
102 elif opt == '-w':
103 width = string.atoi(arg)
104 elif opt == '-n':
105 norecord = 1
106 elif opt == '-d':
107 drop = 1
108 elif opt == '-g':
109 grey = 1
110 greybits = string.atoi(arg)
111 if not greybits in (2, 4, 8):
112 sys.stderr.write( \
113 'Only 2, 4 or 8 bit greyscale supported\n')
114 sys.exit(2)
115 elif opt == '-G':
116 grey = 1
117 greybits = -2
118 elif opt == '-m':
119 mono = 1
120 elif opt == '-M':
121 mono = 1
122 monotreshold = string.atoi(arg)
123 elif opt == '-f':
124 fields = 1
125 elif opt == '-P':
126 preallocspace = string.atoi(arg)
127 except string.atoi_error:
128 sys.stdout = sys.stderr
129 print 'Option', opt, 'requires integer argument'
130 sys.exit(2)
132 # Check excess arguments
133 # If norecord is on, refuse filename arguments
134 if norecord:
135 if args:
136 sys.stdout = sys.stderr
137 print 'With -n, no filename arguments are used\n'
138 usage()
139 sys.exit(2)
140 elif args[2:]:
141 sys.stdout = sys.stderr
142 print 'Too many filename arguments\n'
143 usage()
144 sys.exit(2)
146 # Process file arguments
147 if args:
148 filename = args[0]
149 else:
150 filename = 'film.video'
152 if args[1:] and not audio:
153 sys.stderr.write('-a turned on by appearance of 2nd file\n')
154 audio = 1
156 if audio:
157 if args[1:]:
158 audiofilename = args[1]
159 else:
160 audiofilename = 'film.aiff'
161 else:
162 audiofilename = None
164 if norecord:
165 filename = audiofilename = ''
167 # Open video
168 v = sv.OpenVideo()
169 # Determine maximum window size based on signal standard
170 param = [SV.BROADCAST, 0]
171 v.GetParam(param)
172 if param[1] == SV.PAL:
173 x = SV.PAL_XMAX
174 y = SV.PAL_YMAX
175 elif param[1] == SV.NTSC:
176 x = SV.NTSC_XMAX
177 y = SV.NTSC_YMAX
178 else:
179 print 'Unknown video standard', param[1]
180 sys.exit(1)
182 gl.foreground()
183 gl.maxsize(x, y)
184 gl.keepaspect(x, y)
185 gl.stepunit(8, 6)
186 if width:
187 height = width*3/4
188 x1 = 150
189 x2 = x1 + width-1
190 y2 = 768-150
191 y1 = y2-height+1
192 gl.prefposition(x1, x2, y1, y2)
193 win = gl.winopen(filename)
194 if width:
195 gl.maxsize(x, y)
196 gl.keepaspect(x, y)
197 gl.stepunit(8, 6)
198 gl.winconstraints()
199 x, y = gl.getsize()
200 print x, 'x', y
202 v.SetSize(x, y)
204 if drop:
205 param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
206 else:
207 param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
208 if mono or grey:
209 param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
210 SV.INPUT_BYPASS, 1]
211 else:
212 param = param+[SV.COLOR, SV.DEFAULT_COLOR, SV.INPUT_BYPASS, 0]
214 v.BindGLWindow(win, SV.IN_REPLACE)
215 v.SetParam(param)
217 gl.qdevice(DEVICE.LEFTMOUSE)
218 gl.qdevice(DEVICE.WINQUIT)
219 gl.qdevice(DEVICE.WINSHUT)
220 gl.qdevice(DEVICE.ESCKEY)
222 help()
224 while 1:
225 dev, val = gl.qread()
226 if dev == DEVICE.LEFTMOUSE:
227 if val == 1:
228 info = format, x, y, qsize, rate
229 record(v, info, filename, audiofilename,\
230 mono, grey, greybits, monotreshold, \
231 fields, preallocspace)
232 elif dev == DEVICE.REDRAW:
233 # Window resize (or move)
234 x, y = gl.getsize()
235 print x, 'x', y
236 v.SetSize(x, y)
237 v.BindGLWindow(win, SV.IN_REPLACE)
238 elif dev in (DEVICE.ESCKEY, DEVICE.WINQUIT, DEVICE.WINSHUT):
239 # Quit
240 v.CloseVideo()
241 gl.winclose(win)
242 break
245 # Record until the mouse is released (or any other GL event)
246 # XXX audio not yet supported
248 def record(v, info, filename, audiofilename, mono, grey, greybits, \
249 monotreshold, fields, preallocspace):
250 import thread
251 format, x, y, qsize, rate = info
252 fps = 59.64 # Fields per second
253 # XXX (Strange: need fps of Indigo monitor, not of PAL or NTSC!)
254 tpf = 1000.0 / fps # Time per field in msec
255 if filename:
256 vout = VFile.VoutFile(filename)
257 if mono:
258 format = 'mono'
259 elif grey and greybits == 8:
260 format = 'grey'
261 elif grey:
262 format = 'grey'+`abs(greybits)`
263 else:
264 format = 'rgb8'
265 vout.setformat(format)
266 vout.setsize(x, y)
267 if fields:
268 vout.setpf((1, -2))
269 vout.writeheader()
270 if preallocspace:
271 print 'Preallocating space...'
272 vout.prealloc(preallocspace)
273 print 'done.'
274 MAXSIZE = 20 # XXX should be a user option
275 import Queue
276 queue = Queue.Queue(MAXSIZE)
277 done = thread.allocate_lock()
278 done.acquire_lock()
279 convertor = None
280 if grey:
281 if greybits == 2:
282 convertor = imageop.grey2grey2
283 elif greybits == 4:
284 convertor = imageop.grey2grey4
285 elif greybits == -2:
286 convertor = imageop.dither2grey2
287 thread.start_new_thread(saveframes, \
288 (vout, queue, done, mono, monotreshold, convertor))
289 if audiofilename:
290 audiodone = thread.allocate_lock()
291 audiodone.acquire_lock()
292 audiostop = []
293 initaudio(audiofilename, audiostop, audiodone)
294 gl.wintitle('(rec) ' + filename)
295 lastid = 0
296 t0 = time.time()
297 count = 0
298 ids = []
299 v.InitContinuousCapture(info)
300 while not gl.qtest():
301 try:
302 cd, id = v.GetCaptureData()
303 except sv.error:
304 #time.sleep(0.010) # XXX is this necessary?
305 sgi.nap(1) # XXX Try by Jack
306 continue
307 ids.append(id)
309 id = id + 2*rate
310 ## if id <> lastid + 2*rate:
311 ## print lastid, id
312 lastid = id
313 count = count+1
314 if fields:
315 data1, data2 = cd.GetFields()
316 cd.UnlockCaptureData()
317 if filename:
318 queue.put((data1, int(id*tpf)))
319 queue.put((data2, int((id+1)*tpf)))
320 else:
321 data = cd.InterleaveFields(1)
322 cd.UnlockCaptureData()
323 if filename:
324 queue.put((data, int(id*tpf)))
325 t1 = time.time()
326 gl.wintitle('(busy) ' + filename)
327 print lastid, 'fields in', round(t1-t0, 3), 'sec',
328 print '--', round(lastid/(t1-t0), 1), 'fields/sec'
329 print 'Captured',count*2, 'fields,',
330 print round(count*2/(t1-t0), 1), 'f/s',
331 if lastid:
332 print '(',
333 print round(count*200.0/lastid), '%, or',
334 print round(count*rate*200.0/lastid), '% of wanted rate )',
335 print
336 if ids:
337 print 'Ids:',
338 t0 = ids[0]
339 del ids[0]
340 for t1 in ids:
341 print t1-t0,
342 t0 = t1
343 print
344 if filename and audiofilename:
345 audiostop.append(None)
346 audiodone.acquire_lock()
347 v.EndContinuousCapture()
348 if filename:
349 queue.put(None) # Sentinel
350 done.acquire_lock()
351 gl.wintitle('(done) ' + filename)
354 # Thread to save the frames to the file
356 def saveframes(vout, queue, done, mono, monotreshold, convertor):
357 while 1:
358 x = queue.get()
359 if not x:
360 break
361 data, t = x
362 if convertor:
363 data = convertor(data, len(data), 1)
364 elif mono and monotreshold >= 0:
365 data = imageop.grey2mono(data, len(data), 1,\
366 monotreshold)
367 elif mono:
368 data = imageop.dither2mono(data, len(data), 1)
369 vout.writeframe(t, data, None)
370 sys.stderr.write('Done writing video\n')
371 vout.close()
372 done.release_lock()
375 # Initialize audio recording
377 AQSIZE = 8000 # XXX should be a user option
379 def initaudio(filename, stop, done):
380 import thread, aifc
381 afile = aifc.open(filename, 'w')
382 afile.setnchannels(AL.MONO)
383 afile.setsampwidth(AL.SAMPLE_8)
384 params = [AL.INPUT_RATE, 0]
385 al.getparams(AL.DEFAULT_DEVICE, params)
386 print 'audio sampling rate =', params[1]
387 afile.setframerate(params[1])
388 c = al.newconfig()
389 c.setchannels(AL.MONO)
390 c.setqueuesize(AQSIZE)
391 c.setwidth(AL.SAMPLE_8)
392 aport = al.openport(filename, 'r', c)
393 thread.start_new_thread(audiorecord, (afile, aport, stop, done))
396 # Thread to record audio samples
398 def audiorecord(afile, aport, stop, done):
399 while not stop:
400 data = aport.readsamps(AQSIZE/2)
401 afile.writesampsraw(data)
402 del data
403 afile.close()
404 print 'Done writing audio'
405 done.release_lock()
408 # Don't forget to call the main program
410 try:
411 main()
412 except KeyboardInterrupt:
413 print '[Interrupt]'