048fd2ffe40d76bd96c7d29769fb43da465592af
[vpproxy.git] / vlcclient / vlcclient.py
1 '''
2 Minimal VLC VLM client for AceProxy. Client class.
3 '''
4
5 import gevent
6 import gevent.event
7 import gevent.coros
8 import telnetlib
9 import logging
10 from vlcmessages import *
11
12
13 class VlcException(Exception):
14
15     '''
16     Exception from VlcClient
17     '''
18     pass
19
20
21 class VlcClient(object):
22
23     '''
24     VLC Client class
25     '''
26
27     def __init__(
28         self, host='127.0.0.1', port=4212, password='admin', connect_timeout=10,
29             result_timeout=10, out_port=8081):
30         # Receive buffer
31         self._recvbuffer = None
32         # Output port
33         self._out_port = out_port
34         # VLC socket
35         self._socket = None
36         # Result timeout
37         self._resulttimeout = result_timeout
38         # Shutting down flag
39         self._shuttingDown = gevent.event.Event()
40         # Authentication done event
41         self._auth = gevent.event.AsyncResult()
42         # Request lock
43         self._resultlock = gevent.coros.RLock()
44         # Request result
45         self._result = gevent.event.AsyncResult()
46         # VLC version string
47         self._vlcver = None
48         # Saving password
49         self._password = password
50
51         # Logger
52         logger = logging.getLogger('VlcClient_init')
53
54         # Making connection
55         try:
56             self._socket = telnetlib.Telnet(host, port, connect_timeout)
57             logger.debug("Successfully connected with VLC socket!")
58         except Exception as e:
59             raise VlcException(
60                 "Socket creation error! VLC is not running? ERROR: " + repr(e))
61
62         # Spawning recvData greenlet
63         gevent.spawn(self._recvData)
64         gevent.sleep()
65
66         # Waiting for authentication event
67         try:
68             if self._auth.get(timeout=self._resulttimeout) == False:
69                 errmsg = "Authentication error"
70                 logger.error(errmsg)
71                 raise VlcException(errmsg)
72         except gevent.Timeout:
73             errmsg = "Authentication timeout"
74             logger.error(errmsg)
75             raise VlcException(errmsg)
76
77     def __del__(self):
78         # Destructor just calls destroy() method
79         self.destroy()
80
81     def destroy(self):
82         # Logger
83         logger = logging.getLogger("VlcClient_destroy")
84
85         if self._shuttingDown.isSet():
86             # Already in the middle of destroying
87             return
88
89         # If socket is still alive (connected)
90         if self._socket:
91             try:
92                 logger.debug("Destroying VlcClient...")
93                 self._write(VlcMessage.request.SHUTDOWN)
94                 # Set shuttingDown flag for recvData
95                 self._shuttingDown.set()
96             except:
97                 # Ignore exceptions on destroy
98                 pass
99
100     def _write(self, message):
101         # Return if in the middle of destroying
102         if self._shuttingDown.isSet():
103             return
104
105         try:
106             # Write message
107             print 'VLC command:',message
108             self._socket.write(message + "\r\n")
109         except EOFError as e:
110             raise VlcException("Vlc Write error! ERROR: " + repr(e))
111
112     def _broadcast(self, brtype, stream_name, input=None, muxer='ts', pre_access='', qtype='default'):
113         if self._shuttingDown.isSet():
114             return
115
116         # Start/stop broadcast with VLC
117         # Logger
118         if brtype == True:
119             broadcast = 'startBroadcast'
120         else:
121             broadcast = 'stopBroadcast'
122
123         logger = logging.getLogger("VlcClient_" + broadcast)
124         # Clear AsyncResult
125         self._result = gevent.event.AsyncResult()
126         # Get lock
127         self._resultlock.acquire()
128         # Write message to VLC socket
129         if brtype == True:
130             msg = VlcMessage.request.startBroadcast(stream_name, input, self._out_port, muxer, pre_access, qtype)
131             self._write(msg)
132         else:
133             self._write(VlcMessage.request.stopBroadcast(stream_name))
134
135         try:
136             gevent.sleep()
137             result = self._result.get(timeout=self._resulttimeout)
138             if result == False:
139                 logger.error(broadcast + " error")
140                 raise VlcException(broadcast + " error")
141         except gevent.Timeout:
142             logger.error(broadcast + " result timeout")
143             raise VlcException(broadcast + " result timeout")
144         finally:
145             self._resultlock.release()
146
147         if brtype == True:
148             logger.debug("Broadcast started")
149         else:
150             logger.debug("Broadcast stopped")
151
152     def startBroadcast(self, stream_name, input, muxer='ts', pre_access='', qtype='default'):
153         print "Starting broadcast......"
154         return self._broadcast(True, stream_name, input, muxer, pre_access, qtype)
155
156     def stopBroadcast(self, stream_name):
157         return self._broadcast(False, stream_name)
158
159     def pauseBroadcast(self, stream_name):
160         return self._write(VlcMessage.request.pauseBroadcast(stream_name))
161
162     def playBroadcast(self, stream_name):
163         return self._write(VlcMessage.request.playBroadcast(stream_name))
164
165     def _recvData(self):
166         # Logger
167         logger = logging.getLogger("VlcClient_recvData")
168
169         while True:
170             gevent.sleep()
171             try:
172                 self._recvbuffer = self._socket.read_until("\n")
173                 # Stripping "> " from VLC
174                 self._recvbuffer = self._recvbuffer.lstrip("> ")
175             except:
176                 # If something happened during read, abandon reader
177                 if not self._shuttingDown.isSet():
178                     logger.error("Exception at socket read")
179                     self._shuttingDown.set()
180                 return
181
182             # Parsing everything only if the string is not empty
183             if self._recvbuffer:
184                 if not self._vlcver:
185                     # First line (VLC version)
186                     self._vlcver = self._recvbuffer.strip()
187                     # Send password here since PASSWORD doesn't have \n
188                     self._write(self._password)
189
190                 elif self._recvbuffer.startswith(VlcMessage.response.SHUTDOWN):
191                     # Exit from this loop
192                     logger.debug("Got SHUTDOWN from VLC")
193                     return
194
195                 elif self._recvbuffer.startswith(VlcMessage.response.WRONGPASS):
196                     # Wrong password
197                     logger.error("Wrong VLC password!")
198                     self._auth.set(False)
199                     return
200
201                 elif self._recvbuffer.startswith(VlcMessage.response.AUTHOK):
202                     # Authentication OK
203                     logger.info("Authentication successful")
204                     self._auth.set(True)
205
206                 elif VlcMessage.response.BROADCASTEXISTS in self._recvbuffer:
207                     # Broadcast already exists
208                     logger.error("Broadcast already exists!")
209                     self._result.set(False)
210
211                 elif VlcMessage.response.STOPERR in self._recvbuffer:
212                     # Media unknown (stopping non-existent stream)
213                     logger.error("Broadcast does not exist!")
214                     self._result.set(False)
215
216                 # Do not move this before error handlers!
217                 elif self._recvbuffer.startswith(VlcMessage.response.STARTOK):
218                     # Broadcast started
219                     logger.debug("Broadcast started")
220                     self._result.set(True)
221
222                 elif self._recvbuffer.startswith(VlcMessage.response.STOPOK):
223                     # Broadcast stopped
224                     logger.debug("Broadcast stopped")
225                     self._result.set(True)