# -*- coding: utf-8 -*-
# Proxy RTSP vers HTTP v0.0.4
# (c) Puyb, AlexSoloex 2006
# http://www.puyb.net
#
# Ce programme est distribué sous les termes la licence GPL v2 de la Free Software Fondation
# This software is distributed under the terms of the Free Software Fondation's  GPL v2 licence
# Vous pouvez obtenir une copie de la licence a l'adresse :
# You can find a copy of the licence at :
# http://www.puyb.net/download/LICENCE-GPL2.txt
#
# changelog :
# v0.0.4 - Meilleur gestion du protocoles RTSP... Envois du paquet de réponse pour eviter les deconnexions
#          Par AlexSolex
# v0.0.3 - reconnexion automatique du flux rtsp
# v0.0.2 - correction de la taille des paquets UDP lus
#        - suppression de l'entÂte 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.4"
_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
                self.RTCP_port=None
        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
                        print "   %s : %s"%(l2[0],l2[1])
                        header[l2[0]]=l2[1]
                        if l2[0]=="Transport":
                            prm_transport=l2[1].split(";")
                            for prms in prm_transport:
                                try:
                                    print prms
                                    key,val=prms.split("=")
                                    if key=="server_port":
                                        self.RTCP_port=int(val.split("-")[1])
                                        break

                                except:
                                    pass

                        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 char2bits(txt):
    bits = []
    for char in txt:
            if type(char) == str:char = ord(char)
            #print char,
            for i in range(8):
                    j = 2**(7-i)
                    bits.append(int(char - j >= 0))
                    char = char % j
    print len(bits)
    print bits



def bits2char(bits):
    char = 0
    for b in range(8):
        if bits[7-b] == 1:
            char += 2**b
    return char


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":
                print "ATTENTION : le player n'a pas envoyé de 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: %s
##
##""" % (url, "%d", _version))
                #print "HEADER OPTIONS: \n%s"%header
                #print "DATA OPTIONS: \n%s"%data
                (header, data)=rtsp_session.request("""SETUP rtsp://212.27.38.253/freeboxtv%s RTSP/1.0
CSeq: %s
Transport: RTP/AVP;unicast;client_port=31330-31331
User-Agent: %s

""" % (url, "%d", _version))
                #print "HEADER SETUP: \n%s"%header
                #print "DATA SETUP: \n%s"%data
                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(("", 31330))
                rtsp_data.settimeout(0.1)
                #préparation du control de flux
                rtsp_control = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
                rtsp_control.bind(("",31331))
                rtsp_control.connect(("mafreebox.freebox.fr",rtsp_session.RTCP_port))
                # Go, go, go
                (header, data)=rtsp_session.request("""PLAY rtsp://mafreebox.freebox.fr/freeboxtv%s RTSP/1.0
CSeq: %s
Session: %s
Range: npt="now"
User-Agent: %s

""" % (url, "%d", session, _version))
                #print "HEADER PLAY: \n%s"%header
                #print "DATA PLAY: \n%s"%header
                # on repete tout au client http
                # on met des datas en mémoire
                ok=True
                t=time.clock()
                while ok:
                        if time.clock()-t>5:
                                sheader=""
                                for key in header.keys():
                                        sheader+="%s: %s\n"%(key,header[key])
                                        sheader+="\n"
                                rtsp_control.send(sheader)
                                t=time.clock()
                        # 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:
                                        #m=data[:12]
                                        #char2bits(m)
                                        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 !
                                        print "exception lors de l'envoi des données en http"
                                        reconnect=False


                # une des connexion a été coupée, on ferme toutes les connexions...
                try:
                        print 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))
                        print "HEADER TEARDOWN: \n%s"%header
                except:
                        print "Exception on TEARDOWN, skiping"
                        print "%s"%url
                rtsp_session.close()
                rtsp_control.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(("", 8083))
while True:
        http_conn, addr = http_listen.accept()
        print 'Connected by', addr
        thread.start_new_thread(httpthread, (http_conn, addr))