414 lines
9.8 KiB
Python
Executable File
414 lines
9.8 KiB
Python
Executable File
#! /usr/bin/env python
|
|
#! /ufs/guido/bin/sgi/python-405
|
|
|
|
# Capture a CMIF movie using the Indigo video library and board
|
|
|
|
# The CMIF video file format is documented in cmif-film.ms.
|
|
# Audio data is recorded in AIFF format, using the input sampling
|
|
# rate, source and volume set by the audio panel, in mono, 8
|
|
# bits/sample.
|
|
|
|
|
|
# Usage and help functions (keep this up-to-date if you change the program!)
|
|
|
|
def usage():
|
|
print 'Usage: Vrec [options] [moviefile [audiofile]]'
|
|
print
|
|
print 'Options:'
|
|
print '-a : record audio as well'
|
|
print '-q queuesize : set the capture queue size (default 2)'
|
|
print '-r rate : capture 1 out of every "rate" frames', \
|
|
'(default and min 2)'
|
|
print '-w width : initial window width', \
|
|
'(default 256, use 0 for interactive placement)'
|
|
print '-n : Don\'t write to file, only timing info'
|
|
print '-d : drop fields if needed'
|
|
print '-g bits : greyscale (2, 4 or 8 bits)'
|
|
print '-G : 2-bit greyscale dithered'
|
|
print '-m : monochrome dithered'
|
|
print '-M value : monochrome tresholded with value'
|
|
print '-f : Capture fields (in stead of frames)'
|
|
print '-P frames : preallocate space for "frames" frames'
|
|
print 'moviefile : here goes the movie data (default film.video)'
|
|
print 'audiofile : with -a, here goes the audio data', \
|
|
'(default film.aiff)'
|
|
|
|
def help():
|
|
print 'Press the left mouse button to start recording, release it to'
|
|
print 'end recording. You can record as many times as you wish, but'
|
|
print 'each recording overwrites the output file(s) -- only the last'
|
|
print 'recording is kept.'
|
|
print
|
|
print 'Press ESC or use the window manager Quit or Close window option'
|
|
print 'to quit. If you quit before recording anything, the output'
|
|
print 'file(s) are not touched.'
|
|
|
|
|
|
# Imported modules
|
|
|
|
import sys
|
|
sys.path.append('/ufs/guido/src/video')
|
|
import sv, SV
|
|
import VFile
|
|
import gl, GL, DEVICE
|
|
import al, AL
|
|
import time
|
|
import posix
|
|
import getopt
|
|
import string
|
|
import imageop
|
|
import sgi
|
|
|
|
|
|
# Main program
|
|
|
|
def main():
|
|
format = SV.RGB8_FRAMES
|
|
qsize = 2
|
|
audio = 0
|
|
rate = 2
|
|
width = 0
|
|
norecord = 0
|
|
drop = 0
|
|
mono = 0
|
|
grey = 0
|
|
greybits = 0
|
|
monotreshold = -1
|
|
fields = 0
|
|
preallocspace = 0
|
|
|
|
# Parse command line
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:], 'aq:r:w:ndg:mM:GfP:')
|
|
except getopt.error, msg:
|
|
sys.stdout = sys.stderr
|
|
print 'Error:', msg, '\n'
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
# Interpret options
|
|
try:
|
|
for opt, arg in opts:
|
|
if opt == '-a':
|
|
audio = 1
|
|
elif opt == '-q':
|
|
qsize = string.atoi(arg)
|
|
elif opt == '-r':
|
|
rate = string.atoi(arg)
|
|
if rate < 2:
|
|
sys.stderr.write( \
|
|
'-r rate must be >= 2\n')
|
|
sys.exit(2)
|
|
elif opt == '-w':
|
|
width = string.atoi(arg)
|
|
elif opt == '-n':
|
|
norecord = 1
|
|
elif opt == '-d':
|
|
drop = 1
|
|
elif opt == '-g':
|
|
grey = 1
|
|
greybits = string.atoi(arg)
|
|
if not greybits in (2, 4, 8):
|
|
sys.stderr.write( \
|
|
'Only 2, 4 or 8 bit greyscale supported\n')
|
|
sys.exit(2)
|
|
elif opt == '-G':
|
|
grey = 1
|
|
greybits = -2
|
|
elif opt == '-m':
|
|
mono = 1
|
|
elif opt == '-M':
|
|
mono = 1
|
|
monotreshold = string.atoi(arg)
|
|
elif opt == '-f':
|
|
fields = 1
|
|
elif opt == '-P':
|
|
preallocspace = string.atoi(arg)
|
|
except string.atoi_error:
|
|
sys.stdout = sys.stderr
|
|
print 'Option', opt, 'requires integer argument'
|
|
sys.exit(2)
|
|
|
|
# Check excess arguments
|
|
# If norecord is on, refuse filename arguments
|
|
if norecord:
|
|
if args:
|
|
sys.stdout = sys.stderr
|
|
print 'With -n, no filename arguments are used\n'
|
|
usage()
|
|
sys.exit(2)
|
|
elif args[2:]:
|
|
sys.stdout = sys.stderr
|
|
print 'Too many filename arguments\n'
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
# Process file arguments
|
|
if args:
|
|
filename = args[0]
|
|
else:
|
|
filename = 'film.video'
|
|
|
|
if args[1:] and not audio:
|
|
sys.stderr.write('-a turned on by appearance of 2nd file\n')
|
|
audio = 1
|
|
|
|
if audio:
|
|
if args[1:]:
|
|
audiofilename = args[1]
|
|
else:
|
|
audiofilename = 'film.aiff'
|
|
else:
|
|
audiofilename = None
|
|
|
|
if norecord:
|
|
filename = audiofilename = ''
|
|
|
|
# Open video
|
|
v = sv.OpenVideo()
|
|
# Determine maximum window size based on signal standard
|
|
param = [SV.BROADCAST, 0]
|
|
v.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)
|
|
|
|
gl.foreground()
|
|
gl.maxsize(x, y)
|
|
gl.keepaspect(x, y)
|
|
gl.stepunit(8, 6)
|
|
if width:
|
|
height = width*3/4
|
|
x1 = 150
|
|
x2 = x1 + width-1
|
|
y2 = 768-150
|
|
y1 = y2-height+1
|
|
gl.prefposition(x1, x2, y1, y2)
|
|
win = gl.winopen(filename)
|
|
if width:
|
|
gl.maxsize(x, y)
|
|
gl.keepaspect(x, y)
|
|
gl.stepunit(8, 6)
|
|
gl.winconstraints()
|
|
x, y = gl.getsize()
|
|
print x, 'x', y
|
|
|
|
v.SetSize(x, y)
|
|
|
|
if drop:
|
|
param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
|
|
else:
|
|
param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
|
|
if mono or grey:
|
|
param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
|
|
SV.INPUT_BYPASS, 1]
|
|
else:
|
|
param = param+[SV.COLOR, SV.DEFAULT_COLOR, SV.INPUT_BYPASS, 0]
|
|
|
|
v.BindGLWindow(win, SV.IN_REPLACE)
|
|
v.SetParam(param)
|
|
|
|
gl.qdevice(DEVICE.LEFTMOUSE)
|
|
gl.qdevice(DEVICE.WINQUIT)
|
|
gl.qdevice(DEVICE.WINSHUT)
|
|
gl.qdevice(DEVICE.ESCKEY)
|
|
|
|
help()
|
|
|
|
while 1:
|
|
dev, val = gl.qread()
|
|
if dev == DEVICE.LEFTMOUSE:
|
|
if val == 1:
|
|
info = format, x, y, qsize, rate
|
|
record(v, info, filename, audiofilename,\
|
|
mono, grey, greybits, monotreshold, \
|
|
fields, preallocspace)
|
|
elif dev == DEVICE.REDRAW:
|
|
# Window resize (or move)
|
|
x, y = gl.getsize()
|
|
print x, 'x', y
|
|
v.SetSize(x, y)
|
|
v.BindGLWindow(win, SV.IN_REPLACE)
|
|
elif dev in (DEVICE.ESCKEY, DEVICE.WINQUIT, DEVICE.WINSHUT):
|
|
# Quit
|
|
v.CloseVideo()
|
|
gl.winclose(win)
|
|
break
|
|
|
|
|
|
# Record until the mouse is released (or any other GL event)
|
|
# XXX audio not yet supported
|
|
|
|
def record(v, info, filename, audiofilename, mono, grey, greybits, \
|
|
monotreshold, fields, preallocspace):
|
|
import thread
|
|
format, x, y, qsize, rate = info
|
|
fps = 59.64 # Fields per second
|
|
# XXX (Strange: need fps of Indigo monitor, not of PAL or NTSC!)
|
|
tpf = 1000.0 / fps # Time per field in msec
|
|
if filename:
|
|
vout = VFile.VoutFile(filename)
|
|
if mono:
|
|
format = 'mono'
|
|
elif grey and greybits == 8:
|
|
format = 'grey'
|
|
elif grey:
|
|
format = 'grey'+`abs(greybits)`
|
|
else:
|
|
format = 'rgb8'
|
|
vout.setformat(format)
|
|
vout.setsize(x, y)
|
|
if fields:
|
|
vout.setpf((1, -2))
|
|
vout.writeheader()
|
|
if preallocspace:
|
|
print 'Preallocating space...'
|
|
vout.prealloc(preallocspace)
|
|
print 'done.'
|
|
MAXSIZE = 20 # XXX should be a user option
|
|
import Queue
|
|
queue = Queue.Queue(MAXSIZE)
|
|
done = thread.allocate_lock()
|
|
done.acquire_lock()
|
|
convertor = None
|
|
if grey:
|
|
if greybits == 2:
|
|
convertor = imageop.grey2grey2
|
|
elif greybits == 4:
|
|
convertor = imageop.grey2grey4
|
|
elif greybits == -2:
|
|
convertor = imageop.dither2grey2
|
|
thread.start_new_thread(saveframes, \
|
|
(vout, queue, done, mono, monotreshold, convertor))
|
|
if audiofilename:
|
|
audiodone = thread.allocate_lock()
|
|
audiodone.acquire_lock()
|
|
audiostop = []
|
|
initaudio(audiofilename, audiostop, audiodone)
|
|
gl.wintitle('(rec) ' + filename)
|
|
lastid = 0
|
|
t0 = time.time()
|
|
count = 0
|
|
ids = []
|
|
v.InitContinuousCapture(info)
|
|
while not gl.qtest():
|
|
try:
|
|
cd, id = v.GetCaptureData()
|
|
except sv.error:
|
|
#time.sleep(0.010) # XXX is this necessary?
|
|
sgi.nap(1) # XXX Try by Jack
|
|
continue
|
|
ids.append(id)
|
|
|
|
id = id + 2*rate
|
|
## if id <> lastid + 2*rate:
|
|
## print lastid, id
|
|
lastid = id
|
|
count = count+1
|
|
if fields:
|
|
data1, data2 = cd.GetFields()
|
|
cd.UnlockCaptureData()
|
|
if filename:
|
|
queue.put((data1, int(id*tpf)))
|
|
queue.put((data2, int((id+1)*tpf)))
|
|
else:
|
|
data = cd.InterleaveFields(1)
|
|
cd.UnlockCaptureData()
|
|
if filename:
|
|
queue.put((data, int(id*tpf)))
|
|
t1 = time.time()
|
|
gl.wintitle('(busy) ' + filename)
|
|
print lastid, 'fields in', round(t1-t0, 3), 'sec',
|
|
print '--', round(lastid/(t1-t0), 1), 'fields/sec'
|
|
print 'Captured',count*2, 'fields,',
|
|
print round(count*2/(t1-t0), 1), 'f/s',
|
|
if lastid:
|
|
print '(',
|
|
print round(count*200.0/lastid), '%, or',
|
|
print round(count*rate*200.0/lastid), '% of wanted rate )',
|
|
print
|
|
if ids:
|
|
print 'Ids:',
|
|
t0 = ids[0]
|
|
del ids[0]
|
|
for t1 in ids:
|
|
print t1-t0,
|
|
t0 = t1
|
|
print
|
|
if filename and audiofilename:
|
|
audiostop.append(None)
|
|
audiodone.acquire_lock()
|
|
v.EndContinuousCapture()
|
|
if filename:
|
|
queue.put(None) # Sentinel
|
|
done.acquire_lock()
|
|
gl.wintitle('(done) ' + filename)
|
|
|
|
|
|
# Thread to save the frames to the file
|
|
|
|
def saveframes(vout, queue, done, mono, monotreshold, convertor):
|
|
while 1:
|
|
x = queue.get()
|
|
if not x:
|
|
break
|
|
data, t = x
|
|
if convertor:
|
|
data = convertor(data, len(data), 1)
|
|
elif mono and monotreshold >= 0:
|
|
data = imageop.grey2mono(data, len(data), 1,\
|
|
monotreshold)
|
|
elif mono:
|
|
data = imageop.dither2mono(data, len(data), 1)
|
|
vout.writeframe(t, data, None)
|
|
sys.stderr.write('Done writing video\n')
|
|
vout.close()
|
|
done.release_lock()
|
|
|
|
|
|
# Initialize audio recording
|
|
|
|
AQSIZE = 8000 # XXX should be a user option
|
|
|
|
def initaudio(filename, stop, done):
|
|
import thread, aifc
|
|
afile = aifc.open(filename, 'w')
|
|
afile.setnchannels(AL.MONO)
|
|
afile.setsampwidth(AL.SAMPLE_8)
|
|
params = [AL.INPUT_RATE, 0]
|
|
al.getparams(AL.DEFAULT_DEVICE, params)
|
|
print 'audio sampling rate =', params[1]
|
|
afile.setframerate(params[1])
|
|
c = al.newconfig()
|
|
c.setchannels(AL.MONO)
|
|
c.setqueuesize(AQSIZE)
|
|
c.setwidth(AL.SAMPLE_8)
|
|
aport = al.openport(filename, 'r', c)
|
|
thread.start_new_thread(audiorecord, (afile, aport, stop, done))
|
|
|
|
|
|
# Thread to record audio samples
|
|
|
|
def audiorecord(afile, aport, stop, done):
|
|
while not stop:
|
|
data = aport.readsamps(AQSIZE/2)
|
|
afile.writesampsraw(data)
|
|
del data
|
|
afile.close()
|
|
print 'Done writing audio'
|
|
done.release_lock()
|
|
|
|
|
|
# Don't forget to call the main program
|
|
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print '[Interrupt]'
|