# Proxy RTSP vers HTTP v0.0.2
# (c) Puyb 2006
# http://www.puyb.net
#
# changelog :
# v0.0.2 - correction de la taille des paquets UDP lus
#        - suppression de l'entte RTP de 12 octets avant envoi du paquet en HTTP
# v0.0.1 - version initiale

import socket
import sys
import thread
import time

_version="0.0.3"
_server_string="rtsp2http proxy %s" % _version

# une petire class de serveur qui me simplifie un petit peu le travail
class server_socket(socket.socket):
	def __init__(self, arg):
		if not type(arg)==tuple:
			self.socket=arg
		else:
			self.socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			self.socket.bind(arg)
			self.socket.listen(1)
	
	def accept(self):
		s, addr=self.socket.accept()
		return server_socket(s), addr
	
	def readline(self):
		"""receive and return a complete line"""
		line=""
		while 1:
			char = self.recv(1)
			if not char:
				if line=="":
					return False
				return line
			if char=="\n":
				return line
			if char!="\r":
				line += char
	
	def __getattr__(self, name):
		return getattr(self.socket, name)

# une petite class de client.. la même que pour un serveur, sauf l'initialisation
class client_socket(server_socket):
	def __init__(self, ip, port):
		self.socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.socket.connect((ip,port))

# une class qui essaye de comprendre les réponses au requettes RTSP
class basic_rtsp_socket(client_socket):
	def __init__(self, ip, port):
		client_socket.__init__(self, ip, port)
		self.cseq=1
		
	def request(self, request):
		#print "\n".join([">>> %s" % x for x in (request % self.cseq).split("\n")])
		self.send(request % self.cseq)
		self.cseq+=1
		l=self.readline()
		#print "<<< %s" % l
		(proto, code)=l.split(" ")[0:2]
		if proto[0:4]!="RTSP": raise "Error: not a rtsp response", l
		if code!="200": raise "Error: rtsp error", l
		
		l=self.readline()
		#print "<<< %s" % l
		header={}
		while l!="" and l!=None:
			l2=l.split(": ")
			if len(l2)!=2: raise "Incorrect response", l
			header[l2[0]]=l2[1]
			l=self.readline()
			#print "<<< %s" % l
		if l==None: raise "rtsp connection closed by foreign host", None
		data=None
		if "Content-length" in header.keys():
			data=read(int(header["Content-length"]))
		return (header, data)


def httpthread(http_conn, addr):
	data = http_conn.readline()
	if not data: raise "no data on http line", None
	# on prend la première ligne
	(request, url, proto)=data.split(" ")[0:3]
	if request!="GET":
		raise "Error : not a get request", data
	if proto[0:4]!="HTTP":
		raise "Error : not a http request", data
	# on attend la fin de la requette
	# soit une ligne vide
	# un peu barbare... mais je suis qur qu'il ren a dire d'interressant
	l=http_conn.readline()
	#print "<<< %s" %l
	while l!="" and l!=None:
		l=http_conn.readline()
		#print "<<< %s" % l
		# parle a ma main !!!
	if l==None: raise "http connection closed", None
	
	# on repond le header
	reponse="""HTTP/1.0 200 OK
Content-type: application/octet-stream
Cache-Control: no-cache

"""
	#print reponse
	http_conn.send(reponse)
	
	reconnect=True
	while reconnect:
		print "starting RTSP connection at %f" % time.clock()
		# on établie la connexion rtsp
		rtsp_session=basic_rtsp_socket("mafreebox.freebox.fr", 554)
		#print "connected to the rtsp session"
		# dump du dialogue RTSP entre VLC et la freebox...
		(header, data)=rtsp_session.request("""OPTIONS rtsp://mafreebox.freebox.fr/freeboxtv%s RTSP/1.0
CSeq: %s
User-Agent: rtsp2http v0.0.1

""" % (url, "%d"))
		(header, data)=rtsp_session.request("""SETUP rtsp://212.27.38.253/freeboxtv%s RTSP/1.0
CSeq: %s
Transport: RTP/AVP;unicast;client_port=1662-1663
User-Agent: %s

""" % (url, "%d", _version))
		session=header["Session"]
		# on se prépare a recevoir les connexion UDP
		rtsp_data = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
		rtsp_data.bind(("", 1662))
		rtsp_data.settimeout(1.0)
		# Go, go, go
		(header, data)=rtsp_session.request("""PLAY rtsp://mafreebox.freebox.fr/freeboxtv%s RTSP/1.0
CSeq: %s
Session: %s
Range: npt=0.000-
User-Agent: %s

""" % (url, "%d", session, _version))
		
		# on repete tout au client http
		ok=True
		while ok:
			# on lit 2k... Si le datagram est plus petit, on aura moins de données (FIXME : lire la MTU d'un paquet UDP)
			try:
				data, addr=rtsp_data.recvfrom(2048)
			except:
				ok=False
				print "RTSP TimeOut : retrying"
			if addr[0]==socket.gethostbyname("mafreebox.freebox.fr"):
				try:
					http_conn.sendall(data[12:])
					# on envoi la paylaod... C'est a dire, le datgram moins les 12 octets d'entête
				except:
					ok=False # une execption... on arrete tout !
					reconnect=False
		
		
		# une des connexion a été coupée, on ferme toutes les connexions...
		try:
			rtsp_session.request("""TEARDOWN rtsp://mafreebox.freebox.fr/freeboxtv%s RTSP/1.0
CSeq: %s
Session: %s
User-Agent: rtsp2http v0.0.1

""" % (url, "%d", session))
		except:
			print "Exception on TEARDOWN, skiping"
		rtsp_session.close()
		rtsp_data.close()

	http_listen.close()
	http_conn.close()
	print "%s disconnected" % (addr, )

# Début du code

# on met en place un serveur http
http_listen=server_socket(("", 8082))
while True:
	http_conn, addr = http_listen.accept()
	print 'Connected by', addr
	thread.start_new_thread(httpthread, (http_conn, addr))


