Petite experimentation autour d'une Freebox
Par Puyb, vendredi 10 février 2006 à 14:36 :: General :: #19 :: rss
Hier soir, je n'arrivais pas à dormir... J'ai donc décidé de regarder la Freebox sur mon portable, en Wifi depuis mon lit...
Le problème en Wifi, c'est qu'il y a beaucoup trop de paquets perdus... et comme le flux est en UDP, ils ne sont pas retransmis... Donc, ça coupe tout le temps et on a toujours plein de gros carrés.
Je me suis donc mis en tête d'écrire un petit proxy RTSP vers HTTP. Mon postulat de départ est simple. En HTTP, grâce a une liaison TCP, la qualité devrait être meilleur, vu que les paquets perdus sont retransmis (en contrepartie, il faut un buffer pour pouvoir attendre que tout les paquet soient arrivé et dans l'ordre avant de les jouer, mais ça c'est le player qui s'en occupe...). D'autre part, le fait de disposer d'un flux http devrais permettre de lire le flux avec des players qui ne gèrent pas le RTSP (comme QuikTime Player ou des périphériques dédié). Enfin, je me suis dit que l'écriture d'un truc comme ça devait être simple est marrante...
J'ai donc commencé à essayer de le faire en C... Puis je me suis rappelé le nombre de lignes de code qu'il faut aligner avant de pouvoir mettre en place un socket, et aussi que la gestion des string était inexistante... J'ai donc décidé d'essayer d'écrire ça en python... Comme je viens de me mettre à ce langage, je me suis dit que ça pouvait être un bon exercice...
L'algorithme est plutôt simple :
Pour l'instant, VLC arrive à se connecter, le proxy se connecte à la Freebox et commence à répéter le flux... mais rien a ne s'affiche à l'écran...
Bon il était déjà 2h30 et maintenant, j’avais vraiment envie de dormir… Mission réussie… Je suis aller me coucher ;-)
J'ai deux piste à creuser pour essayer de faire marcher ça : Vérifier que toutes les données sont bien retransmises (problème de taille de buffer, ou de vitesse (paquets dropé) par exemple), ou vérifier si je peux bien renvoyer le flux brute de cette façon, et s'il ne faut pas le convertir et el remettre en forme...
Affaire à suivre ;-)
Update 12/02/2006 : J'ai débugé le script.
Update 21/02/2006 : Quelques modifications.
Le problème en Wifi, c'est qu'il y a beaucoup trop de paquets perdus... et comme le flux est en UDP, ils ne sont pas retransmis... Donc, ça coupe tout le temps et on a toujours plein de gros carrés.
Je me suis donc mis en tête d'écrire un petit proxy RTSP vers HTTP. Mon postulat de départ est simple. En HTTP, grâce a une liaison TCP, la qualité devrait être meilleur, vu que les paquets perdus sont retransmis (en contrepartie, il faut un buffer pour pouvoir attendre que tout les paquet soient arrivé et dans l'ordre avant de les jouer, mais ça c'est le player qui s'en occupe...). D'autre part, le fait de disposer d'un flux http devrais permettre de lire le flux avec des players qui ne gèrent pas le RTSP (comme QuikTime Player ou des périphériques dédié). Enfin, je me suis dit que l'écriture d'un truc comme ça devait être simple est marrante...
J'ai donc commencé à essayer de le faire en C... Puis je me suis rappelé le nombre de lignes de code qu'il faut aligner avant de pouvoir mettre en place un socket, et aussi que la gestion des string était inexistante... J'ai donc décidé d'essayer d'écrire ça en python... Comme je viens de me mettre à ce langage, je me suis dit que ça pouvait être un bon exercice...
L'algorithme est plutôt simple :
- On met en place un socket serveur pour attendre une connexion HTTP de la part du client
- On récupère l'url demandé par le client, et on lui renvois le header de réponse
- On se connecte à la Freebox, et on demande a jouer le flux demandé (via l'url)
- On met en place un socket UDP prêt à recevoir le flux de la Freebox
- Et enfin, on répète toutes les données envoyées sur le port UDP vers le client
import socket import sys # 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__(ip, port) self.cseq=1 def request(self, request): print "\n".join([">>> %s" % x for x in request.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) # Début du code # on met en place un serveur http http_listen=server_socket(("", 8082)) http_conn, addr = http_listen.accept() print 'Connected by', 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 http_conn.send("""HTTP/1.0 200 OK Content-type: application/octet-stream Cache-Control: no-cache """) # on établie la connexion rtsp rtsp_session=client_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: rtsp2http v0.0.1 """ % (url, "%d")) 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)) # 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: rtsp2http v0.0.1 """ % (url, "%d", session)) # on repete tout au client http ok=True while ok: data, addr=rtsp_data.recvfrom(1024) if addr[0]==socket.gethostbyname("mafreebox.freebox.fr"): try: http_conn.sendall(data) # il y a peut etre besoin d'une conversion... # mais j'espere pas... # a creuser except: ok=False # une execption... on arrete tout ! # une des connexion a été coupée, on ferme toutes les connexions... 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)) rtsp_session.close() rtsp_data.close() http_listen.close() http_conn.close()
Pour l'instant, VLC arrive à se connecter, le proxy se connecte à la Freebox et commence à répéter le flux... mais rien a ne s'affiche à l'écran...
Bon il était déjà 2h30 et maintenant, j’avais vraiment envie de dormir… Mission réussie… Je suis aller me coucher ;-)
J'ai deux piste à creuser pour essayer de faire marcher ça : Vérifier que toutes les données sont bien retransmises (problème de taille de buffer, ou de vitesse (paquets dropé) par exemple), ou vérifier si je peux bien renvoyer le flux brute de cette façon, et s'il ne faut pas le convertir et el remettre en forme...
Affaire à suivre ;-)
Update 12/02/2006 : J'ai débugé le script.
Update 21/02/2006 : Quelques modifications.
Commentaires
1. Le samedi 31 mai 2008 à 23:45, par flux rss
2. Le mardi 27 avril 2010 à 10:07, par jerome
3. Le mardi 27 avril 2010 à 14:48, par Puyb
Ajouter un commentaire