我有一个视频客户端,它只能通过rtsp请求加入视频流-它不能直接使用SDP文件。
但是,需要观看的组播视频源不支持rtsp...
最终,rtsp请求所做的就是提供一种机制,让SDP返回给client...so。我一直在尝试找到一种解决方案,允许我从客户端向某种代理服务器发出rtsp请求,然后根据所使用的URI,该服务器将返回相关的SDP,以响应DESCRIBE请求。这将允许我播放视频源,尽管客户端只能通过rtsp请求视频。
这听起来很简单,但我还没有找到一种方法。有什么想法吗?
发布于 2015-09-12 22:24:18
这就是我最终要做的;下面的代码是用Python编写的。它不会赢得任何选美比赛,我希望有很多方法,它可以break...but它的第一次尝试。它从URL (在本例中)获取SDP文件,然后响应针对它的RTSP请求,将SDP文件提供给客户端。所需的SDP可以指定为RTSP URI的参数:
#! /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()发布于 2015-10-01 06:03:04
您可以从SDP文件创建ServerMediaSession并将其添加到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;
}此RTSPServer将创建一个读取url指定的SDP文件的会话,而不发送任何RTP/RTCP流。
它将允许访问RTSP服务器的运行目录中的SDP文件(就像live555MediaServer对视频文件所做的那样)。
例如,rtsp://<server>:8554/cam1.sdp将提供对cam1.sdp中描述的流的访问
https://stackoverflow.com/questions/32522706
复制相似问题