69d9fb92f00507d2b71604ac833d04a4bf741049
[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 = logging.getLogger("VlcClient_startBroadcast")
157         logger.debug("Starting broadcast......")
158         return self._broadcast(True, stream_name, input, muxer, pre_access, qtype)
159
160     def stopBroadcast(self, stream_name):
161         return self._broadcast(False, stream_name)
162
163     def pauseBroadcast(self, stream_name):
164         return self._write(VlcMessage.request.pauseBroadcast(stream_name))
165
166     def playBroadcast(self, stream_name):
167         return self._write(VlcMessage.request.playBroadcast(stream_name))
168
169     def _recvData(self):
170         # Logger
171         logger = logging.getLogger("VlcClient_recvData")
172
173         while True:
174             gevent.sleep()
175             try:
176                 self._recvbuffer = self._socket.read_until("\n")
177                 # Stripping "> " from VLC
178                 self._recvbuffer = self._recvbuffer.lstrip("> ")
179             except:
180                 # If something happened during read, abandon reader
181                 if not self._shuttingDown.isSet():
182                     logger.error("Exception at socket read")
183                     self._shuttingDown.set()
184                 return
185
186             # Parsing everything only if the string is not empty
187             if self._recvbuffer:
188                 if not self._vlcver:
189                     # First line (VLC version)
190                     self._vlcver = self._recvbuffer.strip()
191                     # Send password here since PASSWORD doesn't have \n
192                     self._write(self._password)
193
194                 elif self._recvbuffer.startswith(VlcMessage.response.SHUTDOWN):
195                     # Exit from this loop
196                     logger.debug("Got SHUTDOWN from VLC")
197                     return
198
199                 elif self._recvbuffer.startswith(VlcMessage.response.WRONGPASS):
200                     # Wrong password
201                     logger.error("Wrong VLC password!")
202                     self._auth.set(False)
203                     return
204
205                 elif self._recvbuffer.startswith(VlcMessage.response.AUTHOK):
206                     # Authentication OK
207                     logger.info("Authentication successful")
208                     self._auth.set(True)
209
210                 elif VlcMessage.response.BROADCASTEXISTS in self._recvbuffer:
211                     # Broadcast already exists
212                     logger.error("Broadcast already exists!")
213                     self._result.set(False)
214
215                 elif VlcMessage.response.STOPERR in self._recvbuffer:
216                     # Media unknown (stopping non-existent stream)
217                     logger.error("Broadcast does not exist!")
218                     self._result.set(False)
219
220                 # Do not move this before error handlers!
221                 elif self._recvbuffer.startswith(VlcMessage.response.STARTOK):
222                     # Broadcast started
223                     logger.debug("Broadcast started")
224                     self._result.set(True)
225
226                 elif self._recvbuffer.startswith(VlcMessage.response.STOPOK):
227                     # Broadcast stopped
228                     logger.debug("Broadcast stopped")
229                     self._result.set(True)