--- /dev/null
+#include <Python.h>
+#include <vlc/vlc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+static PyObject *vlc_create(PyObject *self, PyObject *args)
+{
+ int iRc;
+
+ iRc = VLC_Create();
+ return Py_BuildValue("i", iRc);
+}
+
+
+static PyObject *vlc_init(PyObject *self, PyObject *args)
+{
+ int iVlc;
+ char *file;
+ char *pArgv[] = { "vlc", "--sout", NULL };
+ int iRc;
+
+ if (!PyArg_ParseTuple(args, "iss", &iVlc, &file, &pArgv[2]))
+ return NULL;
+ iRc = VLC_Init(iVlc, 3, pArgv);
+ if (iRc >= 0)
+ iRc = VLC_AddTarget(iVlc, file, PLAYLIST_APPEND, PLAYLIST_END);
+ return Py_BuildValue("i", iRc);
+}
+
+
+static PyObject *vlc_play(PyObject *self, PyObject *args)
+{
+ int iVlc;
+ int iRc;
+
+ if (!PyArg_ParseTuple(args, "i", &iVlc))
+ return NULL;
+ iRc = VLC_Play(iVlc);
+ return Py_BuildValue("i", iRc);
+}
+
+
+static PyObject *vlc_stop(PyObject *self, PyObject *args)
+{
+ int iVlc;
+ int iRc;
+
+ if (!PyArg_ParseTuple(args, "i", &iVlc))
+ return NULL;
+ iRc = VLC_Stop(iVlc);
+ return Py_BuildValue("i", iRc);
+}
+
+
+static PyObject *vlc_pause(PyObject *self, PyObject *args)
+{
+ int iVlc;
+ int iRc;
+
+ if (!PyArg_ParseTuple(args, "i", &iVlc))
+ return NULL;
+ iRc = VLC_Pause(iVlc);
+ return Py_BuildValue("i", iRc);
+}
+
+
+static PyMethodDef VlcMethods[] = {
+ {"create", vlc_create, METH_VARARGS, "Create a vlc thread."},
+ {"init", vlc_init, METH_VARARGS, "Initialize a vlc thread."},
+ {"play", vlc_play, METH_VARARGS, "Play"},
+ {"stop", vlc_stop, METH_VARARGS, "Stop"},
+ {"pause", vlc_pause, METH_VARARGS, "Pause"},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+
+void initvlc(void)
+{
+ Py_InitModule("vlc", VlcMethods);
+}
+
--- /dev/null
+#!/usr/bin/python
+#
+# VideoLAN RTSP Server
+#
+# Author: Cyril Deguet <asmax@via.ecp.fr>
+#
+# See: RFC 2326 Real Time Streaming Protocol
+# RFC 2327 Session Description Protocol
+
+
+import cfg, mimetools, re, socket, time, SocketServer, string, sys
+
+
+def ntpTime():
+ "return the current time in NTP decimal format"
+ return "%d" % (int(time.time()) + 2208988800L)
+
+
+
+class SdpMessage:
+ "Build a SDP message"
+
+ uri = "http://www.videolan.org"
+
+ def __init__(self, sessionName, address, uri):
+ "Build the message"
+ self.sessionName = sessionName
+ self.address = address
+ self.uri = uri
+
+ def getMessage(self):
+ "Return the SDP message"
+ msg = "v=0\r\n" + \
+ "o=asmax " + ntpTime() + " " + ntpTime() + \
+ " IN IP4 sphinx.via.ecp.fr\r\n" + \
+ "s=" + self.sessionName + "\r\n" + \
+ "u=" + self.uri + "\r\n" + \
+ "t=0 0\r\n" + \
+ "c=IN IP4 " + self.address + "/1\r\n" + \
+ "m=video 1234 RTP/MP2T 33\r\n" + \
+ "a=control:" + self.uri + "\r\n"
+ return msg
+
+
+
+class RtspServerHandler(SocketServer.StreamRequestHandler):
+ "Request handler of the server socket"
+
+ version = "RTSP/1.0"
+ ok = "200 OK"
+ badRequest = "400 Bad Request"
+ uriNotFound = "404 Not found"
+ sessionNotFound = "454 Session Not Found"
+ invalidHeader = "456 Header Field Not Valid for Resource"
+ internalError = "500 Internal Server Error"
+ notImplemented = "501 Not Implemented"
+
+ def error(self, message, cseq):
+ self.wfile.write(self.version + " " + message + "\r\n" + \
+ "Cseq: " + cseq + "\r\n" + \
+ "\r\n")
+
+ def parseHeader(self, header):
+ "Split a RTCP header into a mapping of parameters"
+ list = map(string.strip, re.split('[; \n]*', header, re.S))
+ result = {}
+ for item in list:
+ m = re.match('([^=]*)(?:=(.*))?', item)
+ if m is None:
+ return None
+ result[m.group(1)] = m.group(2)
+ return result
+
+ def optionsMethod(self):
+ "Handle an OPTION request"
+ response = "Public: OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, PING, TEARDOWN\r\n" + \
+ "\r\n"
+ return response
+
+ def pingMethod(self, msg):
+ "Handle an PING request"
+ cseq = msg.getheader('cseq')
+ id = msg.getheader('Session')
+ if id is None:
+ self.error(self.badRequest, cseq)
+ return
+ response = "Session: " + id + "\r\n" + \
+ "\r\n"
+ return response
+
+ def describeMethod(self, msg, uri):
+ "Handle a DESCRIBE request"
+ cseq = msg.getheader('cseq')
+
+ # Find the URI in the playlist
+ media = cfg.playlist.getMedia(uri)
+ if media is None:
+ self.error(self.uriNotFound, cseq)
+ return None
+
+ message = SdpMessage(media['name'], media['addr'], uri)
+ description = message.getMessage()
+ size = `len(description)`
+ response = "Content-Type: application/sdp\r\n" + \
+ "Content-Length: " + size + "\r\n" + \
+ "\r\n" + description
+ return response
+
+ def setupMethod(self, msg, uri):
+ "Handle a SETUP request"
+ cseq = msg.getheader('cseq')
+
+ # Find the URI in the playlist
+ media = cfg.playlist.getMedia(uri)
+ if media is None:
+ self.error(self.uriNotFound, cseq)
+ return None
+
+ transportHeader = msg.getheader('transport')
+ if transportHeader is None:
+ self.error(self.badRequest, cseq)
+ return None
+ transport = self.parseHeader(transportHeader)
+
+ # Check the multicast/unicast fields in the headers
+ if transport.has_key('multicast'):
+ type = "multicast"
+ elif transport.has_key('unicast'):
+ type = "unicast"
+ else:
+ self.error(self.invalidHeader, cseq)
+ return None
+
+ # Check the destination field in the headers
+ dest= None
+ if transport.has_key('destination'):
+ dest = transport['destination']
+ if dest is None:
+ dest = media['addr'] # default destination address
+
+ id = cfg.sessionList.newSession(uri, dest)
+ if id is None:
+ self.error(self.internalError, cseq)
+ return None
+ response = "Session: " + id + "\r\n" + \
+ "Transport: RTP/MP2T/UDP;" + type + ";destination=" + dest + "\r\n" + \
+ "\r\n"
+ return response
+
+ def playMethod(self, msg, uri):
+ "Handle a PLAY request"
+ cseq = msg.getheader('cseq')
+
+ # Find the URI in the playlist
+ media = cfg.playlist.getMedia(uri)
+ if media is None:
+ self.error(self.uriNotFound, cseq)
+ return None
+
+ id = msg.getheader('Session')
+ session = cfg.sessionList.getSession(id)
+ if session is None:
+ self.error(self.sessionNotFound, cseq)
+ return None
+ if session.play() < 0:
+ self.error(self.internalError, cseq)
+ return None
+ response = "Session: " + id + "\r\n" + \
+ "\r\n"
+ return response
+
+ def pauseMethod(self, msg, uri):
+ "Handle a PAUSE request"
+ cseq = msg.getheader('cseq')
+
+ # Find the URI in the playlist
+ media = cfg.playlist.getMedia(uri)
+ if media is None:
+ self.error(self.uriNotFound, cseq)
+ return None
+
+ id = msg.getheader('Session')
+ session = cfg.sessionList.getSession(id)
+ if session is None:
+ self.error(self.sessionNotFound, cseq)
+ return None
+ if session.pause() < 0:
+ self.error(self.internalError, cseq)
+ return None
+ response = "Session: " + id + "\r\n" + \
+ "\r\n"
+ return response
+
+ def teardownMethod(self, msg, uri):
+ "Handle a TEARDOWN request"
+ cseq = msg.getheader('cseq')
+
+ # Find the URI in the playlist
+ media = cfg.playlist.getMedia(uri)
+ if media is None:
+ self.error(self.uriNotFound, cseq)
+ return None
+
+ id = msg.getheader('Session')
+ session = cfg.sessionList.getSession(id)
+ if session is None:
+ self.error(self.sessionNotFound, cseq)
+ return None
+ if session.stop() < 0:
+ self.error(self.internalError, cseq)
+ return None
+ if cfg.sessionList.delSession(id) < 0:
+ self.error(self.internalError, cseq)
+ return None
+ response = "\r\n"
+ return response
+
+ def parseRequest(self):
+ "Parse a RSTP request"
+ requestLine = self.rfile.readline()
+ m = re.match("(?P<method>[A-Z]+) (?P<uri>(\*|(?:(?P<protocol>rtsp|rtspu)://" + \
+ "(?P<host>[^:/]*)(:(?P<port>\d*))?(?P<path>.*)))) " + \
+ "RTSP/(?P<major>\d)\.(?P<minor>\d)", requestLine)
+ if m is None:
+ self.error(self.badRequest, "0")
+ return
+ uri = m.group('uri')
+
+ # Get the message headers
+ msg = mimetools.Message(self.rfile, "0")
+ cseq = msg.getheader('CSeq')
+ if cseq is None:
+ self.error(self.badRequest, "0")
+ return
+
+ method = m.group('method')
+ if method == 'OPTIONS':
+ response = self.optionsMethod()
+ elif method == 'DESCRIBE':
+ response = self.describeMethod(msg, uri)
+ elif method == 'SETUP':
+ response = self.setupMethod(msg, uri)
+ elif method == 'PLAY':
+ response = self.playMethod(msg, uri)
+ elif method == 'PAUSE':
+ response = self.pauseMethod(msg, uri)
+ elif method == 'PING':
+ response = self.pingMethod(msg)
+ elif method == 'TEARDOWN':
+ response = self.teardownMethod(msg, uri)
+ else:
+ self.error(self.notImplemented, cseq)
+ return
+
+ # Send the response
+ if response is None:
+ return
+ else:
+ self.wfile.write(self.version + " " + self.ok + "\r\n" + \
+ "CSeq: " + cseq + "\r\n" + \
+ response)
+
+ def handle(self):
+ "Handle an incoming request"
+ while 1:
+ try:
+ self.parseRequest()
+ except IOError:
+ return
+
+
--- /dev/null
+#!/usr/bin/python -O
+#
+# VideoLAN RTSP Server
+#
+# Author: Cyril Deguet <asmax@via.ecp.fr>
+
+
+import cfg, random, time
+
+from streamer import VlcError, VlcStreamer
+
+
+class Session:
+ "RTSP Session"
+
+ def __init__(self, id, uri, dest):
+ self.id = id
+ self.uri = uri
+ self.dest = dest
+ self.state = 'ready'
+ media = cfg.playlist.getMedia(self.uri)
+ self.fileName = media['file']
+ address = "rtp/ts://" + dest
+ self.streamer = VlcStreamer(self.fileName, address)
+
+ def play(self):
+ "Play this session"
+ if self.state == 'playing':
+ print "Session " + self.id + " (" + self.fileName + "): already playing"
+ return 0
+ self.state = 'playing'
+ print "Session " + self.id + " (" + self.fileName + "): play"
+ try:
+ self.streamer.play()
+ except VlcError:
+ print "Streamer: play failed"
+ return -1
+ return 0
+
+ def pause(self):
+ "Pause this session"
+ print "Session " + self.id + " (" + self.fileName + "): pause"
+ self.state = 'ready'
+ try:
+ self.streamer.pause()
+ except VlcError:
+ print "Streamer: pause failed"
+ return -1
+ return 0
+
+ def stop(self):
+ "Stop this session"
+ print "Session " + self.id + " (" + self.fileName + "): stop"
+ try:
+ self.streamer.stop()
+ except VlcError:
+ print "Streamer: stop failed"
+ return -1
+ return 0
+
+
+
+class SessionList:
+ "Manages RTSP sessions"
+
+ list = {}
+ chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+ def __init__(self):
+ self.rand = random.Random(time.time())
+
+ def newSessionId(self):
+ "Build a random session id"
+ id = ""
+ for x in range(12):
+ id += self.chars[self.rand.randrange(0, len(self.chars), 1)]
+ return id
+
+ def newSession(self, uri, dest):
+ "Create a new RTSP session"
+ id = self.newSessionId()
+ while self.list.has_key(id):
+ id = self.newSessionId()
+ try:
+ session = Session(id, uri, dest)
+ except VlcError:
+ print "Streamer: creation failed"
+ return None
+ self.list[id] = session
+ print "New session: " + id
+ return id
+
+ def getSession(self, id):
+ "Get a session from its session id"
+ if self.list.has_key(id):
+ return self.list[id]
+ else:
+ return None
+
+ def delSession(self, id):
+ "Delete a session"
+ if self.list.has_key(id):
+ del self.list[id]
+ return 0
+ else:
+ return -1
+
+