5 # Author: Cyril Deguet <asmax@via.ecp.fr>
7 # See: RFC 2326 Real Time Streaming Protocol
8 # RFC 2327 Session Description Protocol
11 import cfg, mimetools, re, socket, time, SocketServer, string, sys
13 from sap import SdpMessage
16 class RtspServerHandler(SocketServer.StreamRequestHandler):
17 "Request handler of the server socket"
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"
28 def error(self, message, cseq):
29 self.wfile.write(self.version + " " + message + "\r\n" + \
30 "Cseq: " + cseq + "\r\n" + \
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))
38 m = re.match('([^=]*)(?:=(.*))?', item)
41 result[m.group(1)] = m.group(2)
44 def optionsMethod(self):
45 "Handle an OPTION request"
46 response = "Public: OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, PING, TEARDOWN\r\n" + \
50 def pingMethod(self, msg):
51 "Handle a PING request"
52 cseq = msg.getheader('cseq')
53 id = msg.getheader('Session')
55 self.error(self.badRequest, cseq)
57 response = "Session: " + id + "\r\n" + \
61 def describeMethod(self, msg, uri):
62 "Handle a DESCRIBE request"
63 cseq = msg.getheader('cseq')
65 # Find the URI in the playlist
66 media = cfg.playlist.getMedia(uri)
68 self.error(self.uriNotFound, cseq)
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" + \
79 def setupMethod(self, msg, uri):
80 "Handle a SETUP request"
81 cseq = msg.getheader('cseq')
83 # Find the URI in the playlist
84 media = cfg.playlist.getMedia(uri)
86 self.error(self.uriNotFound, cseq)
89 transportHeader = msg.getheader('transport')
90 if transportHeader is None:
91 self.error(self.badRequest, cseq)
93 transport = self.parseHeader(transportHeader)
95 # Check the multicast/unicast fields in the headers
96 if transport.has_key('multicast'):
98 elif transport.has_key('unicast'):
101 self.error(self.invalidHeader, cseq)
104 # Check the destination field in the headers
106 if transport.has_key('destination'):
107 dest = transport['destination']
109 dest = media['addr'] # default destination address
111 id = cfg.sessionList.newSession(uri, dest)
113 self.error(self.internalError, cseq)
115 response = "Session: " + id + "\r\n" + \
116 "Transport: RTP/MP2T/UDP;" + type + ";destination=" + dest + "\r\n" + \
120 def playMethod(self, msg, uri):
121 "Handle a PLAY request"
122 cseq = msg.getheader('cseq')
124 # Find the URI in the playlist
125 media = cfg.playlist.getMedia(uri)
127 self.error(self.uriNotFound, cseq)
130 id = msg.getheader('Session')
131 session = cfg.sessionList.getSession(id)
133 self.error(self.sessionNotFound, cseq)
135 if session.play() < 0:
136 self.error(self.internalError, cseq)
138 response = "Session: " + id + "\r\n" + \
142 def pauseMethod(self, msg, uri):
143 "Handle a PAUSE request"
144 cseq = msg.getheader('cseq')
146 # Find the URI in the playlist
147 media = cfg.playlist.getMedia(uri)
149 self.error(self.uriNotFound, cseq)
152 id = msg.getheader('Session')
153 session = cfg.sessionList.getSession(id)
155 self.error(self.sessionNotFound, cseq)
157 if session.pause() < 0:
158 self.error(self.internalError, cseq)
160 response = "Session: " + id + "\r\n" + \
164 def teardownMethod(self, msg, uri):
165 "Handle a TEARDOWN request"
166 cseq = msg.getheader('cseq')
168 # Find the URI in the playlist
169 media = cfg.playlist.getMedia(uri)
171 self.error(self.uriNotFound, cseq)
174 id = msg.getheader('Session')
175 session = cfg.sessionList.getSession(id)
177 self.error(self.sessionNotFound, cseq)
179 if session.stop() < 0:
180 self.error(self.internalError, cseq)
182 if cfg.sessionList.delSession(id) < 0:
183 self.error(self.internalError, cseq)
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)
195 self.error(self.badRequest, "0")
199 # Get the message headers
200 msg = mimetools.Message(self.rfile, "0")
201 cseq = msg.getheader('CSeq')
203 self.error(self.badRequest, "0")
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)
222 self.error(self.notImplemented, cseq)
229 self.wfile.write(self.version + " " + self.ok + "\r\n" + \
230 "CSeq: " + cseq + "\r\n" + \
234 "Handle an incoming request"