55import struct
66import select
77import posix
8+ import time
89
910DEVICE = '/dev/ttyd2'
1011
@@ -75,20 +76,8 @@ def initline(name):
7576 dummy = fcntl .ioctl (fd , IOCTL .TCSETA , arg )
7677 return fp , ofp
7778
78- #ifp, ofp = initline('/dev/ttyd2')
79- #while 1:
80- # print 'GO'
81- # inset, d, d = select.select([sys.stdin, ifp], [], [])
82- # if sys.stdin in inset:
83- # cmd = eval(sys.stdin.readline(100))
84- # print 'CMD:', `cmd`
85- # if cmd:
86- # ofp.write(cmd)
87- # ofp.flush()
88- # if ifp in inset:
89- # data = ifp.read(1)
90- # print 'LEN', len(data), 'DATA', `data`
91-
79+ #
80+ #
9281error = 'VCR.error'
9382
9483# Commands/replies:
@@ -116,12 +105,17 @@ def initline(name):
116105STEP_FWD = '\x2b ' # Was: '\xad'
117106FM_SELECT = EXP_8 + '\xc8 '
118107FM_STILL = EXP_8 + '\xcd '
108+ PREVIEW = EXP_7 + '\x9d '
109+ REVIEW = EXP_7 + '\x9e '
119110DM_OFF = EXP_8 + '\xc9 '
120111DM_SET = EXP_8 + '\xc4 '
121112FWD_SHUTTLE = '\xb5 '
122113REV_SHUTTLE = '\xb6 '
123114EM_SELECT = EXP_8 + '\xc0 '
124115N_FRAME_REC = EXP_8 + '\x92 '
116+ SEARCH_PREROLL = EXP_8 + '\x90 '
117+ EDIT_PB_STANDBY = EXP_8 + '\x96 '
118+ EDIT_PLAY = EXP_8 + '\x98 '
125119
126120IN_ENTRY = EXP_7 + '\x90 '
127121IN_ENTRY_RESET = EXP_7 + '\x91 '
@@ -137,13 +131,55 @@ def initline(name):
137131OUT_ENTRY_DEC = EXP_7 + '\x98 '
138132OUT_ENTRY_SENSE = EXP_7 + '\x9b '
139133
134+ MUTE_AUDIO = '\x24 '
135+ MUTE_AUDIO_OFF = '\x25 '
136+ MUTE_VIDEO = '\x26 '
137+ MUTE_VIDEO_OFF = '\x27 '
138+ MUTE_AV = EXP_7 + '\xc6 '
139+ MUTE_AV_OFF = EXP_7 + '\xc7 '
140+
140141DEBUG = 0
141142
142143class VCR :
143144 def init (self ):
144145 self .ifp , self .ofp = initline (DEVICE )
146+ self .busy_cmd = None
147+ self .async = 0
148+ self .cb = None
149+ self .cb_arg = None
145150 return self
146151
152+ def _check (self ):
153+ if self .busy_cmd :
154+ raise error , 'Another command active: ' + self .busy_cmd
155+
156+ def _endlongcmd (self , name ):
157+ if not self .async :
158+ self .waitready ()
159+ return 1
160+ self .busy_cmd = name
161+ return 'VCR BUSY'
162+
163+ def fileno (self ):
164+ return self .ifp .fileno ()
165+
166+ def setasync (self , async ):
167+ self .async = async
168+
169+ def setcallback (self , cb , arg ):
170+ self .setasync (1 )
171+ self .cb = cb
172+ self .cb_arg = arg
173+
174+ def poll (self ):
175+ if not self .async :
176+ raise error , 'Can only call poll() in async mode'
177+ if not self .busy_cmd :
178+ return
179+ if self .testready ():
180+ if self .cb :
181+ apply (self .cb , self .cb_arg )
182+
147183 def _cmd (self , cmd ):
148184 if DEBUG :
149185 print '>>>' ,`cmd`
@@ -153,12 +189,16 @@ def _cmd(self, cmd):
153189 def _waitdata (self , len , tout ):
154190 rep = ''
155191 while len > 0 :
156- ready , d1 , d2 = select .select ([self .ifp ], [], [], tout )
192+ if tout == None :
193+ ready , d1 , d2 = select .select ( \
194+ [self .ifp ], [], [])
195+ else :
196+ ready , d1 , d2 = select .select ( \
197+ [self .ifp ], [], [], tout )
157198 if ready == []:
158199## if rep:
159200## print 'FLUSHED:', `rep`
160201 return None
161- # XXXX Niet goed: er is meer gebufferd!
162202 data = self .ifp .read (1 )
163203 if DEBUG :
164204 print '<<<' ,`data`
@@ -219,27 +259,47 @@ def _number(self, number, digits):
219259 if not ok :
220260 raise error , 'Error while transmitting number'
221261
222- def wait (self ):
262+ def initvcr (self , * optarg ):
263+ timeout = None
264+ if optarg <> ():
265+ timeout = optarg [0 ]
266+ starttime = time .time ()
267+ self .busy_cmd = None
223268 self ._iflush ()
224269 while 1 :
225270## print 'SENDCL'
226271 self ._cmd (CL )
227272 rep = self ._waitdata (1 , 2 )
228273## print `rep`
229274 if rep in ( None , CL , NAK ):
275+ if timeout :
276+ if time .time () - starttime > timeout :
277+ raise error , \
278+ 'No reply from VCR'
230279 continue
231280 break
232281 if rep <> ACK :
233282 raise error , 'Unexpected reply:' + `rep`
234283 dummy = self .simplecmd (CTRL_ENABLE )
235284
236285 def waitready (self ):
237- rep = self ._waitdata (1 , 60 )
286+ rep = self ._waitdata (1 , None )
287+ if rep == None :
288+ raise error , 'Unexpected None reply from waitdata'
289+ if rep not in (COMPLETION , ACK ):
290+ self ._iflush ()
291+ raise error , 'Unexpected waitready reply:' + `rep`
292+ self .busy_cmd = None
293+
294+ def testready (self ):
295+ rep = self ._waitdata (1 , 0 )
238296 if rep == None :
239- raise error , 'Command not finished in one minute'
297+ return 0
240298 if rep not in (COMPLETION , ACK ):
241299 self ._iflush ()
242300 raise error , 'Unexpected waitready reply:' + `rep`
301+ self .busy_cmd = None
302+ return 1
243303
244304 def play (self ): return self .simplecmd (PLAY )
245305 def stop (self ): return self .simplecmd (STOP )
@@ -258,11 +318,11 @@ def goto(self, (h, m, s, f)):
258318 self ._number (f , 2 )
259319 if not self .simplecmd (ENTER ):
260320 return 0
261- self .waitready ()
262- return 1
321+ return self ._endlongcmd ('goto' )
263322
264323 # XXXX TC_SENSE doesn't seem to work
265324 def faulty_where (self ):
325+ self ._check ()
266326 self ._cmd (TC_SENSE )
267327 h = self ._getnumber (2 )
268328 m = self ._getnumber (2 )
@@ -274,6 +334,7 @@ def where(self):
274334 return self .addr2tc (self .sense ())
275335
276336 def sense (self ):
337+ self ._check ()
277338 self ._cmd (ADDR_SENSE )
278339 num = self ._getnumber (5 )
279340 return num
@@ -291,6 +352,7 @@ def tc2addr(self, (h, m, s, f)):
291352 return ((h * 60 + m )* 60 + s )* 25 + f
292353
293354 def fmmode (self , mode ):
355+ self ._check ()
294356 if mode == 'off' :
295357 arg = 0
296358 elif mode == 'buffer' :
@@ -306,7 +368,21 @@ def fmmode(self, mode):
306368 return 0
307369 return 1
308370
371+ def mute (self , mode , value ):
372+ self ._check ()
373+ if mode == 'audio' :
374+ cmds = (MUTE_AUDIO_OFF , MUTE_AUDIO )
375+ elif mode == 'video' :
376+ cmds = (MUTE_VIDEO_OFF , MUTE_VIDEO )
377+ elif mode == 'av' :
378+ cmds = (MUTE_AV_OFF , MUTE_AV )
379+ else :
380+ raise error , 'mute type should be audio, video or av'
381+ cmd = cmds [value ]
382+ return self .simplecmd (cmd )
383+
309384 def editmode (self , mode ):
385+ self ._check ()
310386 if mode == 'off' :
311387 a0 = a1 = a2 = 0
312388 elif mode == 'format' :
@@ -338,16 +414,40 @@ def nframerec(self, num):
338414 self ._number (num , 4 )
339415 if not self .simplecmd (ENTER ):
340416 return 0
341- self .waitready ()
342- return 1
417+ return self ._endlongcmd ('nframerec' )
343418
344419 def fmstill (self ):
345420 if not self .simplecmd (FM_STILL ):
346421 return 0
347- self .waitready ()
348- return 1
422+ return self ._endlongcmd ('fmstill' )
423+
424+ def preview (self ):
425+ if not self .simplecmd (PREVIEW ):
426+ return 0
427+ return self ._endlongcmd ('preview' )
428+
429+ def review (self ):
430+ if not self .simplecmd (REVIEW ):
431+ return 0
432+ return self ._endlongcmd ('review' )
433+
434+ def search_preroll (self ):
435+ if not self .simplecmd (SEARCH_PREROLL ):
436+ return 0
437+ return self ._endlongcmd ('search_preroll' )
438+
439+ def edit_pb_standby (self ):
440+ if not self .simplecmd (EDIT_PB_STANDBY ):
441+ return 0
442+ return self ._endlongcmd ('edit_pb_standby' )
443+
444+ def edit_play (self ):
445+ if not self .simplecmd (EDIT_PLAY ):
446+ return 0
447+ return self ._endlongcmd ('edit_play' )
349448
350449 def dmcontrol (self , mode ):
450+ self ._check ()
351451 if mode == 'off' :
352452 return self .simplecmd (DM_OFF )
353453 if mode == 'multi freeze' :
@@ -380,6 +480,7 @@ def revshuttle(self, num):
380480 return 1
381481
382482 def getentry (self , which ):
483+ self ._check ()
383484 if which == 'in' :
384485 cmd = IN_ENTRY_SENSE
385486 elif which == 'out' :
@@ -404,6 +505,7 @@ def outentry(self, arg):
404505 OUT_ENTRY_SET , OUT_ENTRY_INC , OUT_ENTRY_DEC ))
405506
406507 def ioentry (self , arg , (Load , Clear , Set , Inc , Dec )):
508+ self ._check ()
407509 if type (arg ) == type (()):
408510 h , m , s , f = arg
409511 if not self .simplecmd (Set ):
0 commit comments