互联网直播业务,除私有协议外,大多采用rtmp协议进行媒体发布或推流,而很少选择使用rtsp协议推流和发布;而rtsp协议也是一个很好的协议,可以分别支持rtp over udp和 rtp over rtsp,媒体控制更加灵活。下面简单介绍一下如何采用rtsp协议进行推流或媒体资源发布。
1、rtsp支持标准的推流扩展,通过扩展announce消息后,可以使用rtspclient主动将媒体流发布到服务器,然后在服务器提供直播或点播服务,不过live555等大多开源项目默认并不支持该扩展;早年间,跟华为、上海贝尔等厂商对接过流媒体系统,使用过该扩展,客户端实现比较简单,这里就不多讲了;
2、基于rtsp协议实现链路复用,扩展register消息,将rtsp协议扩展成双向控制协议;
本文将会主要讲述上述第二种方式,实现rtsp链路复用,扩展支持rtsp流媒体发布的方法;基本步骤如下:
2.1、首先,客户端启动后,主动向rtsp服务器进行register注册,并携带需要发布的(1-n个)媒体资源信息;
2.2、其次,rtsp服务器收到register注册消息之后,将携带的媒体资源进行发布,并将该rtsp的tcp链路复用,作为服务器fd进行处理;
2.3、如果rtsp服务器接收到外部媒体资源访问或媒体流点播或直播,则复用之前注册过的rtsp链路,访问远端的客户端进行拉流;
总体来说,就是复用rtsp协议的tcp长连接,进行媒体资源发布、解决拉流时的NAT穿越问题。通信流程如下:
实际上客户端运行的是一个rtspServer,服务器端运行的是一个rtspProxy;双方可以始终保持这个rtsp的tcp长连接;根据需要随时点播或者直播客户端的视频流。而无需NAT穿越,rtp over udp传输也没有NAT。
下面上代码,live555中有一个register和unregitser流程,如果不能复用rtsp链接的话,这个流程用处不大,还不如私有协议更方便;所以下面的代码就是将这个流程修改成可以满足上述要求的rtsp链路复用的业务处理逻辑。
/RTSPRegisterSender.cpp
#include "RTSPRegisterSender.hh"
#include <GroupsockHelper.hh> // for MAKE_SOCKADDR_IN
// RTSPRegisterOrDeregisterSender implementation /
RTSPRegisterOrDeregisterSender::RTSPRegisterOrDeregisterSender(UsageEnvironment &env,
char const *remoteClientNameOrAddress, portNumBits remoteClientPortNum,
Authenticator *authenticator,
int verbosityLevel, char const *applicationName)
: RTSPClient(env, NULL, verbosityLevel, applicationName, 0, -1),
fRemoteClientPortNum(remoteClientPortNum)
{
// Set up a connection to the remote client. To do this, we create a fake "rtsp://" URL for it:
char const *fakeRTSPURLFmt = "rtsp://%s:%u/";
unsigned fakeRTSPURLSize = strlen(fakeRTSPURLFmt) + strlen(remoteClientNameOrAddress) + 5/* max port num len */;
char *fakeRTSPURL = new char[fakeRTSPURLSize];
sprintf(fakeRTSPURL, fakeRTSPURLFmt, remoteClientNameOrAddress, remoteClientPortNum);
setBaseURL(fakeRTSPURL);
delete[] fakeRTSPURL;
if(authenticator != NULL) { fCurrentAuthenticator = *authenticator; }
}
RTSPRegisterOrDeregisterSender::~RTSPRegisterOrDeregisterSender()
{
}
RTSPRegisterOrDeregisterSender::RequestRecord_REGISTER_or_DEREGISTER::RequestRecord_REGISTER_or_DEREGISTER(unsigned cseq, char const *cmdName,
RTSPClient::responseHandler *rtspResponseHandler,
char const *rtspURLToRegisterOrDeregister,
char const *proxyURLSuffix)
: RTSPClient::RequestRecord(cseq, cmdName, rtspResponseHandler),
fRTSPURLToRegisterOrDeregister(strDup(rtspURLToRegisterOrDeregister)),
fProxyURLSuffix(proxyURLSuffix ? strDup(proxyURLSuffix) : strDup(GetLocalMac()))
{
}
RTSPRegisterOrDeregisterSender::RequestRecord_REGISTER_or_DEREGISTER::~RequestRecord_REGISTER_or_DEREGISTER()
{
delete[] fRTSPURLToRegisterOrDeregister;
delete[] fProxyURLSuffix;
}
// RTSPRegisterSender implementation /
RTSPRegisterSender *RTSPRegisterSender::createNew(UsageEnvironment &env,
char const *remoteClientNameOrAddress, portNumBits remoteClientPortNum,
char const *rtspURLToRegister, char const *proxyURLSuffix,
RTSPClient::responseHandler *rtspResponseHandler, Authenticator *authenticator,
Boolean requestStreamingViaTCP, Boolean reuseConnection,
int verbosityLevel, char const *applicationName)
{
return new RTSPRegisterSender(env, remoteClientNameOrAddress, remoteClientPortNum, rtspURLToRegister,
rtspResponseHandler, authenticator,
requestStreamingViaTCP, proxyURLSuffix, reuseConnection,
verbosityLevel, applicationName);
}
///将注册的客户端rtsp链接以及fd作为rtsp服务器链接以及fd使用,复用tcp链路
void RTSPRegisterSender::defaultRegisterResponseHandler(RTSPClient *rtspClient, int resultCode, char *resultString)
{
RTSPRegisterSender *client = (RTSPRegisterSender *)rtspClient;
if(!client || resultCode) {
fprintf(stderr, "response error: \n%s\n", resultString);
return;
}
if(!client->fOurServer) {
OutPacketBuffer::maxSize = THE_MAX_OUT_PACKET_LEN;
portNumBits listenPort = client->getLocalListenPort() ? client->getLocalListenPort() : GetRandomRtpPorts();
client->fOurServer = RTSPServer::createNew(client->envir(), listenPort);
}
client->fOurServer->setReuseConnectionRequestHandler(client);
}
void RTSPRegisterSender::sendRegisterCommand()
{
// Send the "REGISTER" request:
sendRequest(new RequestRecord_REGISTER(++fCSeq, defaultRegisterResponseHandler, NULL, True, False, NULL));
}
void RTSPRegisterSender::grabConnection(int &sock, struct sockaddr_in &remoteAddress)
{
//sock = grabSocket();
sock = socketNum();
MAKE_SOCKADDR_IN(remoteAddr, fServerAddress, htons(fRemoteClientPortNum));
remoteAddress = remoteAddr;
}
RTSPRegisterSender::RTSPRegisterSender(UsageEnvironment &env,
char const *remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const *rtspURLToRegister,
RTSPClient::responseHandler *rtspResponseHandler, Authenticator *authenticator,
Boolean requestStreamingViaTCP, char const *proxyURLSuffix, Boolean reuseConnection,
int verbosityLevel, char const *applicationName)
: fOurServer(NULL), fLocalListenPortNum(0), RTSPRegisterOrDeregisterSender(env, remoteClientNameOrAddress, remoteClientPortNum,
authenticator, verbosityLevel, applicationName)
{
// Send the "REGISTER" request:
sendRequest(new RequestRecord_REGISTER(++fCSeq, rtspResponseHandler,
rtspURLToRegister, reuseConnection, requestStreamingViaTCP, proxyURLSuffix));
}
RTSPRegisterSender::~RTSPRegisterSender()
{
Medium::close(fOurServer);
}
Boolean RTSPRegisterSender::setRequestFields(RequestRecord *request,
char *&cmdURL, Boolean &cmdURLWasAllocated,
char const *&protocolStr,
char *&extraHeaders, Boolean &extraHeadersWereAllocated)
{
if(strcmp(request->commandName(), "REGISTER") == 0) {
RequestRecord_REGISTER *request_REGISTER = (RequestRecord_REGISTER *)request;
if(request_REGISTER->rtspURLToRegister()) {
setBaseURL(request_REGISTER->rtspURLToRegister());
}
cmdURL = (char *)url();
cmdURLWasAllocated = False;
// Generate the "Transport:" header that will contain our REGISTER-specific parameters. This will be "extraHeaders".
// First, generate the "proxy_url_suffix" parameter string, if any:
char *proxyURLSuffixParameterStr;
if(request_REGISTER->proxyURLSuffix() == NULL) {
proxyURLSuffixParameterStr = strDup("");
} else {
char const *proxyURLSuffixParameterFmt = "; proxy_url_suffix=%s";
unsigned proxyURLSuffixParameterSize = strlen(proxyURLSuffixParameterFmt)
+ strlen(request_REGISTER->proxyURLSuffix());
proxyURLSuffixParameterStr = new char[proxyURLSuffixParameterSize];
sprintf(proxyURLSuffixParameterStr, proxyURLSuffixParameterFmt, request_REGISTER->proxyURLSuffix());
}
char const *transportHeaderFmt = "Transport: %spreferred_delivery_protocol=%s%s\r\n";
unsigned transportHeaderSize = strlen(transportHeaderFmt) + 100/*conservative*/ + strlen(proxyURLSuffixParameterStr);
char *transportHeaderStr = new char[transportHeaderSize];
sprintf(transportHeaderStr, transportHeaderFmt,
request_REGISTER->reuseConnection() ? "reuse_connection; " : "",
request_REGISTER->requestStreamingViaTCP() ? "interleaved" : "udp",
proxyURLSuffixParameterStr);
delete[] proxyURLSuffixParameterStr;
extraHeaders = transportHeaderStr;
extraHeadersWereAllocated = True;
return True;
} else {
return RTSPClient::setRequestFields(request, cmdURL, cmdURLWasAllocated, protocolStr, extraHeaders, extraHeadersWereAllocated);
}
}
RTSPRegisterSender::RequestRecord_REGISTER::RequestRecord_REGISTER(unsigned cseq, RTSPClient::responseHandler *rtspResponseHandler,
char const *rtspURLToRegister,
Boolean reuseConnection, Boolean requestStreamingViaTCP, char const *proxyURLSuffix)
: RTSPRegisterOrDeregisterSender::RequestRecord_REGISTER_or_DEREGISTER(cseq, "REGISTER", rtspResponseHandler, rtspURLToRegister, proxyURLSuffix),
fReuseConnection(reuseConnection), fRequestStreamingViaTCP(requestStreamingViaTCP)
{
}
RTSPRegisterSender::RequestRecord_REGISTER::~RequestRecord_REGISTER()
{
}
// RTSPDeregisterSender implementation /
RTSPDeregisterSender *RTSPDeregisterSender::createNew(UsageEnvironment &env,
char const *remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const *rtspURLToDeregister,
RTSPClient::responseHandler *rtspResponseHandler, Authenticator *authenticator,
char const *proxyURLSuffix, int verbosityLevel, char const *applicationName)
{
return new RTSPDeregisterSender(env, remoteClientNameOrAddress, remoteClientPortNum, rtspURLToDeregister,
rtspResponseHandler, authenticator,
proxyURLSuffix, verbosityLevel, applicationName);
}
void RTSPDeregisterSender::sendDeregisterCommand()
{
// Send the "REGISTER" request:
sendRequest(new RequestRecord_DEREGISTER(++fCSeq, RTSPRegisterSender::defaultRegisterResponseHandler, NULL, NULL));
}
RTSPDeregisterSender::RTSPDeregisterSender(UsageEnvironment &env,
char const *remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const *rtspURLToDeregister,
RTSPClient::responseHandler *rtspResponseHandler, Authenticator *authenticator,
char const *proxyURLSuffix,
int verbosityLevel, char const *applicationName)
: RTSPRegisterOrDeregisterSender(env, remoteClientNameOrAddress, remoteClientPortNum, authenticator, verbosityLevel, applicationName)
{
// Send the "DEREGISTER" request:
sendRequest(new RequestRecord_DEREGISTER(++fCSeq, rtspResponseHandler, rtspURLToDeregister, proxyURLSuffix));
}
RTSPDeregisterSender::~RTSPDeregisterSender()
{
}
Boolean RTSPDeregisterSender::setRequestFields(RequestRecord *request,
char *&cmdURL, Boolean &cmdURLWasAllocated,
char const *&protocolStr,
char *&extraHeaders, Boolean &extraHeadersWereAllocated)
{
if(strcmp(request->commandName(), "DEREGISTER") == 0) {
RequestRecord_DEREGISTER *request_DEREGISTER = (RequestRecord_DEREGISTER *)request;
if(request_DEREGISTER->rtspURLToDeregister()) {
setBaseURL(request_DEREGISTER->rtspURLToDeregister());
}
cmdURL = (char *)url();
cmdURLWasAllocated = False;
// Generate the "Transport:" header that will contain our DEREGISTER-specific parameters. This will be "extraHeaders".
// First, generate the "proxy_url_suffix" parameter string, if any:
char *proxyURLSuffixParameterStr;
if(request_DEREGISTER->proxyURLSuffix() == NULL) {
proxyURLSuffixParameterStr = strDup("");
} else {
char const *proxyURLSuffixParameterFmt = "proxy_url_suffix=%s";
unsigned proxyURLSuffixParameterSize = strlen(proxyURLSuffixParameterFmt)