live555源码之rtsp协议交互流程

live555是专门用于实现rtsp协议的专业开源库,其能兼容各种rtsp参数,条件等;使用过程中不免好奇,live555中用于信令(OPTION DESCRIBE SETUP PLAY等)交互的TCP协议;用于进行流媒体传输的UDP/TCP协议什么时候被创建的?什么时候被使用的?

概要

rtsp在交互的过程中用到很多协议:tcp,udp,rtp,rtcp,sdp等协议;该篇文章主要分析在live555中这些协议是什么时候被创建的,什么时候被使用的等协议相关流程。

TCP:服务器与客户端进行协商(OPTION DESCRIBE SETUP PLAY);

UDP/TCP:协议是rtsp服务器用来想客户端推流;当然rtsp向客户端推流也可以使用tcp协议;那么就rtsp而言使用udp推流和使用tcp推流有什么区别呢?

UDP推流

tcp连接进行rtsp信令交互;

创建新的udp套接字来发送rtp包;

创建新的udp套接字来发送rtcp包;

TCP推流

tcp连接进行rtsp信令交互;

复用rtsp的tcp连接发送rtp和rtcp包;

嵌入式开发一般使用udp推流,实时性相对较高;

RTP:对视频流(h264/h265)/音频流(AAC/MP3)裸流进行封装,用于网络传输;

RTCP:服务器和客户端用来管理流媒体协议

TCP交互协商

在程序创建RTSPServer类对象时就会创建用于信令协商的TCP协议,见如下代码:

//创建RTSPServer类对象
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
//createNew实现
RTSPServer*
RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
          UserAuthenticationDatabase* authDatabase,
          unsigned reclamationSeconds) {
  int ourSocketIPv4 = setUpOurSocket(env, ourPort, AF_INET);
  int ourSocketIPv6 = setUpOurSocket(env, ourPort, AF_INET6);
  if (ourSocketIPv4 < 0 && ourSocketIPv6 < 0) return NULL;
  
  return new RTSPServer(env, ourSocketIPv4, ourSocketIPv6, ourPort, authDatabase, reclamationSeconds);
}

从源码可以看出创建RTSPServer类对象的时候会创建ipv4和ipv6两种套接字,因此理论上来说live555实现的rtsp服务器支持ipv4和ipv6两种网络传输。

//RTSPServer构造函数
RTSPServer::RTSPServer(UsageEnvironment& env,
           int ourSocketIPv4, int ourSocketIPv6, Port ourPort,
           UserAuthenticationDatabase* authDatabase,
           unsigned reclamationSeconds)
  : GenericMediaServer(env, ourSocketIPv4, ourSocketIPv6, ourPort, reclamationSeconds),
    fHTTPServerSocketIPv4(-1), fHTTPServerSocketIPv6(-1), fHTTPServerPort(0),
    fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
    fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)),
    fPendingRegisterOrDeregisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)),
    fRegisterOrDeregisterRequestCounter(0), fAuthDB(authDatabase),
    fAllowStreamingRTPOverTCP(True),
    fOurConnectionsUseTLS(False), fWeServeSRTP(False) {
}
//GenericMediaServer构造函数
GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocketIPv4, int ourSocketIPv6, Port ourPort,
         unsigned reclamationSeconds)
  : Medium(env),
    fServerSocketIPv4(ourSocketIPv4), fServerSocketIPv6(ourSocketIPv6),
    fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
    fClientSessions(HashTable::create(STRING_HASH_KEYS)),
    fPreviousClientSessionId(0),
    fTLSCertificateFileName(NULL), fTLSPrivateKeyFileName(NULL) {
  ignoreSigPipeOnSocket(fServerSocketIPv4); // so that clients on the same host that are killed don't also kill us
  ignoreSigPipeOnSocket(fServerSocketIPv6); // ditto
  
  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocketIPv4, incomingConnectionHandlerIPv4, this);
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocketIPv6, incomingConnectionHandlerIPv6, this);
}

在GenericMediaServer构造函数中会把创建的fServerSocketIPv4和fServerSocketIPv6这两个套接字插入到双向闭环链表中等待doEventLoop循环处理,对应的处理函数分别为:incomingConnectionHandlerIPv4, incomingConnectionHandlerIPv6;最终都会调用incomingConnectionHandlerOnSocket函数;

GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer,
       int clientSocket, struct sockaddr_storage const& clientAddr,
       Boolean useTLS)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr), fTLS(envir()) {
  fInputTLS = fOutputTLS = &fTLS;
 
  // Add ourself to our 'client connections' table:
  fOurServer.fClientConnections->Add((char const*)this, this);
  
  if (useTLS) {
    // Perform extra processing to handle a TLS connection:
    fTLS.setCertificateAndPrivateKeyFileNames(ourServer.fTLSCertificateFileName,
                ourServer.fTLSPrivateKeyFileName);
    fTLS.isNeeded = True;
 
    fTLS.tlsAcceptIsNeeded = True; // call fTLS.accept() the next time the socket is readable
  }
 
  // Arrange to handle incoming requests:
  resetRequestBuffer();
  envir().taskScheduler()
    .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}
//incomingRequestHandler函数最终调用
void GenericMediaServer::ClientConnection::incomingRequestHandler() {
  if (fInputTLS->tlsAcceptIsNeeded) { // we need to successfully call fInputTLS->accept() first:
    if (fInputTLS->accept(fOurSocket) <= 0) return; // either an error, or we need to try again later
 
    fInputTLS->tlsAcceptIsNeeded = False;
    // We can now read data, as usual:
  }
 
  int bytesRead;
  if (fInputTLS->isNeeded) {
    bytesRead = fInputTLS->read(&fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft);
  } else {
    struct sockaddr_storage dummy; // 'from' address, meaningless in this case
  
    bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  }
  handleRequestBytes(bytesRead);//该函数实现了对 OPTION DESCRIBE SETUP等各种信令的处理逻辑
}

在构造函数中setBackgroundHandling会把客户端套接字fOurSocket和对应的处理函数incomingRequestHandler添加到闭环双链表中,在doEventLoop中循环遍历,客户端有信令交互就调用相关的处理函数;至此用于协商的TCP协议处理流程就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值