85d4be2c6337f40c793e2248a862c4c7a7f1c220
[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
102         logger = logging.getLogger("VlcClient_write")
103
104         # Return if in the middle of destroying
105         if self._shuttingDown.isSet():
106             return
107
108         try:
109             # Write message
110             logger.debug('VLC command: ' + message)
111             self._socket.write(message + "\r\n")
112         except EOFError as e:
113             raise VlcException("Vlc Write error! ERROR: " + repr(e))
114
115     def _broadcast(self, brtype, stream_name, input=None, muxer='ts', pre_access='', qtype='default'):
116         if self._shuttingDown.isSet():
117             return
118
119         # Start/stop broadcast with VLC
120         # Logger
121         if brtype == True:
122             broadcast = 'startBroadcast'
123         else:
124             broadcast = 'stopBroadcast'
125
126         logger = logging.getLogger("VlcClient_" + broadcast)
127         # Clear AsyncResult
128         self._result = gevent.event.AsyncResult()
129         # Get lock
130         self._resultlock.acquire()
131         # Write message to VLC socket
132         if brtype == True:
133             msg = VlcMessage.request.startBroadcast(stream_name, input, self._out_port, muxer, pre_access, qtype)
134             self._write(msg)
135         else:
136             self._write(VlcMessage.request.stopBroadcast(stream_name))
137
138         try:
139             gevent.sleep()
140             result = self._result.get(timeout=self._resulttimeout)
141             if result == False:
142                 logger.error(broadcast + " error")
143                 raise VlcException(broadcast + " error")
144         except gevent.Timeout:
145             logger.error(broadcast + " result timeout")
146             raise VlcException(broadcast + " result timeout")
147         finally:
148             self._resultlock.release()
149
150         if brtype == True:
151             logger.debug("Broadcast started")
152         else:
153             logger.debug("Broadcast stopped")
154
155     def startBroadcast(self, stream_name, input, muxer='ts', pre_access='', qtype='default'):
156         logger.debug("Starting broadcast......")
157         return self._broadcast(True, stream_name, input, muxer, pre_access, qtype)
158
159     def stopBroadcast(self, stream_name):
160         return self._broadcast(False, stream_name)
161
162     def pauseBroadcast(self, stream_name):
163         return self._write(VlcMessage.request.pauseBroadcast(stream_name))
164
165     def playBroadcast(self, stream_name):
166         return self._write(VlcMessage.request.playBroadcast(stream_name))
167
168     def _recvData(self):
169         # Logger
170         logger = logging.getLogger("VlcClient_recvData")
171
172         while True:
173             gevent.sleep()
174             try:
175                 self._recvbuffer = self._socket.read_until("\n")
176                 # Stripping "> " from VLC
177                 self._recvbuffer = self._recvbuffer.lstrip("> ")
178             except:
179                 # If something happened during read, abandon reader
180                 if not self._shuttingDown.isSet():
181                     logger.error("Exception at socket read")
182                     self._shuttingDown.set()
183                 return
184
185             # Parsing everything only if the string is not empty
186             if self._recvbuffer:
187                 if not self._vlcver:
188                     # First line (VLC version)
189                     self._vlcver = self._recvbuffer.strip()
190                     # Send password here since PASSWORD doesn't have \n
191                     self._write(self._password)
192
193                 elif self._recvbuffer.startswith(VlcMessage.response.SHUTDOWN):
194                     # Exit from this loop
195                     logger.debug("Got SHUTDOWN from VLC")
196                     return
197
198                 elif self._recvbuffer.startswith(VlcMessage.response.WRONGPASS):
199                     # Wrong password
200                     logger.error("Wrong VLC password!")
201                     self._auth.set(False)
202                     return
203
204                 elif self._recvbuffer.startswith(VlcMessage.response.AUTHOK):
205                     # Authentication OK
206                     logger.info("Authentication successful")
207                     self._auth.set(True)
208
209                 elif VlcMessage.response.BROADCASTEXISTS in self._recvbuffer:
210                     # Broadcast already exists
211                     logger.error("Broadcast already exists!")
212                     self._result.set(False)
213
214                 elif VlcMessage.response.STOPERR in self._recvbuffer:
215                     # Media unknown (stopping non-existent stream)
216                     logger.error("Broadcast does not exist!")
217                     self._result.set(False)
218
219                 # Do not move this before error handlers!
220                 elif self._recvbuffer.startswith(VlcMessage.response.STARTOK):
221                     # Broadcast started
222                     logger.debug("Broadcast started")
223                     self._result.set(True)
224
225                 elif self._recvbuffer.startswith(VlcMessage.response.STOPOK):
226                     # Broadcast stopped
227                     logger.debug("Broadcast stopped")
228                     self._result.set(True)