blob: 2a465712bad1caff024823eae7498bf0b03824e4 [file] [log] [blame]
Guido van Rossumb7e3cc11993-05-06 16:06:44 +00001#! /ufs/guido/bin/sgi/python
2
3# Video bag-of-tricks
4
Guido van Rossumbc6d3c31993-05-07 09:37:42 +00005# XXX To do: audio; rationalize user interface; ...?
6
Guido van Rossumb7e3cc11993-05-06 16:06:44 +00007import sys
8import getopt
9import string
10import os
11sts = os.system('makemap') # Must be before "import fl"
12import sgi
13import gl
14import GL
15import DEVICE
16import fl
17import FL
18import flp
19import watchcursor
20import sv
21import SV
22import VFile
23import VGrabber
24import imageop
25
26ARROW = 0
27WATCH = 1
28watchcursor.defwatch(WATCH)
29
30def main():
31 vb = VideoBagOfTricks().init()
32 while 1:
33 dummy = fl.do_forms()
34 [dummy]
35
36StopCapture = 'StopCapture'
37
Guido van Rossumbc6d3c31993-05-07 09:37:42 +000038Labels = ['rgb8', 'grey8', 'grey4', 'grey2', \
39 'grey2 dith', 'mono dith', 'mono thresh']
40Formats = ['rgb8', 'grey', 'grey4', 'grey2', \
41 'grey2', 'mono', 'mono']
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000042
43class VideoBagOfTricks:
44
45 def init(self):
46 formdef = flp.parse_form('VbForm', 'form')
47 flp.create_full_form(self, formdef)
Guido van Rossume17c6c31993-05-06 16:27:25 +000048 self.g_stop.hide_object()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000049 self.setdefaults()
50 self.openvideo()
51 self.makewindow()
52 self.bindvideo()
53 self.capturing = 0
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000054 self.form.show_form(FL.PLACE_SIZE, FL.TRUE, \
55 'Video Bag Of Tricks')
56 fl.set_event_call_back(self.do_event)
57 return self
58
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000059 def setdefaults(self):
Guido van Rossumbc6d3c31993-05-07 09:37:42 +000060 self.mono_thresh = 128
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000061 self.format = 'rgb8'
62 self.c_format.clear_choice()
Guido van Rossumbc6d3c31993-05-07 09:37:42 +000063 for label in Labels:
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000064 self.c_format.addto_choice(label)
Guido van Rossumbc6d3c31993-05-07 09:37:42 +000065 self.get_format()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000066 self.b_drop.set_button(1)
67 self.b_burst.set_button(0)
68 self.in_rate.set_input('2')
69 self.in_maxmem.set_input('1.0')
70 self.in_nframes.set_input('0')
71 self.in_file.set_input('film.video')
72
73 def openvideo(self):
Guido van Rossumbc6d3c31993-05-07 09:37:42 +000074 try:
75 self.video = sv.OpenVideo()
76 except sv.error, msg:
77 print 'Error opening video:', msg
78 self.video = None
79 #sys.exit(1)
80 param = [SV.BROADCAST, SV.PAL]
81 if self.video: self.video.GetParam(param)
Guido van Rossumb7e3cc11993-05-06 16:06:44 +000082 if param[1] == SV.PAL:
83 x = SV.PAL_XMAX
84 y = SV.PAL_YMAX
85 elif param[1] == SV.NTSC:
86 x = SV.NTSC_XMAX
87 y = SV.NTSC_YMAX
88 else:
89 print 'Unknown video standard:', param[1]
90 sys.exit(1)
91 self.maxx, self.maxy = x, y
92
93 def makewindow(self):
94 x, y = self.maxx, self.maxy
95 gl.foreground()
96 gl.maxsize(x, y)
97 gl.keepaspect(x, y)
98 gl.stepunit(8, 6)
99 width = 256 # XXX
100 if width:
101 height = width*3/4
102 x1 = 150
103 x2 = x1 + width-1
104 y2 = 768-150
105 y1 = y2-height+1
106 gl.prefposition(x1, x2, y1, y2)
107 self.window = gl.winopen('Vb: initializing')
108 self.settitle()
109 if width:
110 gl.maxsize(x, y)
111 gl.keepaspect(x, y)
112 gl.stepunit(8, 6)
113 gl.winconstraints()
114 gl.qdevice(DEVICE.LEFTMOUSE)
115 gl.qdevice(DEVICE.WINQUIT)
116 gl.qdevice(DEVICE.WINSHUT)
117
118 def settitle(self):
119 gl.winset(self.window)
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000120 x, y = gl.getsize()
121 title = 'Vb:' + self.in_file.get_input() + ' (%dx%d)' % (x, y)
122 gl.wintitle(title)
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000123
124 def bindvideo(self):
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000125 if not self.video: return
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000126 x, y = gl.getsize()
127 self.video.SetSize(x, y)
128 drop = self.b_drop.get_button()
129 if drop:
130 param = [SV.FIELDDROP, 1, SV.GENLOCK, SV.GENLOCK_OFF]
131 else:
132 param = [SV.FIELDDROP, 0, SV.GENLOCK, SV.GENLOCK_ON]
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000133 if self.rgb:
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000134 param = param+[SV.COLOR, SV.DEFAULT_COLOR, \
135 SV.DITHER, 1, \
136 SV.INPUT_BYPASS, 0]
137 else:
138 param = param+[SV.COLOR, SV.MONO, SV.DITHER, 0, \
139 SV.INPUT_BYPASS, 1]
140 self.video.BindGLWindow(self.window, SV.IN_REPLACE)
141 self.video.SetParam(param)
142
143 def rebindvideo(self):
144 gl.winset(self.window)
145 self.bindvideo()
146
147 def do_event(self, dev, val):
148 #print 'Event:', dev, val
149 if dev in (DEVICE.WINSHUT, DEVICE.WINQUIT):
150 self.cb_quit()
151 if dev == DEVICE.REDRAW and val == self.window:
152 self.rebindvideo()
153 self.settitle()
154
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000155 def get_format(self):
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000156 i = self.c_format.get_choice()
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000157 label = Labels[i-1]
158 format = Formats[i-1]
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000159 self.format = format
160 #
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000161 self.rgb = (format[:3] == 'rgb')
162 self.mono = (format == 'mono')
163 self.grey = (format[:4] == 'grey')
164 self.mono_use_thresh = (label == 'mono thresh')
165 s = format[4:]
166 if s:
167 self.greybits = string.atoi(s)
168 else:
169 self.greybits = 8
170 if label == 'grey2 dith':
171 self.greybits = -2
172 #
173 convertor = None
174 if self.grey:
175 if self.greybits == 2:
176 convertor = imageop.grey2grey2
177 elif self.greybits == 4:
178 convertor = imageop.grey2grey4
179 elif self.greybits == -2:
180 convertor = imageop.dither2grey2
181 self.convertor = convertor
182
183 def cb_format(self, *args):
184 self.get_format()
185 if self.mono_use_thresh:
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000186 s = `self.mono_thresh`
187 s = fl.show_input('Please enter mono threshold', s)
188 if s:
189 try:
190 self.mono_thresh = string.atoi(s)
191 except string.atoi_error:
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000192 fl.show_message('Bad input, using', \
193 `self.mono_thresh`, '')
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000194 self.rebindvideo()
195
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000196 def cb_rate(self, *args):
197 pass
198
199 def cb_drop(self, *args):
200 self.rebindvideo()
201
202 def cb_burst(self, *args):
203 pass
204
205 def cb_maxmem(self, *args):
206 pass
207
208 def cb_nframes(self, *args):
209 pass
210
211 def cb_file(self, *args):
212 filename = self.in_file.get_input()
213 if filename == '':
214 filename = 'film.video'
215 self.in_file.set_input(filename)
216 self.settitle()
217
218 def cb_open(self, *args):
219 filename = self.in_file.get_input()
220 hd, tl = os.path.split(filename)
221 filename = fl.file_selector('Select file:', hd, '', tl)
222 if filename:
223 hd, tl = os.path.split(filename)
224 if hd == os.getcwd():
225 filename = tl
226 self.in_file.set_input(filename)
227 self.cb_file()
228
229 def cb_play(self, *args):
230 filename = self.in_file.get_input()
231 sts = os.system('Vplay -q ' + filename + ' &')
232
233 def cb_stop(self, *args):
234 if self.capturing:
235 raise StopCapture
236 gl.ringbell()
237
238 def cb_capture(self, *args):
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000239 if not self.video:
240 gl.ringbell()
241 return
242 if self.b_burst.get_button():
243 self.burst_capture()
244 else:
245 self.cont_capture()
246
247 def burst_capture(self):
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000248 self.setwatch()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000249 gl.winset(self.window)
250 x, y = gl.getsize()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000251 vformat = SV.RGB8_FRAMES
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000252 try:
253 nframes = string.atoi(self.in_nframes.get_input())
254 except string.atoi_error:
255 nframes = 0
256 if nframes == 0:
257 try:
258 maxmem = \
259 float(eval(self.in_maxmem.get_input()))
260 except:
261 maxmem = 1.0
262 memsize = int(maxmem * 1024 * 1024)
263 nframes = calcnframes(x, y, \
264 self.mono or self.grey, memsize)
265 print 'nframes =', nframes
266 rate = string.atoi(self.in_rate.get_input())
267 # XXX Should check ranges and not crash if non-integer
268 info = (vformat, x, y, nframes, rate)
269 try:
270 info2, data, bitvec = self.video.CaptureBurst(info)
271 except sv.error, msg:
272 fl.show_message('Capture error:', str(msg), '')
273 self.setarrow()
274 return
275 print info2
276 self.save_burst(info2, data, bitvec)
277 self.setarrow()
278
279 def save_burst(self, info, data, bitvec):
280 (vformat, x, y, nframes, rate) = info
281 self.open_file()
282 fieldsize = x*y/2
283 nskipped = 0
284 realframeno = 0
285 tpf = 1000 / 50.0 #XXXX
286 # Trying to find the pattern in frame skipping
287 okstretch = 0
288 skipstretch = 0
289 for frameno in range(0, nframes*2):
290 if frameno <> 0 and \
291 bitvec[frameno] == bitvec[frameno-1]:
292 nskipped = nskipped + 1
293 if okstretch:
294 #print okstretch, 'ok',
295 okstretch = 0
296 skipstretch = skipstretch + 1
297 continue
298 if skipstretch:
299 #print skipstretch, 'skipped'
300 skipstretch = 0
301 okstretch = okstretch + 1
302 #
303 # Save field.
304 # XXXX Works only for fields and top-to-bottom
305 #
306 start = frameno*fieldsize
307 field = data[start:start+fieldsize]
308 realframeno = realframeno + 1
309 fn = int(realframeno*tpf)
310 if not self.write_frame(fn, field):
311 break
312 #print okstretch, 'ok',
313 #print skipstretch, 'skipped'
314 #print 'Skipped', nskipped, 'duplicate frames'
315 self.close_file()
316
317 def cont_capture(self):
318 self.setwatch()
319 self.g_main.hide_object()
320 self.open_file()
321 vformat = SV.RGB8_FRAMES
322 qsize = 1 # XXX Should be an option?
323 try:
324 rate = string.atoi(self.in_rate.get_input())
325 except string.atoi_error:
326 rate = 2
327 x, y = self.vout.getsize()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000328 info = (vformat, x, y, qsize, rate)
329 ids = []
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000330 fps = 59.64 # Fields per second
331 # XXX (fps of Indigo monitor, not of PAL or NTSC!)
332 tpf = 1000.0 / fps # Time per field in msec
333 info2 = self.video.InitContinuousCapture(info)
334 if info2 <> info:
335 print 'Info mismatch: requested', info, 'got', info2
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000336 self.capturing = 1
Guido van Rossume17c6c31993-05-06 16:27:25 +0000337 self.g_stop.show_object()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000338 self.setarrow()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000339 while 1:
340 try:
341 void = fl.check_forms()
342 except StopCapture:
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000343 break
344 try:
345 cd, id = self.video.GetCaptureData()
346 except sv.error:
347 sgi.nap(1)
348 continue
349 ids.append(id)
350 id = id + 2*rate
351 data = cd.InterleaveFields(1)
352 cd.UnlockCaptureData()
353 t = id*tpf
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000354 if not self.write_frame(t, data):
Guido van Rossume17c6c31993-05-06 16:27:25 +0000355 break
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000356 self.setwatch()
Guido van Rossume17c6c31993-05-06 16:27:25 +0000357 self.g_stop.hide_object()
358 self.capturing = 0
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000359 self.video.EndContinuousCapture()
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000360 self.close_file()
Guido van Rossume17c6c31993-05-06 16:27:25 +0000361 self.g_main.show_object()
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000362 self.setarrow()
363
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000364 def open_file(self):
365 gl.winset(self.window)
366 x, y = gl.getsize()
367 self.cb_file() # Make sure filename is OK
368 filename = self.in_file.get_input()
369 vout = VFile.VoutFile().init(filename)
370 vout.setformat(self.format)
371 vout.setsize(x, y)
372 if self.b_burst.get_button():
373 vout.setpf((1, -2))
374 vout.writeheader()
375 self.vout = vout
376
377 def close_file(self):
378 try:
379 self.vout.close()
380 except IOError, msg:
381 if msg == (0, 'Error 0'):
382 msg = 'disk full??'
383 fl.show_message('IOError', str(msg), '')
384 del self.vout
385
386 def write_frame(self, t, data):
387 if self.convertor:
388 data = self.convertor(data, len(data), 1)
389 elif self.mono:
390 if self.mono_use_thresh:
391 data = imageop.grey2mono(data, \
392 len(data), 1,\
393 self.mono_thresh)
394 else:
395 data = imageop.dither2mono(data, \
396 len(data), 1)
397 try:
398 self.vout.writeframe(t, data, None)
399 except IOError, msg:
400 if msg == (0, 'Error 0'):
401 msg = 'disk full??'
402 fl.show_message('IOError', str(msg), '')
403 return 0
404 return 1
405
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000406 def cb_quit(self, *args):
407 raise SystemExit, 0
408
Guido van Rossumbc6d3c31993-05-07 09:37:42 +0000409 def setwatch(self):
410 gl.winset(self.form.window)
411 gl.setcursor(WATCH, 0, 0)
412
413 def setarrow(self):
414 gl.winset(self.form.window)
415 gl.setcursor(ARROW, 0, 0)
416
417def calcnframes(x, y, grey, memsize):
418 pixels = x*y
419 pixels = pixels/2 # XXX always assume fields
420 if grey: n = memsize/pixels
421 else: n = memsize/(4*pixels)
422 return max(1, n)
Guido van Rossumb7e3cc11993-05-06 16:06:44 +0000423
424try:
425 main()
426except KeyboardInterrupt:
427 print '[Interrupt]'
428 sys.exit(1)