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