home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Demo / sgi / video / VCR.py < prev    next >
Text File  |  1994-06-07  |  12KB  |  535 lines

  1. import fcntl
  2. import IOCTL
  3. from IOCTL import *
  4. import sys
  5. import struct
  6. import select
  7. import posix
  8. import time
  9.  
  10. DEVICE='/dev/ttyd2'
  11.  
  12. class 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.  
  32. def 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.  
  50. def nullttyargs():
  51.     chars = ['\0']*IOCTL.NCCS
  52.     return packttyargs(0, 0, 0, 0, 0, chars)
  53.  
  54. def 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.  
  63. def initline(name):
  64.     fp = UnixFile().open(name, 2)
  65.     ofp = fp
  66.     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.  
  79. #
  80. #
  81. error = 'VCR.error'
  82.  
  83. # Commands/replies:
  84. COMPLETION = '\x01'
  85. ACK  ='\x0a'
  86. NAK  ='\x0b'
  87.  
  88. NUMBER_N = 0x30
  89. ENTER  = '\x40'
  90.  
  91. EXP_7= '\xde'
  92. EXP_8= '\xdf'
  93.  
  94. CL   ='\x56'
  95. CTRL_ENABLE = EXP_8 + '\xc6'
  96. SEARCH_DATA = EXP_8 + '\x93'
  97. ADDR_SENSE = '\x60'
  98.  
  99. PLAY ='\x3a'
  100. STOP ='\x3f'
  101. EJECT='\x2a'
  102. FF   ='\xab'
  103. REW  ='\xac'
  104. STILL='\x4f'
  105. STEP_FWD ='\x2b'    # Was: '\xad'
  106. FM_SELECT=EXP_8 + '\xc8'
  107. FM_STILL=EXP_8 + '\xcd'
  108. PREVIEW=EXP_7 + '\x9d'
  109. REVIEW=EXP_7 + '\x9e'
  110. DM_OFF=EXP_8 + '\xc9'
  111. DM_SET=EXP_8 + '\xc4'
  112. FWD_SHUTTLE='\xb5'
  113. REV_SHUTTLE='\xb6'
  114. EM_SELECT=EXP_8 + '\xc0'
  115. N_FRAME_REC=EXP_8 + '\x92'
  116. SEARCH_PREROLL=EXP_8 + '\x90'
  117. EDIT_PB_STANDBY=EXP_8 + '\x96'
  118. EDIT_PLAY=EXP_8 + '\x98'
  119. AUTO_EDIT=EXP_7 + '\x9c'
  120.  
  121. IN_ENTRY=EXP_7 + '\x90'
  122. IN_ENTRY_RESET=EXP_7 + '\x91'
  123. IN_ENTRY_SET=EXP_7 + '\x98'
  124. IN_ENTRY_INC=EXP_7 + '\x94'
  125. IN_ENTRY_DEC=EXP_7 + '\x95'
  126. IN_ENTRY_SENSE=EXP_7 + '\x9a'
  127.  
  128. OUT_ENTRY=EXP_7 + '\x92'
  129. OUT_ENTRY_RESET=EXP_7 + '\x93'
  130. OUT_ENTRY_SET=EXP_7 + '\x99'
  131. OUT_ENTRY_INC=EXP_7 + '\x96'
  132. OUT_ENTRY_DEC=EXP_7 + '\x98'
  133. OUT_ENTRY_SENSE=EXP_7 + '\x9b'
  134.  
  135. MUTE_AUDIO = '\x24'
  136. MUTE_AUDIO_OFF = '\x25'
  137. MUTE_VIDEO = '\x26'
  138. MUTE_VIDEO_OFF = '\x27'
  139. MUTE_AV = EXP_7 + '\xc6'
  140. MUTE_AV_OFF = EXP_7 + '\xc7'
  141.  
  142. DEBUG=0
  143.  
  144. class VCR:
  145.     def __init__(self):
  146.         self.ifp, self.ofp = initline(DEVICE)
  147.         self.busy_cmd = None
  148.         self.async = 0
  149.         self.cb = None
  150.         self.cb_arg = None
  151.  
  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.  
  183.     def _cmd(self, cmd):
  184.         if DEBUG:
  185.             print '>>>',`cmd`
  186.         self.ofp.write(cmd)
  187.         self.ofp.flush()
  188.  
  189.     def _waitdata(self, len, tout):
  190.         rep = ''
  191.         while len > 0:
  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)
  198.             if ready == []:
  199. ##                if rep:
  200. ##                    print 'FLUSHED:', `rep`
  201.                 return None
  202.             data = self.ifp.read(1)
  203.             if DEBUG:
  204.                 print '<<<',`data`
  205.             if data == NAK:
  206.                 return NAK
  207.             rep = rep + data
  208.             len = len - 1
  209.         return rep
  210.  
  211.     def _reply(self, len):
  212.         data = self._waitdata(len, 10)
  213.         if data == None:
  214.             raise error, 'Lost contact with VCR'
  215.         return data
  216.  
  217.     def _getnumber(self, len):
  218.         data = self._reply(len)
  219.         number = 0
  220.         for c in data:
  221.             digit = ord(c) - NUMBER_N
  222.             if digit < 0 or digit > 9:
  223.                 raise error, 'Non-digit in number'+`c`
  224.             number = number*10 + digit
  225.         return number
  226.  
  227.     def _iflush(self):
  228.         dummy = self._waitdata(10000, 0)
  229. ##        if dummy:
  230. ##            print 'IFLUSH:', dummy
  231.  
  232.     def simplecmd(self,cmd):
  233.         self._iflush()
  234.         for ch in cmd:
  235.             self._cmd(ch)
  236.             rep = self._reply(1)
  237.             if rep == NAK:
  238.                 return 0
  239.             elif rep <> ACK:
  240.                 raise error, 'Unexpected reply:' + `rep`
  241.         return 1
  242.  
  243.     def replycmd(self, cmd):
  244.         if not self.simplecmd(cmd[:-1]):
  245.             return 0
  246.         self._cmd(cmd[-1])
  247.  
  248.     def _number(self, number, digits):
  249.         if number < 0:
  250.             raise error, 'Unexpected negative number:'+ `number`
  251.         maxnum = pow(10, digits)
  252.         if number > maxnum:
  253.             raise error, 'Number too big'
  254.         while maxnum > 1:
  255.             number = number % maxnum
  256.             maxnum = maxnum / 10
  257.             digit = number / maxnum
  258.             ok = self.simplecmd(chr(NUMBER_N + digit))
  259.             if not ok:
  260.                 raise error, 'Error while transmitting number'
  261.  
  262.     def initvcr(self, *optarg):
  263.         timeout = None
  264.         if optarg <> ():
  265.             timeout = optarg[0]
  266.             starttime = time.time()
  267.         self.busy_cmd = None
  268.         self._iflush()
  269.         while 1:
  270. ##            print 'SENDCL'
  271.             self._cmd(CL)
  272.             rep = self._waitdata(1, 2)
  273. ##            print `rep`
  274.             if rep in ( None, CL, NAK ):
  275.                 if timeout:
  276.                     if time.time() - starttime > timeout:
  277.                         raise error, \
  278.                               'No reply from VCR'
  279.                 continue
  280.             break
  281.         if rep <> ACK:
  282.             raise error, 'Unexpected reply:' + `rep`
  283.         dummy = self.simplecmd(CTRL_ENABLE)
  284.  
  285.     def waitready(self):
  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)
  296.         if rep == None:
  297.             return 0
  298.         if rep not in  (COMPLETION, ACK):
  299.             self._iflush()
  300.             raise error, 'Unexpected waitready reply:' + `rep`
  301.         self.busy_cmd = None
  302.         return 1
  303.  
  304.     def play(self): return self.simplecmd(PLAY)
  305.     def stop(self): return self.simplecmd(STOP)
  306.     def ff(self):   return self.simplecmd(FF)
  307.     def rew(self):  return self.simplecmd(REW)
  308.     def eject(self):return self.simplecmd(EJECT)
  309.     def still(self):return self.simplecmd(STILL)
  310.     def step(self): return self.simplecmd(STEP_FWD)
  311.  
  312.     def goto(self, (h, m, s, f)):
  313.         if not self.simplecmd(SEARCH_DATA):
  314.             return 0
  315.         self._number(h, 2)
  316.         self._number(m, 2)
  317.         self._number(s, 2)
  318.         self._number(f, 2)
  319.         if not self.simplecmd(ENTER):
  320.             return 0
  321.         return self._endlongcmd('goto')
  322.  
  323.     # XXXX TC_SENSE doesn't seem to work
  324.     def faulty_where(self):
  325.         self._check()
  326.         self._cmd(TC_SENSE)
  327.         h = self._getnumber(2)
  328.         m = self._getnumber(2)
  329.         s = self._getnumber(2)
  330.         f = self._getnumber(2)
  331.         return (h, m, s, f)
  332.  
  333.     def where(self):
  334.         return self.addr2tc(self.sense())
  335.  
  336.     def sense(self):
  337.         self._check()
  338.         self._cmd(ADDR_SENSE)
  339.         num = self._getnumber(5)
  340.         return num
  341.  
  342.     def addr2tc(self, num):
  343.         f = num % 25
  344.         num = num / 25
  345.         s = num % 60
  346.         num = num / 60
  347.         m = num % 60
  348.         h = num / 60
  349.         return (h, m, s, f)
  350.  
  351.     def tc2addr(self, (h, m, s, f)):
  352.         return ((h*60 + m)*60 + s)*25 + f
  353.  
  354.     def fmmode(self, mode):
  355.         self._check()
  356.         if mode == 'off':
  357.             arg = 0
  358.         elif mode == 'buffer':
  359.             arg = 1
  360.         elif mode == 'dnr':
  361.             arg = 2
  362.         else:
  363.             raise error, 'fmmode arg should be off, buffer or dnr'
  364.         if not self.simplecmd(FM_SELECT):
  365.             return 0
  366.         self._number(arg, 1)
  367.         if not self.simplecmd(ENTER):
  368.             return 0
  369.         return 1
  370.  
  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.  
  384.     def editmode(self, mode):
  385.         self._check()
  386.         if mode == 'off':
  387.             a0 = a1 = a2 = 0
  388.         elif mode == 'format':
  389.             a0 = 4
  390.             a1 = 7
  391.             a2 = 4
  392.         elif mode == 'asmbl':
  393.             a0 = 1
  394.             a1 = 7
  395.             a2 = 4
  396.         elif mode == 'insert-video':
  397.             a0 = 2
  398.             a1 = 4
  399.             a2 = 0
  400.         else:
  401.             raise 'editmode should be off,format,asmbl or insert-video'
  402.         if not self.simplecmd(EM_SELECT):
  403.             return 0
  404.         self._number(a0, 1)
  405.         self._number(a1, 1)
  406.         self._number(a2, 1)
  407.         if not self.simplecmd(ENTER):
  408.             return 0
  409.         return 1
  410.  
  411.     def autoedit(self):
  412.         self._check()
  413.         return self._endlongcmd(AUTO_EDIT)
  414.  
  415.     def nframerec(self, num):
  416.         if not self.simplecmd(N_FRAME_REC):
  417.             return 0
  418.         self._number(num, 4)
  419.         if not self.simplecmd(ENTER):
  420.             return 0
  421.         return self._endlongcmd('nframerec')
  422.  
  423.     def fmstill(self):
  424.         if not self.simplecmd(FM_STILL):
  425.             return 0
  426.         return self._endlongcmd('fmstill')
  427.  
  428.     def preview(self):
  429.         if not self.simplecmd(PREVIEW):
  430.             return 0
  431.         return self._endlongcmd('preview')
  432.  
  433.     def review(self):
  434.         if not self.simplecmd(REVIEW):
  435.             return 0
  436.         return self._endlongcmd('review')
  437.  
  438.     def search_preroll(self):
  439.         if not self.simplecmd(SEARCH_PREROLL):
  440.             return 0
  441.         return self._endlongcmd('search_preroll')
  442.  
  443.     def edit_pb_standby(self):
  444.         if not self.simplecmd(EDIT_PB_STANDBY):
  445.             return 0
  446.         return self._endlongcmd('edit_pb_standby')
  447.  
  448.     def edit_play(self):
  449.         if not self.simplecmd(EDIT_PLAY):
  450.             return 0
  451.         return self._endlongcmd('edit_play')
  452.  
  453.     def dmcontrol(self, mode):
  454.         self._check()
  455.         if mode == 'off':
  456.             return self.simplecmd(DM_OFF)
  457.         if mode == 'multi freeze':
  458.             num = 1000
  459.         elif mode == 'zoom freeze':
  460.             num = 2000
  461.         elif mode == 'digital slow':
  462.             num = 3000
  463.         elif mode == 'freeze':
  464.             num = 4011
  465.         else:
  466.             raise error, 'unknown dmcontrol argument: ' + `mode`
  467.         if not self.simplecmd(DM_SET):
  468.             return 0
  469.         self._number(num, 4)
  470.         if not self.simplecmd(ENTER):
  471.             return 0
  472.         return 1
  473.  
  474.     def fwdshuttle(self, num):
  475.         if not self.simplecmd(FWD_SHUTTLE):
  476.             return 0
  477.         self._number(num, 1)
  478.         return 1
  479.  
  480.     def revshuttle(self, num):
  481.         if not self.simplecmd(REV_SHUTTLE):
  482.             return 0
  483.         self._number(num, 1)
  484.         return 1
  485.  
  486.     def getentry(self, which):
  487.         self._check()
  488.         if which == 'in':
  489.             cmd = IN_ENTRY_SENSE
  490.         elif which == 'out':
  491.             cmd = OUT_ENTRY_SENSE
  492.         self.replycmd(cmd)
  493.         h = self._getnumber(2)
  494.         m = self._getnumber(2)
  495.         s = self._getnumber(2)
  496.         f = self._getnumber(2)
  497.         return (h, m, s, f)
  498.  
  499.     def inentry(self, arg):
  500.         return self.ioentry(arg, (IN_ENTRY, IN_ENTRY_RESET, \
  501.               IN_ENTRY_SET, IN_ENTRY_INC, IN_ENTRY_DEC))
  502.  
  503.     def outentry(self, arg):
  504.         return self.ioentry(arg, (OUT_ENTRY, OUT_ENTRY_RESET, \
  505.               OUT_ENTRY_SET, OUT_ENTRY_INC, OUT_ENTRY_DEC))
  506.  
  507.     def ioentry(self, arg, (Load, Clear, Set, Inc, Dec)):
  508.         self._check()
  509.         if type(arg) == type(()):
  510.             h, m, s, f = arg
  511.             if not self.simplecmd(Set):
  512.                 return 0
  513.             self._number(h,2)
  514.             self._number(m,2)
  515.             self._number(s,2)
  516.             self._number(f,2)
  517.             if not self.simplecmd(ENTER):
  518.                 return 0
  519.             return 1
  520.         elif arg == 'reset':
  521.             cmd = Clear
  522.         elif arg == 'load':
  523.             cmd = Load
  524.         elif arg == '+':
  525.             cmd = Inc
  526.         elif arg == '-':
  527.             cmd = Dec
  528.         else:
  529.             raise error, 'Arg should be +,-,reset,load or (h,m,s,f)'
  530.         return self.simplecmd(cmd)
  531.  
  532.     def cancel(self):
  533.         d = self.simplecmd(CL)
  534.         self.busy_cmd = None
  535.