基于rtsp协议实现媒体资源发布或者视频直播

互联网直播业务,除私有协议外,大多采用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)
                   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值