blob: 8fa87b0d95a169de0737473d93a4cf1e7711f0b4 [file] [log] [blame]
Guido van Rossum605b1271993-05-12 12:35:44 +00001import fcntl
2import IOCTL
3from IOCTL import *
4import sys
5import struct
6import select
Guido van Rossumfa041701993-06-10 13:32:32 +00007import posix
Jack Jansenf1cda911993-07-19 16:13:33 +00008import time
Guido van Rossum605b1271993-05-12 12:35:44 +00009
10DEVICE='/dev/ttyd2'
11
Guido van Rossumfa041701993-06-10 13:32:32 +000012class UnixFile:
13 def open(self, name, mode):
14 self.fd = posix.open(name, mode)
15 return self
16
17 def read(self, len):
18 return posix.read(self.fd, len)
19
20 def write(self, data):
21 dummy = posix.write(self.fd, data)
22
23 def flush(self):
24 pass
25
26 def fileno(self):
27 return self.fd
28
29 def close(self):
30 dummy = posix.close(self.fd)
31
Guido van Rossum605b1271993-05-12 12:35:44 +000032def packttyargs(*args):
33 if type(args) <> type(()):
34 raise 'Incorrect argtype for packttyargs'
35 if type(args[0]) == type(1):
36 iflag, oflag, cflag, lflag, line, chars = args
37 elif type(args[0]) == type(()):
38 if len(args) <> 1:
39 raise 'Only 1 argument expected'
40 iflag, oflag, cflag, lflag, line, chars = args[0]
41 elif type(args[0]) == type([]):
42 if len(args) <> 1:
43 raise 'Only 1 argument expected'
44 [iflag, oflag, cflag, lflag, line, chars] = args[0]
45 str = struct.pack('hhhhb', iflag, oflag, cflag, lflag, line)
46 for c in chars:
47 str = str + c
48 return str
49
50def nullttyargs():
51 chars = ['\0']*IOCTL.NCCS
52 return packttyargs(0, 0, 0, 0, 0, chars)
53
54def unpackttyargs(str):
55 args = str[:-IOCTL.NCCS]
56 rawchars = str[-IOCTL.NCCS:]
57 chars = []
58 for c in rawchars:
59 chars.append(c)
60 iflag, oflag, cflag, lflag, line = struct.unpack('hhhhb', args)
61 return (iflag, oflag, cflag, lflag, line, chars)
62
63def initline(name):
Guido van Rossumfa041701993-06-10 13:32:32 +000064 fp = UnixFile().open(name, 2)
65 ofp = fp
Guido van Rossum605b1271993-05-12 12:35:44 +000066 fd = fp.fileno()
67 rv = fcntl.ioctl(fd, IOCTL.TCGETA, nullttyargs())
68 iflag, oflag, cflag, lflag, line, chars = unpackttyargs(rv)
69 iflag = iflag & ~(INPCK|ISTRIP|INLCR|IUCLC|IXON|IXOFF)
70 oflag = oflag & ~OPOST
71 cflag = B9600|CS8|CREAD|CLOCAL
72 lflag = lflag & ~(ISIG|ICANON|ECHO|TOSTOP)
73 chars[VMIN] = chr(1)
74 chars[VTIME] = chr(0)
75 arg = packttyargs(iflag, oflag, cflag, lflag, line, chars)
76 dummy = fcntl.ioctl(fd, IOCTL.TCSETA, arg)
77 return fp, ofp
78
Jack Jansenf1cda911993-07-19 16:13:33 +000079#
80#
Guido van Rossum605b1271993-05-12 12:35:44 +000081error = 'VCR.error'
82
83# Commands/replies:
84COMPLETION = '\x01'
85ACK ='\x0a'
86NAK ='\x0b'
87
88NUMBER_N = 0x30
89ENTER = '\x40'
90
91EXP_7= '\xde'
92EXP_8= '\xdf'
93
94CL ='\x56'
95CTRL_ENABLE = EXP_8 + '\xc6'
96SEARCH_DATA = EXP_8 + '\x93'
97ADDR_SENSE = '\x60'
98
99PLAY ='\x3a'
100STOP ='\x3f'
101EJECT='\x2a'
102FF ='\xab'
103REW ='\xac'
104STILL='\x4f'
Guido van Rossumfa041701993-06-10 13:32:32 +0000105STEP_FWD ='\x2b' # Was: '\xad'
Guido van Rossum605b1271993-05-12 12:35:44 +0000106FM_SELECT=EXP_8 + '\xc8'
107FM_STILL=EXP_8 + '\xcd'
Jack Jansenf1cda911993-07-19 16:13:33 +0000108PREVIEW=EXP_7 + '\x9d'
109REVIEW=EXP_7 + '\x9e'
Guido van Rossum605b1271993-05-12 12:35:44 +0000110DM_OFF=EXP_8 + '\xc9'
111DM_SET=EXP_8 + '\xc4'
112FWD_SHUTTLE='\xb5'
113REV_SHUTTLE='\xb6'
Guido van Rossumfa041701993-06-10 13:32:32 +0000114EM_SELECT=EXP_8 + '\xc0'
115N_FRAME_REC=EXP_8 + '\x92'
Jack Jansenf1cda911993-07-19 16:13:33 +0000116SEARCH_PREROLL=EXP_8 + '\x90'
117EDIT_PB_STANDBY=EXP_8 + '\x96'
118EDIT_PLAY=EXP_8 + '\x98'
Jack Jansenbab2ca31993-09-28 15:28:10 +0000119AUTO_EDIT=EXP_7 + '\x9c'
Guido van Rossumfa041701993-06-10 13:32:32 +0000120
121IN_ENTRY=EXP_7 + '\x90'
122IN_ENTRY_RESET=EXP_7 + '\x91'
123IN_ENTRY_SET=EXP_7 + '\x98'
124IN_ENTRY_INC=EXP_7 + '\x94'
125IN_ENTRY_DEC=EXP_7 + '\x95'
126IN_ENTRY_SENSE=EXP_7 + '\x9a'
127
128OUT_ENTRY=EXP_7 + '\x92'
129OUT_ENTRY_RESET=EXP_7 + '\x93'
130OUT_ENTRY_SET=EXP_7 + '\x99'
131OUT_ENTRY_INC=EXP_7 + '\x96'
132OUT_ENTRY_DEC=EXP_7 + '\x98'
133OUT_ENTRY_SENSE=EXP_7 + '\x9b'
134
Jack Jansenf1cda911993-07-19 16:13:33 +0000135MUTE_AUDIO = '\x24'
136MUTE_AUDIO_OFF = '\x25'
137MUTE_VIDEO = '\x26'
138MUTE_VIDEO_OFF = '\x27'
139MUTE_AV = EXP_7 + '\xc6'
140MUTE_AV_OFF = EXP_7 + '\xc7'
141
Guido van Rossumfa041701993-06-10 13:32:32 +0000142DEBUG=0
Guido van Rossum605b1271993-05-12 12:35:44 +0000143
Jack Jansena1e1f731993-06-08 12:47:06 +0000144class VCR:
Guido van Rossum605b1271993-05-12 12:35:44 +0000145 def init(self):
146 self.ifp, self.ofp = initline(DEVICE)
Jack Jansenf1cda911993-07-19 16:13:33 +0000147 self.busy_cmd = None
148 self.async = 0
149 self.cb = None
150 self.cb_arg = None
Guido van Rossum605b1271993-05-12 12:35:44 +0000151 return self
152
Jack Jansenf1cda911993-07-19 16:13:33 +0000153 def _check(self):
154 if self.busy_cmd:
155 raise error, 'Another command active: '+self.busy_cmd
156
157 def _endlongcmd(self, name):
158 if not self.async:
159 self.waitready()
160 return 1
161 self.busy_cmd = name
162 return 'VCR BUSY'
163
164 def fileno(self):
165 return self.ifp.fileno()
166
167 def setasync(self, async):
168 self.async = async
169
170 def setcallback(self, cb, arg):
171 self.setasync(1)
172 self.cb = cb
173 self.cb_arg = arg
174
175 def poll(self):
176 if not self.async:
177 raise error, 'Can only call poll() in async mode'
178 if not self.busy_cmd:
179 return
180 if self.testready():
181 if self.cb:
182 apply(self.cb, self.cb_arg)
183
Guido van Rossum605b1271993-05-12 12:35:44 +0000184 def _cmd(self, cmd):
Guido van Rossumfa041701993-06-10 13:32:32 +0000185 if DEBUG:
186 print '>>>',`cmd`
Guido van Rossum605b1271993-05-12 12:35:44 +0000187 self.ofp.write(cmd)
188 self.ofp.flush()
189
190 def _waitdata(self, len, tout):
191 rep = ''
192 while len > 0:
Jack Jansenf1cda911993-07-19 16:13:33 +0000193 if tout == None:
194 ready, d1, d2 = select.select( \
195 [self.ifp], [], [])
196 else:
197 ready, d1, d2 = select.select( \
198 [self.ifp], [], [], tout)
Guido van Rossum605b1271993-05-12 12:35:44 +0000199 if ready == []:
200## if rep:
201## print 'FLUSHED:', `rep`
202 return None
203 data = self.ifp.read(1)
Guido van Rossumfa041701993-06-10 13:32:32 +0000204 if DEBUG:
205 print '<<<',`data`
Guido van Rossum605b1271993-05-12 12:35:44 +0000206 if data == NAK:
207 return NAK
208 rep = rep + data
209 len = len - 1
210 return rep
211
212 def _reply(self, len):
213 data = self._waitdata(len, 10)
214 if data == None:
215 raise error, 'Lost contact with VCR'
216 return data
217
218 def _getnumber(self, len):
219 data = self._reply(len)
220 number = 0
221 for c in data:
222 digit = ord(c) - NUMBER_N
223 if digit < 0 or digit > 9:
224 raise error, 'Non-digit in number'+`c`
225 number = number*10 + digit
226 return number
227
228 def _iflush(self):
Guido van Rossumfa041701993-06-10 13:32:32 +0000229 dummy = self._waitdata(10000, 0)
Guido van Rossum605b1271993-05-12 12:35:44 +0000230## if dummy:
231## print 'IFLUSH:', dummy
232
233 def simplecmd(self,cmd):
Guido van Rossumfa041701993-06-10 13:32:32 +0000234 self._iflush()
Guido van Rossum605b1271993-05-12 12:35:44 +0000235 for ch in cmd:
236 self._cmd(ch)
237 rep = self._reply(1)
238 if rep == NAK:
239 return 0
240 elif rep <> ACK:
241 raise error, 'Unexpected reply:' + `rep`
242 return 1
243
Guido van Rossumfa041701993-06-10 13:32:32 +0000244 def replycmd(self, cmd):
245 if not self.simplecmd(cmd[:-1]):
246 return 0
247 self._cmd(cmd[-1])
248
Guido van Rossum605b1271993-05-12 12:35:44 +0000249 def _number(self, number, digits):
250 if number < 0:
251 raise error, 'Unexpected negative number:'+ `number`
252 maxnum = pow(10, digits)
253 if number > maxnum:
254 raise error, 'Number too big'
255 while maxnum > 1:
256 number = number % maxnum
257 maxnum = maxnum / 10
258 digit = number / maxnum
259 ok = self.simplecmd(chr(NUMBER_N + digit))
260 if not ok:
261 raise error, 'Error while transmitting number'
262
Jack Jansenf1cda911993-07-19 16:13:33 +0000263 def initvcr(self, *optarg):
264 timeout = None
265 if optarg <> ():
266 timeout = optarg[0]
267 starttime = time.time()
268 self.busy_cmd = None
Guido van Rossum605b1271993-05-12 12:35:44 +0000269 self._iflush()
270 while 1:
271## print 'SENDCL'
272 self._cmd(CL)
273 rep = self._waitdata(1, 2)
274## print `rep`
275 if rep in ( None, CL, NAK ):
Jack Jansenf1cda911993-07-19 16:13:33 +0000276 if timeout:
277 if time.time() - starttime > timeout:
278 raise error, \
279 'No reply from VCR'
Guido van Rossum605b1271993-05-12 12:35:44 +0000280 continue
281 break
282 if rep <> ACK:
283 raise error, 'Unexpected reply:' + `rep`
284 dummy = self.simplecmd(CTRL_ENABLE)
285
286 def waitready(self):
Jack Jansenf1cda911993-07-19 16:13:33 +0000287 rep = self._waitdata(1, None)
Guido van Rossum605b1271993-05-12 12:35:44 +0000288 if rep == None:
Jack Jansenf1cda911993-07-19 16:13:33 +0000289 raise error, 'Unexpected None reply from waitdata'
Guido van Rossum605b1271993-05-12 12:35:44 +0000290 if rep not in (COMPLETION, ACK):
291 self._iflush()
292 raise error, 'Unexpected waitready reply:' + `rep`
Jack Jansenf1cda911993-07-19 16:13:33 +0000293 self.busy_cmd = None
294
295 def testready(self):
296 rep = self._waitdata(1, 0)
297 if rep == None:
298 return 0
299 if rep not in (COMPLETION, ACK):
300 self._iflush()
301 raise error, 'Unexpected waitready reply:' + `rep`
302 self.busy_cmd = None
303 return 1
Guido van Rossum605b1271993-05-12 12:35:44 +0000304
305 def play(self): return self.simplecmd(PLAY)
306 def stop(self): return self.simplecmd(STOP)
307 def ff(self): return self.simplecmd(FF)
308 def rew(self): return self.simplecmd(REW)
309 def eject(self):return self.simplecmd(EJECT)
310 def still(self):return self.simplecmd(STILL)
311 def step(self): return self.simplecmd(STEP_FWD)
312
313 def goto(self, (h, m, s, f)):
314 if not self.simplecmd(SEARCH_DATA):
315 return 0
316 self._number(h, 2)
317 self._number(m, 2)
318 self._number(s, 2)
319 self._number(f, 2)
320 if not self.simplecmd(ENTER):
321 return 0
Jack Jansenf1cda911993-07-19 16:13:33 +0000322 return self._endlongcmd('goto')
Guido van Rossum605b1271993-05-12 12:35:44 +0000323
324 # XXXX TC_SENSE doesn't seem to work
325 def faulty_where(self):
Jack Jansenf1cda911993-07-19 16:13:33 +0000326 self._check()
Guido van Rossum605b1271993-05-12 12:35:44 +0000327 self._cmd(TC_SENSE)
328 h = self._getnumber(2)
329 m = self._getnumber(2)
330 s = self._getnumber(2)
331 f = self._getnumber(2)
332 return (h, m, s, f)
333
334 def where(self):
335 return self.addr2tc(self.sense())
336
337 def sense(self):
Jack Jansenf1cda911993-07-19 16:13:33 +0000338 self._check()
Guido van Rossum605b1271993-05-12 12:35:44 +0000339 self._cmd(ADDR_SENSE)
340 num = self._getnumber(5)
341 return num
342
343 def addr2tc(self, num):
344 f = num % 25
345 num = num / 25
346 s = num % 60
347 num = num / 60
348 m = num % 60
349 h = num / 60
350 return (h, m, s, f)
351
352 def tc2addr(self, (h, m, s, f)):
353 return ((h*60 + m)*60 + s)*25 + f
354
355 def fmmode(self, mode):
Jack Jansenf1cda911993-07-19 16:13:33 +0000356 self._check()
Guido van Rossum605b1271993-05-12 12:35:44 +0000357 if mode == 'off':
358 arg = 0
359 elif mode == 'buffer':
360 arg = 1
361 elif mode == 'dnr':
362 arg = 2
363 else:
364 raise error, 'fmmode arg should be off, buffer or dnr'
365 if not self.simplecmd(FM_SELECT):
366 return 0
367 self._number(arg, 1)
368 if not self.simplecmd(ENTER):
369 return 0
370 return 1
371
Jack Jansenf1cda911993-07-19 16:13:33 +0000372 def mute(self, mode, value):
373 self._check()
374 if mode == 'audio':
375 cmds = (MUTE_AUDIO_OFF, MUTE_AUDIO)
376 elif mode == 'video':
377 cmds = (MUTE_VIDEO_OFF, MUTE_VIDEO)
378 elif mode == 'av':
379 cmds = (MUTE_AV_OFF, MUTE_AV)
380 else:
381 raise error, 'mute type should be audio, video or av'
382 cmd = cmds[value]
383 return self.simplecmd(cmd)
384
Guido van Rossumfa041701993-06-10 13:32:32 +0000385 def editmode(self, mode):
Jack Jansenf1cda911993-07-19 16:13:33 +0000386 self._check()
Guido van Rossumfa041701993-06-10 13:32:32 +0000387 if mode == 'off':
388 a0 = a1 = a2 = 0
389 elif mode == 'format':
390 a0 = 4
391 a1 = 7
392 a2 = 4
393 elif mode == 'asmbl':
394 a0 = 1
395 a1 = 7
396 a2 = 4
397 elif mode == 'insert-video':
398 a0 = 2
399 a1 = 4
400 a2 = 0
401 else:
402 raise 'editmode should be off,format,asmbl or insert-video'
403 if not self.simplecmd(EM_SELECT):
404 return 0
405 self._number(a0, 1)
406 self._number(a1, 1)
407 self._number(a2, 1)
408 if not self.simplecmd(ENTER):
409 return 0
410 return 1
411
Jack Jansenbab2ca31993-09-28 15:28:10 +0000412 def autoedit(self):
413 self._check()
414 return self._endlongcmd(AUTO_EDIT)
415
Guido van Rossumfa041701993-06-10 13:32:32 +0000416 def nframerec(self, num):
417 if not self.simplecmd(N_FRAME_REC):
418 return 0
419 self._number(num, 4)
420 if not self.simplecmd(ENTER):
421 return 0
Jack Jansenf1cda911993-07-19 16:13:33 +0000422 return self._endlongcmd('nframerec')
Guido van Rossumfa041701993-06-10 13:32:32 +0000423
Guido van Rossum605b1271993-05-12 12:35:44 +0000424 def fmstill(self):
425 if not self.simplecmd(FM_STILL):
426 return 0
Jack Jansenf1cda911993-07-19 16:13:33 +0000427 return self._endlongcmd('fmstill')
428
429 def preview(self):
430 if not self.simplecmd(PREVIEW):
431 return 0
432 return self._endlongcmd('preview')
433
434 def review(self):
435 if not self.simplecmd(REVIEW):
436 return 0
437 return self._endlongcmd('review')
438
439 def search_preroll(self):
440 if not self.simplecmd(SEARCH_PREROLL):
441 return 0
442 return self._endlongcmd('search_preroll')
443
444 def edit_pb_standby(self):
445 if not self.simplecmd(EDIT_PB_STANDBY):
446 return 0
447 return self._endlongcmd('edit_pb_standby')
448
449 def edit_play(self):
450 if not self.simplecmd(EDIT_PLAY):
451 return 0
452 return self._endlongcmd('edit_play')
Guido van Rossum605b1271993-05-12 12:35:44 +0000453
454 def dmcontrol(self, mode):
Jack Jansenf1cda911993-07-19 16:13:33 +0000455 self._check()
Guido van Rossum605b1271993-05-12 12:35:44 +0000456 if mode == 'off':
457 return self.simplecmd(DM_OFF)
458 if mode == 'multi freeze':
459 num = 1000
460 elif mode == 'zoom freeze':
461 num = 2000
462 elif mode == 'digital slow':
463 num = 3000
464 elif mode == 'freeze':
465 num = 4011
466 else:
467 raise error, 'unknown dmcontrol argument: ' + `mode`
468 if not self.simplecmd(DM_SET):
469 return 0
470 self._number(num, 4)
471 if not self.simplecmd(ENTER):
472 return 0
473 return 1
474
475 def fwdshuttle(self, num):
476 if not self.simplecmd(FWD_SHUTTLE):
477 return 0
478 self._number(num, 1)
479 return 1
480
481 def revshuttle(self, num):
482 if not self.simplecmd(REV_SHUTTLE):
483 return 0
484 self._number(num, 1)
485 return 1
Guido van Rossumfa041701993-06-10 13:32:32 +0000486
487 def getentry(self, which):
Jack Jansenf1cda911993-07-19 16:13:33 +0000488 self._check()
Guido van Rossumfa041701993-06-10 13:32:32 +0000489 if which == 'in':
490 cmd = IN_ENTRY_SENSE
491 elif which == 'out':
492 cmd = OUT_ENTRY_SENSE
493 self.replycmd(cmd)
494 h = self._getnumber(2)
Guido van Rossumfa041701993-06-10 13:32:32 +0000495 m = self._getnumber(2)
Guido van Rossumfa041701993-06-10 13:32:32 +0000496 s = self._getnumber(2)
Guido van Rossumfa041701993-06-10 13:32:32 +0000497 f = self._getnumber(2)
Guido van Rossumfa041701993-06-10 13:32:32 +0000498 return (h, m, s, f)
499
500 def inentry(self, arg):
501 return self.ioentry(arg, (IN_ENTRY, IN_ENTRY_RESET, \
502 IN_ENTRY_SET, IN_ENTRY_INC, IN_ENTRY_DEC))
503
504 def outentry(self, arg):
505 return self.ioentry(arg, (OUT_ENTRY, OUT_ENTRY_RESET, \
506 OUT_ENTRY_SET, OUT_ENTRY_INC, OUT_ENTRY_DEC))
507
508 def ioentry(self, arg, (Load, Clear, Set, Inc, Dec)):
Jack Jansenf1cda911993-07-19 16:13:33 +0000509 self._check()
Guido van Rossumfa041701993-06-10 13:32:32 +0000510 if type(arg) == type(()):
511 h, m, s, f = arg
512 if not self.simplecmd(Set):
513 return 0
514 self._number(h,2)
515 self._number(m,2)
516 self._number(s,2)
517 self._number(f,2)
518 if not self.simplecmd(ENTER):
519 return 0
520 return 1
521 elif arg == 'reset':
522 cmd = Clear
523 elif arg == 'load':
524 cmd = Load
525 elif arg == '+':
526 cmd = Inc
527 elif arg == '-':
528 cmd = Dec
529 else:
530 raise error, 'Arg should be +,-,reset,load or (h,m,s,f)'
531 return self.simplecmd(cmd)
Jack Jansenbab2ca31993-09-28 15:28:10 +0000532
533 def cancel(self):
534 d = self.simplecmd(CL)
535 self.busy_cmd = None