2015-09-11 1 views
1

J'ai un client vidéo qui peut uniquement joindre un flux vidéo via une requête rtsp - il est incapable de consommer directement un fichier SDP.Proxy RTSP pour les fichiers SDP

Cependant, les sources vidéo multicast dont il a besoin pour voir ne pas soutenir rtsp ....

En fin de compte toutes une demande RTSP fait fournit un machanism pour le SDP à retourner au client ... donc J'ai essayé de trouver une solution qui me permettrait de faire une demande rtsp du client à un serveur proxy quelconque, et dépendant de l'URI utilisé ce serveur retournerait alors le SDP approprié en réponse à une demande DESCRIBE. Cela me permettrait de lire les sources vidéo, en dépit du fait que le client ne peut demander la vidéo via RTSP ....

Cela semble simple, mais je n'ai pas réussi à trouver une façon de le faire. Des idées?

+0

Pourquoi implémenter tout simplement pas le serveur RTSP factice avec des réponses codées en dur, si c'est tout ce que vous avez besoin à savoir sur les options retourner une chaîne d'options codées en dur, etc. Vous pouvez voir quelles réponses sont requises en diffusant une session de multidiffusion à l'aide du serveur RTSP live555. – Ralf

Répondre

0

C'est ce que j'ai fini par faire; le dessous est écrit en Python. Il ne gagnera pas de concours de beauté, et je m'attends à ce qu'il y ait de nombreuses façons de briser ... mais ça marche pour un premier essai. Il récupère le fichier SDP à partir de (dans ce cas) une URL, puis répond aux demandes RTSP faites à son encontre, en fournissant le fichier SDP au client. Le SDP requis peut être spécifié comme un paramètre à l'URI RTSP:

#! /usr/bin/python2.6 

# 
# To use this, make an rtsp call of the form rtsp://<ip_address>/<camera_id> 
# 

import socket 
import time 
import re 
import sys 
from thread import * 
from random import randint 
import urllib2 

HOST = '' # Any interface 
PORT = 554 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
print "#### Socket created" 

#Bind socket. Loop until success... 
while True: 
    try: 
     print "Attempting to bind to "+str(PORT) 
     s.bind((HOST, PORT)) 
    except socket.error as msg: 
     print "Bind failed. Error Code : "+ msg[1] 
     time.sleep(10) 
    else: 
     break 
print 'Socket bind complete' 

#Start listening on socket 
s.listen(5) 

print "#### Listening for RTSP calls on port "+str(PORT) 

#Function for handling connections. This will be used to create threads 
def player_thread(conn): 
    data='' 
    total_data=[] 

    sid=randint(1,100000) 

    ####################################################### 
    # This loop is for the duration of the RTSP connection 
    ####################################################### 
    while True: 
     ########################################################################## 
     ########################################################################## 
     # 
     # Receive RTSP message 
     # 
     ########################################################################## 
     ########################################################################## 

     try: 
      conn.settimeout(1) 
      data = conn.recv(1024) 
      if data: 
       total_data.append(data) 
     except: 
      pass 

     rtsp_string=''.join(total_data) 
     total_data=[] 

     ########################################################################## 
     ########################################################################## 
     # 
     # Process incoming messages and respond accordingly 
     # 
     ########################################################################## 
       ########################################################################## 

     if rtsp_string.startswith("DESCRIBE"): 
      try: 
           cam 
         except NameError: 
           p="DESCRIBE[ ]*rtsp://([^/]+)/([^ ]+)" 
           m = re.search(p, rtsp_string) 
           cam=m.group(2) 

      print "DESCRIBE RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Content-type: application/sdp\r\n" 

      sdp=get_sdp(cam) 

      print sdp+"\n\r" 

      sdp+="\r\n" 

      resp+="Content-Length: "+str(len(sdp))+"\r\n" 
      resp+="\r\n" 
      resp+=sdp 

     ############################################################################ 

     elif rtsp_string.startswith("OPTIONS"): 
      try: 
           cam 
         except NameError: 
           p="OPTIONS[ ]*rtsp://([^/]+)/([^ ]+)" 
           m = re.search(p, rtsp_string) 
           cam=m.group(2) 

      print "OPTIONS RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Public: DESCRIBE, OPTIONS, PLAY, SETUP, TEARDOWN\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("SETUP"): 
      print "SETUP RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 
      type=get_type(rtsp_string) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Session: "+str(sid)+"\r\n" 
      resp+=type+"\r\n" 
      resp+="Accept-Ranges: NPT\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("PLAY"): 
      print "PLAY RECEIVED" 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Session: "+str(sid)+"\r\n" 
      resp+="Range: npt=0.0-\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("TEARDOWN"): 
      print "TEARDOWN RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="\r\n" 
      conn.send(resp) 
      print "##### PLAYBACK STOPPED FOR "+cam+" #####" 
      break; 

     ############################################################################ 
     # 
     # Send our response to the RTSP message (assuming connection still open) 
     # 
     ############################################################################ 

     if resp != "": 
      try: 
       conn.send(resp) 
      except: 
       print "##### PLAYBACK STOPPED FOR "+cam+" #####" 
       break; 



############################################################################################### 
############################################################################################### 
# Various worker functions to parse the incoming messages, grab SDP info, and the like... 

def get_type(string): 
    p="(Transport.*)" 
    m = re.search(p, string) 
    if m: 
       type=m.group(1) 
       return type 

     return -1 

def get_cseq(string): 
    p="CSeq:[ ]+([0-9]+)" 
    m = re.search(p, string) 
    if m: 
     cseq=m.group(1) 
     return cseq 

    return -1 

def get_sdp(cam): 
     url="<wherever your SDP file lives>?cam" 
     sdp=urllib2.urlopen(url).read(1000) 
     sdp=sdp.strip() 
     return sdp 


##################################################################################################### 
# Main program loop. Sit here waiting for incoming fonnections and creating threads as required 
# to service them 
##################################################################################################### 

while True: 
    conn, addr = s.accept() 
    print '##### NEW CONNECTION FOR VIDEO RECEIVED FROM ' + addr[0] 
    start_new_thread(player_thread ,(conn,)) 
s.close() 


     s.bind((HOST, PORT)) 
    except socket.error as msg: 
     print "Bind failed. Error Code : "+ msg[1] 
     time.sleep(10) 
    else: 
     break 
print 'Socket bind complete' 

#Start listening on socket 
s.listen(5) 

print "#### Listening for RTSP calls on port "+str(PORT) 

#Function for handling connections. This will be used to create threads 
def player_thread(conn): 
    data='' 
    total_data=[] 

    sid=randint(1,100000) 

    ####################################################### 
    # This loop is for the duration of the RTSP connection 
    ####################################################### 
    while True: 
     ########################################################################## 
     ########################################################################## 
     # 
     # Receive RTSP message 
     # 
     ########################################################################## 
     ########################################################################## 

     try: 
      conn.settimeout(1) 
      data = conn.recv(1024) 
      if data: 
       total_data.append(data) 
     except: 
      pass 

     rtsp_string=''.join(total_data) 
     total_data=[] 

     ########################################################################## 
     ########################################################################## 
     # 
     # Process incoming messages and respond accordingly 
     # 
     ########################################################################## 
       ########################################################################## 

     if rtsp_string.startswith("DESCRIBE"): 
      try: 
           cam 
         except NameError: 
           p="DESCRIBE[ ]*rtsp://([^/]+)/([^ ]+)" 
           m = re.search(p, rtsp_string) 
           cam=m.group(2) 

      print "DESCRIBE RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Content-type: application/sdp\r\n" 

      sdp=get_sdp(cam) 

      print sdp+"\n\r" 

      sdp+="\r\n" 

      resp+="Content-Length: "+str(len(sdp))+"\r\n" 
      resp+="\r\n" 
      resp+=sdp 

     ############################################################################ 

     elif rtsp_string.startswith("OPTIONS"): 
      try: 
           cam 
         except NameError: 
           p="OPTIONS[ ]*rtsp://([^/]+)/([^ ]+)" 
           m = re.search(p, rtsp_string) 
           cam=m.group(2) 

      print "OPTIONS RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Public: DESCRIBE, OPTIONS, PLAY, SETUP, TEARDOWN\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("SETUP"): 
      print "SETUP RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 
      type=get_type(rtsp_string) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Session: "+str(sid)+"\r\n" 
      resp+=type+"\r\n" 
      resp+="Accept-Ranges: NPT\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("PLAY"): 
      print "PLAY RECEIVED" 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="Session: "+str(sid)+"\r\n" 
      resp+="Range: npt=0.0-\r\n" 
      resp+="\r\n" 

     ############################################################################ 

     elif rtsp_string.startswith("TEARDOWN"): 
      print "TEARDOWN RECEIVED FOR "+cam 
      print rtsp_string 
      cseq=str(get_cseq(rtsp_string)) 

      resp ="RTSP/1.0 200 OK\r\n" 
      resp+="CSeq: "+cseq+"\r\n" 
      resp+="\r\n" 
      conn.send(resp) 
      print "##### PLAYBACK STOPPED FOR "+cam+" #####" 
      break; 

     ############################################################################ 
     # 
     # Send our response to the RTSP message (assuming connection still open) 
     # 
     ############################################################################ 

     if resp != "": 
      try: 
       conn.send(resp) 
      except: 
       print "##### PLAYBACK STOPPED FOR "+cam+" #####" 
       break; 



############################################################################################### 
############################################################################################### 
# Various worker functions to parse the incoming messages, grab SDP info, and the like... 

def get_type(string): 
    p="(Transport.*)" 
    m = re.search(p, string) 
    if m: 
       type=m.group(1) 
       return type 

     return -1 

def get_cseq(string): 
    p="CSeq:[ ]+([0-9]+)" 
    m = re.search(p, string) 
    if m: 
     cseq=m.group(1) 
     return cseq 

    return -1 

def get_sdp(cam): 
     url="<wherever your SDP file lives>?cam" 
     sdp=urllib2.urlopen(url).read(1000) 
     sdp=sdp.strip() 
     return sdp 


##################################################################################################### 
# Main program loop. Sit here waiting for incoming fonnections and creating threads as required 
# to service them 
##################################################################################################### 

while True: 
    conn, addr = s.accept() 
    print '##### NEW CONNECTION FOR VIDEO RECEIVED FROM ' + addr[0] 
    start_new_thread(player_thread ,(conn,)) 
s.close() 
0

Vous pouvez créer un ServerMediaSession à partir d'un fichier SDP et l'ajouter à un RTSPServer.

#include "liveMedia.hh" 
#include "BasicUsageEnvironment.hh" 

class SDPMediaSubsession: public ServerMediaSubsession 
{ 
    public: 
     static SDPMediaSubsession* createNew(UsageEnvironment& env, MediaSubsession* subsession) { return new SDPMediaSubsession(env, subsession); } 

    protected: 
     SDPMediaSubsession(UsageEnvironment& env, MediaSubsession* subsession) : ServerMediaSubsession(env), m_subsession(subsession) {}; 
     virtual ~SDPMediaSubsession() {}; 

    protected: 
     virtual char const* sdpLines() { return m_subsession->savedSDPLines(); } 

     virtual void getStreamParameters(unsigned clientSessionId, netAddressBits clientAddress, Port const& clientRTPPort, Port const& clientRTCPPort, int tcpSocketNum, unsigned char rtpChannelId, unsigned char rtcpChannelId, 
      netAddressBits& destinationAddress, u_int8_t& destinationTTL, Boolean& isMulticast, Port& serverRTPPort, Port& serverRTCPPort, void*& streamToken) 
     {  
      destinationAddress = m_subsession->connectionEndpointAddress(); 
      isMulticast = IsMulticastAddress(destinationAddress);  
      serverRTPPort = m_subsession->clientPortNum(); 
      serverRTCPPort = m_subsession->clientPortNum()+1;   
     } 

     virtual void startStream(unsigned clientSessionId, void* streamToken, TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData, unsigned short& rtpSeqNum, unsigned& rtpTimestamp, ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler, void* serverRequestAlternativeByteHandlerClientData) {} 
    protected: 
     MediaSubsession* m_subsession; 
}; 


class SDPRTSPServer: public RTSPServer 
{ 
    public: 
     static SDPRTSPServer* createNew(UsageEnvironment& env, Port ourPort, UserAuthenticationDatabase* authDatabase = NULL, unsigned reclamationTestSeconds = 65) 
     { 
      int ourSocket = setUpOurSocket(env, ourPort); 
      if (ourSocket == -1) return NULL; 
      return new SDPRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);    
     } 

    protected: 
     SDPRTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) 
      : RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds) {}   

    protected: 
     virtual ServerMediaSession* lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession) 
     {   
      ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName); 
      if (sms == NULL) 
      {   
       FILE* file = fopen(streamName, "r");     
       if (file != NULL) 
       { 
        sms = ServerMediaSession::createNew(envir(), streamName); 
        fseek(file, 0, SEEK_END); 
        long size = ftell(file); 
        fseek(file, 0, SEEK_SET); 
        char sdp[size]; 
        fread(sdp,size,1,file); 
        fclose(file); 

        MediaSession* session = MediaSession::createNew(envir(), sdp);  
        MediaSubsessionIterator iter(*session); 
        MediaSubsession* subsession = NULL; 
        while ((subsession = iter.next()) != NULL) 
        { 
         sms->addSubsession(SDPMediaSubsession::createNew(envir(),subsession)); 
        }         
        addServerMediaSession(sms); 
       } 
      } 
      return sms; 
     } 
}; 


int main(int argc, char** argv) 
{ 
    TaskScheduler* scheduler = BasicTaskScheduler::createNew(); 
    BasicUsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); 

    RTSPServer* rtspServer = SDPRTSPServer::createNew(*env, 8554); 
    if (rtspServer == NULL) 
    { 
     *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n"; 
     exit(1); 
    }  

    env->taskScheduler().doEventLoop(); 
    return 0; 
} 

Ce RTSPServer va créer une lecture de session fichier SDP spécifié par l'URL sans envoyer de flux RTP/RTCP.

Il donnera accès aux fichiers SDP disponibles dans le répertoire en cours d'exécution du serveur RTSP (comme live555MediaServer pour les fichiers vidéo).
Par exemple rtsp://<server>:8554/cam1.sdp donnera accès au flux décrit dans cam1.sdp