]> git.sesse.net Git - vlc/blob - python/vlrs/rtsp.py
* string review.
[vlc] / python / vlrs / rtsp.py
1 #!/usr/bin/python
2 #
3 # VideoLAN RTSP Server
4 #
5 # Author: Cyril Deguet <asmax@via.ecp.fr>
6 #
7 # See: RFC 2326 Real Time Streaming Protocol
8 #      RFC 2327 Session Description Protocol
9
10
11 import cfg, mimetools, re, socket, time, SocketServer, string, sys
12
13 from sap import SdpMessage
14
15
16 class RtspServerHandler(SocketServer.StreamRequestHandler):
17     "Request handler of the server socket"
18     
19     version = "RTSP/1.0"
20     ok = "200 OK"
21     badRequest = "400 Bad request"
22     uriNotFound = "404 Not found"
23     sessionNotFound = "454 Session not found"
24     invalidHeader = "456 Header field not valid for resource"
25     internalError = "500 Internal server error"
26     notImplemented = "501 Not implemented"
27     
28     def error(self, message, cseq):
29         self.wfile.write(self.version + " " + message + "\r\n" + \
30                          "Cseq: " + cseq + "\r\n" + \
31                          "\r\n")
32
33     def parseHeader(self, header):
34         "Split a RTCP header into a mapping of parameters"
35         list = map(string.strip, re.split('[; \n]*', header, re.S))
36         result = {}
37         for item in list:
38             m = re.match('([^=]*)(?:=(.*))?', item)
39             if m is None:
40                 return None
41             result[m.group(1)] = m.group(2)
42         return result
43
44     def optionsMethod(self):
45         "Handle an OPTION request"
46         response = "Public: OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, PING, TEARDOWN\r\n" + \
47                    "\r\n"
48         return response
49
50     def pingMethod(self, msg):
51         "Handle a PING request"
52         cseq = msg.getheader('cseq')
53         id = msg.getheader('Session')
54         if id is None:
55             self.error(self.badRequest, cseq)
56             return
57         response = "Session: " + id + "\r\n" + \
58                    "\r\n"
59         return response
60
61     def describeMethod(self, msg, uri):
62         "Handle a DESCRIBE request"
63         cseq = msg.getheader('cseq')
64         
65         # Find the URI in the playlist
66         media = cfg.playlist.getMedia(uri)
67         if media is None:
68             self.error(self.uriNotFound, cseq)
69             return None
70
71         message = SdpMessage(media['name'], media['addr'], uri)
72         description = message.getMessage()
73         size = `len(description)`
74         response = "Content-Type: application/sdp\r\n" + \
75                    "Content-Length: " + size + "\r\n" + \
76                    "\r\n" + description
77         return response
78                          
79     def setupMethod(self, msg, uri):
80         "Handle a SETUP request" 
81         cseq = msg.getheader('cseq')
82         
83         # Find the URI in the playlist
84         media = cfg.playlist.getMedia(uri)
85         if media is None:
86             self.error(self.uriNotFound, cseq)
87             return None
88
89         transportHeader = msg.getheader('transport')
90         if transportHeader is None:
91             self.error(self.badRequest, cseq)
92             return None
93         transport = self.parseHeader(transportHeader)
94
95         # Check the multicast/unicast fields in the headers
96         if transport.has_key('multicast'):
97             type = "multicast"
98         elif transport.has_key('unicast'):
99             type = "unicast"
100         else:
101             self.error(self.invalidHeader, cseq)
102             return None
103             
104         # Check the destination field in the headers
105         dest= None
106         if transport.has_key('destination'):
107             dest = transport['destination']
108         if dest is None:
109             dest = media['addr']       # default destination address
110             
111         id = cfg.sessionList.newSession(uri, dest)
112         if id is None:
113             self.error(self.internalError, cseq)
114             return None
115         response = "Session: " + id + "\r\n" + \
116                    "Transport: RTP/MP2T/UDP;" + type + ";destination=" + dest + "\r\n" + \
117                    "\r\n"
118         return response
119
120     def playMethod(self, msg, uri):
121         "Handle a PLAY request"
122         cseq = msg.getheader('cseq')
123         
124         # Find the URI in the playlist
125         media = cfg.playlist.getMedia(uri)
126         if media is None:
127             self.error(self.uriNotFound, cseq)
128             return None
129
130         id = msg.getheader('Session')
131         session = cfg.sessionList.getSession(id)
132         if session is None:
133             self.error(self.sessionNotFound, cseq)
134             return None
135         if session.play() < 0:
136             self.error(self.internalError, cseq)
137             return None
138         response = "Session: " + id + "\r\n" + \
139                    "\r\n"
140         return response
141
142     def pauseMethod(self, msg, uri):
143         "Handle a PAUSE request"
144         cseq = msg.getheader('cseq')
145         
146         # Find the URI in the playlist
147         media = cfg.playlist.getMedia(uri)
148         if media is None:
149             self.error(self.uriNotFound, cseq)
150             return None
151             
152         id = msg.getheader('Session')
153         session = cfg.sessionList.getSession(id)
154         if session is None:
155             self.error(self.sessionNotFound, cseq)
156             return None
157         if session.pause() < 0:
158             self.error(self.internalError, cseq)
159             return None
160         response = "Session: " + id + "\r\n" + \
161                    "\r\n"
162         return response
163
164     def teardownMethod(self, msg, uri):
165         "Handle a TEARDOWN request"
166         cseq = msg.getheader('cseq')
167         
168         # Find the URI in the playlist
169         media = cfg.playlist.getMedia(uri)
170         if media is None:
171             self.error(self.uriNotFound, cseq)
172             return None
173             
174         id = msg.getheader('Session')
175         session = cfg.sessionList.getSession(id)
176         if session is None:
177             self.error(self.sessionNotFound, cseq)
178             return None
179         if session.stop() < 0:
180             self.error(self.internalError, cseq)
181             return None
182         if cfg.sessionList.delSession(id) < 0:
183             self.error(self.internalError, cseq)
184             return None
185         response = "\r\n"
186         return response
187
188     def parseRequest(self):
189         "Parse a RSTP request"
190         requestLine = self.rfile.readline()
191         m = re.match("(?P<method>[A-Z]+) (?P<uri>(\*|(?:(?P<protocol>rtsp|rtspu)://" + \
192                      "(?P<host>[^:/]*)(:(?P<port>\d*))?(?P<path>.*)))) " + \
193                      "RTSP/(?P<major>\d)\.(?P<minor>\d)", requestLine)
194         if m is None:
195             self.error(self.badRequest, "0")
196             return
197         uri = m.group('uri')
198         
199         # Get the message headers
200         msg = mimetools.Message(self.rfile, "0")
201         cseq = msg.getheader('CSeq')
202         if cseq is None:
203             self.error(self.badRequest, "0")
204             return
205             
206         method = m.group('method')
207         if method == 'OPTIONS':
208             response = self.optionsMethod()
209         elif method == 'DESCRIBE':
210             response = self.describeMethod(msg, uri)
211         elif method == 'SETUP':
212             response = self.setupMethod(msg, uri)
213         elif method == 'PLAY':
214             response = self.playMethod(msg, uri)
215         elif method == 'PAUSE':
216             response = self.pauseMethod(msg, uri)
217         elif method == 'PING':
218             response = self.pingMethod(msg)
219         elif method == 'TEARDOWN':
220             response = self.teardownMethod(msg, uri)
221         else:
222             self.error(self.notImplemented, cseq)
223             return
224  
225         # Send the response
226         if response is None:
227             return
228         else:
229             self.wfile.write(self.version + " " + self.ok + "\r\n" + \
230                              "CSeq: " + cseq + "\r\n" + \
231                              response)
232             
233     def handle(self):
234         "Handle an incoming request"
235         while 1:
236             try:
237                 self.parseRequest()
238             except IOError:
239                 return
240                          
241