blob: 689e8cf5a8fe46a1f7d16d4010839edac31fff39 [file] [log] [blame]
Guido van Rossum180924d1992-08-20 11:46:28 +00001#! /ufs/guido/bin/sgi/python-405
2#! /ufs/guido/bin/sgi/python
3
4# Capture a CMIF movie using the Indigo video library and board
5
6
7# Usage:
8#
Guido van Rossum32517f91992-09-04 13:26:59 +00009# makemovie [-a] [-q queuesize] [-r rate] [-w width] [moviefile [audiofile]]
Guido van Rossum180924d1992-08-20 11:46:28 +000010
11
12# Options:
13#
14# -a : record audio as well
Guido van Rossum62f6bc81992-09-03 16:56:04 +000015# -q queuesize : set the capture queue size (default 2)
Guido van Rossum32517f91992-09-04 13:26:59 +000016# -r rate : capture 1 out of every 'rate' frames (default and min 2)
17# -w width : initial window width (default interactive placement)
Jack Jansen3b253711992-12-14 12:25:21 +000018# -n : Don't write to file, only timing info
19# -d : drop fields if needed
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000020# -g bits : greyscale (2, 4 or 8 bits)
21# -G : 2-bit greyscale dithered
Jack Jansen3b253711992-12-14 12:25:21 +000022# -m : monochrome dithered
23# -M value : monochrome tresholded with value
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000024# -f : Capture fields (in stead of frames)
25# -P frames : preallocate space for 'frames' frames
Guido van Rossum180924d1992-08-20 11:46:28 +000026#
27# moviefile : here goes the movie data (default film.video);
28# the format is documented in cmif-film.ms
29# audiofile : with -a, here goes the audio data (default film.aiff);
30# audio data is recorded in AIFF format, using the
31# input sampling rate, source and volume set by the
32# audio panel, in mono, 8 bits/sample
33
34
35# User interface:
36#
37# Start the application. Resize the window to the desired movie size.
38# Press the left mouse button to start recording, release it to end
Guido van Rossum62f6bc81992-09-03 16:56:04 +000039# recording. You can record as many times as you wish, but each time
40# you overwrite the output file(s), so only the last recording is
41# kept.
Guido van Rossum180924d1992-08-20 11:46:28 +000042#
43# Press ESC or select the window manager Quit or Close window option
Guido van Rossum62f6bc81992-09-03 16:56:04 +000044# to quit. If you quit before recording anything, the output file(s)
45# are not touched.
Guido van Rossum180924d1992-08-20 11:46:28 +000046
47
48import sys
49sys.path.append('/ufs/guido/src/video')
50import sv, SV
51import VFile
52import gl, GL, DEVICE
53import al, AL
54import time
55import posix
56import getopt
57import string
Jack Jansen3b253711992-12-14 12:25:21 +000058import imageop
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000059import sgi
Guido van Rossum180924d1992-08-20 11:46:28 +000060
61# Main program
62
63def main():
Guido van Rossum62f6bc81992-09-03 16:56:04 +000064 format = SV.RGB8_FRAMES
65 qsize = 2
Guido van Rossum180924d1992-08-20 11:46:28 +000066 audio = 0
Guido van Rossum62f6bc81992-09-03 16:56:04 +000067 rate = 2
Guido van Rossum32517f91992-09-04 13:26:59 +000068 width = 0
Jack Jansen3b253711992-12-14 12:25:21 +000069 norecord = 0
70 drop = 0
71 mono = 0
72 grey = 0
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000073 greybits = 0
Jack Jansen3b253711992-12-14 12:25:21 +000074 monotreshold = -1
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000075 fields = 0
76 preallocspace = 0
Guido van Rossum180924d1992-08-20 11:46:28 +000077
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000078 opts, args = getopt.getopt(sys.argv[1:], 'aq:r:w:ndg:mM:GfP:')
Guido van Rossum180924d1992-08-20 11:46:28 +000079 for opt, arg in opts:
80 if opt == '-a':
81 audio = 1
82 elif opt == '-q':
Guido van Rossum62f6bc81992-09-03 16:56:04 +000083 qsize = string.atoi(arg)
Guido van Rossum180924d1992-08-20 11:46:28 +000084 elif opt == '-r':
Guido van Rossum62f6bc81992-09-03 16:56:04 +000085 rate = string.atoi(arg)
86 if rate < 2:
87 sys.stderr.write('-r rate must be >= 2\n')
88 sys.exit(2)
Guido van Rossum32517f91992-09-04 13:26:59 +000089 elif opt == '-w':
90 width = string.atoi(arg)
Jack Jansen3b253711992-12-14 12:25:21 +000091 elif opt == '-n':
92 norecord = 1
93 elif opt == '-d':
94 drop = 1
95 elif opt == '-g':
96 grey = 1
Jack Jansen6bc8c7f1992-12-23 15:37:20 +000097 greybits = string.atoi(arg)
98 if not greybits in (2,4,8):
99 print 'Only 2, 4 or 8 bit greyscale supported'
100 elif opt == '-G':
101 grey = 1
102 greybits = -2
Jack Jansen3b253711992-12-14 12:25:21 +0000103 elif opt == '-m':
104 mono = 1
105 elif opt == '-M':
106 mono = 1
107 monotreshold = string.atoi(arg)
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000108 elif opt == '-f':
109 fields = 1
110 elif opt == '-P':
111 preallocspace = string.atoi(arg)
Guido van Rossum180924d1992-08-20 11:46:28 +0000112
Guido van Rossum8a861be1992-08-20 14:46:46 +0000113 if args[2:]:
114 sys.stderr.write('usage: Vrec [options] [file [audiofile]]\n')
115 sys.exit(2)
116
Guido van Rossum180924d1992-08-20 11:46:28 +0000117 if args:
118 filename = args[0]
119 else:
120 filename = 'film.video'
121
Guido van Rossum8a861be1992-08-20 14:46:46 +0000122 if args[1:] and not audio:
123 sys.stderr.write('-a turned on by appearance of 2nd file\n')
124 audio = 1
125
Guido van Rossum180924d1992-08-20 11:46:28 +0000126 if audio:
127 if args[1:]:
128 audiofilename = args[1]
129 else:
130 audiofilename = 'film.aiff'
131 else:
132 audiofilename = None
133
Jack Jansen3b253711992-12-14 12:25:21 +0000134 if norecord:
135 filename = audiofilename = ''
Guido van Rossum32517f91992-09-04 13:26:59 +0000136 v = sv.OpenVideo()
137 # Determine maximum window size based on signal standard
138 param = [SV.BROADCAST, 0]
139 v.GetParam(param)
140 if param[1] == SV.PAL:
141 x = SV.PAL_XMAX
142 y = SV.PAL_YMAX
143 elif param[1] == SV.NTSC:
144 x = SV.NTSC_XMAX
145 y = SV.NTSC_YMAX
146 else:
147 print 'Unknown video standard', param[1]
148 sys.exit(1)
149
Guido van Rossum180924d1992-08-20 11:46:28 +0000150 gl.foreground()
Guido van Rossum32517f91992-09-04 13:26:59 +0000151 gl.maxsize(x, y)
152 gl.keepaspect(x, y)
Guido van Rossum180924d1992-08-20 11:46:28 +0000153 gl.stepunit(8, 6)
Guido van Rossum32517f91992-09-04 13:26:59 +0000154 if width:
155 gl.prefsize(width, width*3/4)
Guido van Rossum180924d1992-08-20 11:46:28 +0000156 win = gl.winopen(filename)
Guido van Rossum32517f91992-09-04 13:26:59 +0000157 if width:
158 gl.maxsize(x, y)
159 gl.keepaspect(x, y)
160 gl.stepunit(8, 6)
161 gl.winconstraints()
Guido van Rossum180924d1992-08-20 11:46:28 +0000162 x, y = gl.getsize()
163 print x, 'x', y
164
Guido van Rossum180924d1992-08-20 11:46:28 +0000165 v.SetSize(x, y)
Jack Jansen3b253711992-12-14 12:25:21 +0000166
167 if drop:
168 param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
169 else:
170 param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
171 if mono or grey:
172 param = param+[SV.COLOR, SV.MONO, SV.INPUT_BYPASS, 1]
173 else:
174 param = param+[SV.COLOR, SV.DEFAULT_COLOR, SV.INPUT_BYPASS, 0]
175 v.SetParam(param)
Guido van Rossumff3da051992-12-09 22:16:35 +0000176
Guido van Rossum180924d1992-08-20 11:46:28 +0000177 v.BindGLWindow(win, SV.IN_REPLACE)
178
Guido van Rossum180924d1992-08-20 11:46:28 +0000179 gl.qdevice(DEVICE.LEFTMOUSE)
180 gl.qdevice(DEVICE.WINQUIT)
181 gl.qdevice(DEVICE.WINSHUT)
182 gl.qdevice(DEVICE.ESCKEY)
183
184 print 'Press left mouse to start recording, release it to stop'
185
Guido van Rossum180924d1992-08-20 11:46:28 +0000186 while 1:
187 dev, val = gl.qread()
188 if dev == DEVICE.LEFTMOUSE:
189 if val == 1:
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000190 info = format, x, y, qsize, rate
Jack Jansen3b253711992-12-14 12:25:21 +0000191 record(v, info, filename, audiofilename,\
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000192 mono, grey, greybits, monotreshold, \
193 fields, preallocspace)
Guido van Rossum180924d1992-08-20 11:46:28 +0000194 elif dev == DEVICE.REDRAW:
195 # Window resize (or move)
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000196 x, y = gl.getsize()
197 print x, 'x', y
198 v.SetSize(x, y)
199 v.BindGLWindow(win, SV.IN_REPLACE)
Guido van Rossum180924d1992-08-20 11:46:28 +0000200 elif dev in (DEVICE.ESCKEY, DEVICE.WINQUIT, DEVICE.WINSHUT):
201 # Quit
Guido van Rossum180924d1992-08-20 11:46:28 +0000202 v.CloseVideo()
203 gl.winclose(win)
204 break
205
206
207# Record until the mouse is released (or any other GL event)
208# XXX audio not yet supported
209
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000210def record(v, info, filename, audiofilename, mono, grey, greybits, \
211 monotreshold, fields, preallocspace):
Guido van Rossum180924d1992-08-20 11:46:28 +0000212 import thread
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000213 format, x, y, qsize, rate = info
214 fps = 59.64 # Fields per second
215 # XXX (Strange: need fps of Indigo monitor, not of PAL or NTSC!)
216 tpf = 1000.0 / fps # Time per field in msec
Jack Jansen3b253711992-12-14 12:25:21 +0000217 if filename:
218 vout = VFile.VoutFile().init(filename)
219 if mono:
220 vout.format = 'mono'
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000221 elif grey and greybits == 8:
Jack Jansen3b253711992-12-14 12:25:21 +0000222 vout.format = 'grey'
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000223 elif grey:
224 vout.format = 'grey'+`abs(greybits)`
Jack Jansen3b253711992-12-14 12:25:21 +0000225 else:
226 vout.format = 'rgb8'
227 vout.width = x
228 vout.height = y
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000229 if fields:
230 vout.packfactor = (1,-2)
Jack Jansen3b253711992-12-14 12:25:21 +0000231 vout.writeheader()
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000232 if preallocspace:
233 print 'Preallocating space...'
234 vout.prealloc(preallocspace)
235 print 'done.'
Jack Jansen3b253711992-12-14 12:25:21 +0000236 MAXSIZE = 20 # XXX should be a user option
237 import Queue
238 queue = Queue.Queue().init(MAXSIZE)
239 done = thread.allocate_lock()
240 done.acquire_lock()
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000241 convertor = None
242 if grey:
243 if greybits == 2:
244 convertor = imageop.grey2grey2
245 elif greybits == 4:
246 convertor = imageop.grey2grey4
247 elif greybits == -2:
248 convertor = imageop.dither2grey2
Jack Jansen3b253711992-12-14 12:25:21 +0000249 thread.start_new_thread(saveframes, \
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000250 (vout, queue, done, mono, monotreshold, convertor))
Jack Jansen3b253711992-12-14 12:25:21 +0000251 if audiofilename:
252 audiodone = thread.allocate_lock()
253 audiodone.acquire_lock()
254 audiostop = []
255 initaudio(audiofilename, audiostop, audiodone)
Guido van Rossum180924d1992-08-20 11:46:28 +0000256 gl.wintitle('(rec) ' + filename)
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000257 lastid = 0
Guido van Rossum180924d1992-08-20 11:46:28 +0000258 t0 = time.millitimer()
Jack Jansen3b253711992-12-14 12:25:21 +0000259 count = 0
Jack Jansen3b253711992-12-14 12:25:21 +0000260 ids = []
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000261 v.InitContinuousCapture(info)
Guido van Rossum180924d1992-08-20 11:46:28 +0000262 while not gl.qtest():
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000263 try:
264 cd, id = v.GetCaptureData()
Guido van Rossum42e07af1992-09-22 15:01:43 +0000265 except sv.error:
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000266 #time.millisleep(10) # XXX is this necessary?
267 sgi.nap(1) # XXX Try by Jack
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000268 continue
Jack Jansen3b253711992-12-14 12:25:21 +0000269 ids.append(id)
270
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000271 id = id + 2*rate
272## if id <> lastid + 2*rate:
273## print lastid, id
274 lastid = id
Jack Jansen3b253711992-12-14 12:25:21 +0000275 count = count+1
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000276 if fields:
277 data1, data2 = cd.GetFields()
278 cd.UnlockCaptureData()
279 if filename:
280 queue.put((data1, int(id*tpf)))
281 queue.put((data2, int((id+1)*tpf)))
282 else:
283 data = cd.InterleaveFields(1)
284 cd.UnlockCaptureData()
285 if filename:
286 queue.put((data, int(id*tpf)))
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000287 t1 = time.millitimer()
288 gl.wintitle('(busy) ' + filename)
289 print lastid, 'fields in', t1-t0, 'msec',
290 print '--', 0.1 * int(lastid * 10000.0 / (t1-t0)), 'fields/sec'
Guido van Rossum9533ebe1992-12-14 12:29:43 +0000291 print 'Captured',count*2, 'fields,',
292 print 0.1*int(count*20000.0/(t1-t0)), 'f/s',
293 if lastid:
294 print count*200.0/lastid, '%,',
295 print count*rate*200.0/lastid, '% of wanted rate',
296 print
Jack Jansen3b253711992-12-14 12:25:21 +0000297 print 'Ids:',
298 t0 = ids[0]
299 del ids[0]
300 for t1 in ids:
301 print t1-t0,
302 t0 = t1
303 print
304 if filename and audiofilename:
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000305 audiostop.append(None)
306 audiodone.acquire_lock()
307 v.EndContinuousCapture()
Jack Jansen3b253711992-12-14 12:25:21 +0000308 if filename:
309 queue.put(None) # Sentinel
310 done.acquire_lock()
Guido van Rossum180924d1992-08-20 11:46:28 +0000311 gl.wintitle('(done) ' + filename)
312
313
314# Thread to save the frames to the file
315
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000316def saveframes(vout, queue, done, mono, monotreshold, convertor):
Guido van Rossum180924d1992-08-20 11:46:28 +0000317 while 1:
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000318 x = queue.get()
319 if not x:
320 break
321 data, t = x
Jack Jansen6bc8c7f1992-12-23 15:37:20 +0000322 if convertor:
323 data = convertor(data, len(data), 1)
324 elif mono and monotreshold >= 0:
Jack Jansen3b253711992-12-14 12:25:21 +0000325 data = imageop.grey2mono(data, len(data), 1,\
326 monotreshold)
327 elif mono:
328 data = imageop.dither2mono(data, len(data), 1)
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000329 vout.writeframe(t, data, None)
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000330 sys.stderr.write('Done writing video\n')
Guido van Rossum180924d1992-08-20 11:46:28 +0000331 vout.close()
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000332 done.release_lock()
Guido van Rossum180924d1992-08-20 11:46:28 +0000333
334
Guido van Rossum8a861be1992-08-20 14:46:46 +0000335# Initialize audio recording
336
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000337AQSIZE = 8000 # XXX should be a user option
Guido van Rossum8a861be1992-08-20 14:46:46 +0000338
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000339def initaudio(filename, stop, done):
Guido van Rossum8a861be1992-08-20 14:46:46 +0000340 import thread, aiff
341 afile = aiff.Aiff().init(filename, 'w')
342 afile.nchannels = AL.MONO
343 afile.sampwidth = AL.SAMPLE_8
344 params = [AL.INPUT_RATE, 0]
345 al.getparams(AL.DEFAULT_DEVICE, params)
346 print 'audio sampling rate =', params[1]
347 afile.samprate = params[1]
348 c = al.newconfig()
349 c.setchannels(AL.MONO)
350 c.setqueuesize(AQSIZE)
351 c.setwidth(AL.SAMPLE_8)
352 aport = al.openport(filename, 'r', c)
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000353 thread.start_new_thread(audiorecord, (afile, aport, stop, done))
Guido van Rossum8a861be1992-08-20 14:46:46 +0000354
355
356# Thread to record audio samples
357
358# XXX should use writesampsraw for efficiency, but then destroy doesn't
359# XXX seem to set the #samples in the header correctly
360
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000361def audiorecord(afile, aport, stop, done):
362 while not stop:
Guido van Rossum8a861be1992-08-20 14:46:46 +0000363 data = aport.readsamps(AQSIZE/2)
364## afile.writesampsraw(data)
365 afile.writesamps(data)
366 del data
367 afile.destroy()
368 print 'Done writing audio'
Guido van Rossum62f6bc81992-09-03 16:56:04 +0000369 done.release_lock()
Guido van Rossum8a861be1992-08-20 14:46:46 +0000370
371
Guido van Rossum180924d1992-08-20 11:46:28 +0000372# Don't forget to call the main program
373
Guido van Rossume0be2b31992-09-01 14:45:57 +0000374try:
375 main()
376except KeyboardInterrupt:
377 print '[Interrupt]'