live555 处理 sdp消息 三 "SETUP"

log 接收的SETUP 请求

RTSPClientConnection[0xef5bfc40]::handleRequestBytes() read 158 new bytes:
SETUP rtsp://192.168.0.10:8554/H264Video.mkv/track1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=54808-54809
Date: Sat, 01 Jan 2000 04:50:00 GMT


parseRTSPRequestString() succeeded, returning cmdName "SETUP", urlPreSuffix "H264Video.mkv", urlSuffix "track1", CSeq "3", Content-Length 0, with 0 bytes following the message.

这里说明下 track1 是在 处理DESCRIPTION 请求时生成,标记使用的轨道

setup 分析

前面的处理流程和 其他请求消息一样的
log 中可以知道 是setup请求 以及urlPreSuffix urlSuffix等信息
因为依然没有session: 消息
所以 直接跳转到 setup 处理

strcat(urlTotalSuffix, urlSuffix);
if (authenticationOK("SETUP", urlTotalSuffix, (char const*)fRequestBuffer)) {
  clientSession = (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
} else {
  areAuthenticated = False;
}

if (clientSession != NULL) {
    clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
    playAfterSetup = clientSession->fStreamAfterSETUP;
}

因为 handleCmd_SETUP 代码处理的逻辑比较大,选择最主要的部分分析

void RTSPServer::RTSPClientSession
::handleCmd_SETUP(RTSPServer::RTSPClientConnection* ourClientConnection,
          char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
    //获取传输过程中的session
    ServerMediaSession* sms
      = fOurServer.lookupServerMediaSession(streamName, fOurServerMediaSession == NULL);
    // 解析传输协议的头 确定传输格式, udp?tcp multicast?unicast 等 
    parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
             clientsDestinationAddressStr, clientsDestinationTTL,
             clientRTPPortNum, clientRTCPPortNum,
             rtpChannelId, rtcpChannelId);
// 会话中创建socket 传输数据流
subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
                    clientRTPPort, clientRTCPPort,
                    fStreamStates[trackNum].tcpSocketNum, rtpChannelId, rtcpChannelId,
                    destinationAddress, destinationTTL, fIsMulticast,
                    serverRTPPort, serverRTCPPort,
                    fStreamStates[trackNum].streamToken);
 if (fIsMulticast) { //这里提前确定了 传输格式 因为log中已经看出来了,分析在后面写出来
    switch (streamingMode) {
        case RTP_UDP: {
             sizeof ourClientConnection->fResponseBuffer,
             "RTSP/1.0 200 OK\r\n"
             "CSeq: %s\r\n"
             "%s"
             "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
             "Session: %08X%s\r\n\r\n",
             ourClientConnection->fCurrentCSeq,
             dateHeader(),
             destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), 
             ntohs(serverRTCPPort.num()), destinationTTL,
             fOurSessionId, timeoutParameterString);
        break;
      }
    }
 }
}

首先下面log 看下

sending response:
RTSP/1.0 200 OK
CSeq: 3
Date: Sat, Jan 01 2000 00:05:00 GMT
Transport: RTP/AVP;unicast;destination=192.168.0.11;source=192.168.0.10;client_port=54808-54809;server_port=6970-6971
Session: 817D3E3E;timeout=1200

所以 SETUP 请求有一个作用就是确定传输格式 Transport: RTP/AVP;unicast

SETUP 请求处理 干了那些事情

  1. 创建socket 建立传输RTP的流
  2. 确定传输格式

确定传输格式

在解析传输头的之前会解析SETUP 请求的消息,然后传输进parseTransportHeader 进一步处理

static void parseTransportHeader(char const* buf,
                 StreamingMode& streamingMode,
                 char*& streamingModeString,
                 char*& destinationAddressStr,
                 u_int8_t& destinationTTL,
                 portNumBits& clientRTPPortNum, // if UDP
                 portNumBits& clientRTCPPortNum, // if UDP
                 unsigned char& rtpChannelId, // if TCP
                 unsigned char& rtcpChannelId // if TCP
                 ) {
  // Initialize the result parameters to default values:
  streamingMode = RTP_UDP; //默认是RTP_UDP ...

  // Then, run through each of the fields, looking for ones we handle:
  while (sscanf(fields, "%[^;\r\n]", field) == 1) {
    if (strcmp(field, "RTP/AVP/TCP") == 0) { ...
    if (strcmp(field, "RAW/RAW/UDP") == 0 || ...
    else if (_strncasecmp(field, "destination=", 12) == 0) {
      delete[] destinationAddressStr;
      destinationAddressStr = strDup(field+12);
    } else if (sscanf(field, "ttl%u", &ttl) == 1) {
      destinationTTL = (u_int8_t)ttl;
    } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {
      clientRTPPortNum = p1;
      clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p2; // ignore the second port number if the client asked for raw UDP
    } else if (sscanf(field, "client_port=%hu", &p1) == 1) {
      clientRTPPortNum = p1;
      clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;
    } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
      rtpChannelId = (unsigned char)rtpCid;
      rtcpChannelId = (unsigned char)rtcpCid;
    }
  }
  delete[] field;
}

因为在parseTransportHeader 中streamingMode 默认是RTP_UDP, 而请求的传输格式是RTP/AVP,选项中没有,所以就使用默认RTP_UDP 传输格式,然后就是确定 客户端的地址destinationAddressStr ,以及 客户端的RTP传输的地址 clientRTPPortNum,RTCP 传输 用的端口clientRTCPPortNum,RTCP用来保证 QoS的

所以在上面的reponse 信息是RTP_UDP 单播形式

创建socket 建立传输RTP的流

void OnDemandServerMediaSubsession::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) {
  if (destinationAddress == 0) destinationAddress = clientAddress;
  struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;

    // Normal case: Create a new media source:
    //创建.mkv流的FramedSource 使用的是MatroskaFileServerMediaSubsession
    FramedSource* mediaSource
      = createNewStreamSource(clientSessionId, streamBitrate);

    // Create 'groupsock' and 'sink' objects for the destination,
    // Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of
    // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even).
    // (If we're multiplexing RTCP and RTP over the same port number, it can be odd or even.)
    for (portNumBits serverPortNum = fInitialPortNum; ; ++serverPortNum) {
      struct in_addr dummyAddr; dummyAddr.s_addr = 0;
      serverRTPPort = serverPortNum;
      // 创建 数据报类型的socket ,就是用来传输RTP 格式
      rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort);
    // 创建 数据报类型的socket ,就是用来传输RTCP 消息
      // Create a separate 'groupsock' object (with the next (odd) port number) for RTCP:
      rtcpGroupsock = createGroupsock(dummyAddr, serverRTCPPort);
      break; // success
    }
    //编码格式确定
    rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
    if (rtpSink != NULL && rtpSink->estimatedBitrate() > 0) streamBitrate = rtpSink->estimatedBitrate();
      }
}

创建socket

现在我只分析 创建socket RTP传输,另外两个部分也特别重要,(后面会分析)涉及到编码,流的获取,在解决大部分问题都在这两个部分,比如优化,传输速度,花屏等

      rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort);

这里写图片描述

根据上面的流程可以得到 创建socket 的socketnum

Socket::Socket(UsageEnvironment& env, Port port)
  : fEnv(DefaultUsageEnvironment != NULL ? *DefaultUsageEnvironment : env), fPort(port) {
  fSocketNum = setupDatagramSocket(fEnv, port);
}

最后send给客户端

 send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);

猜你喜欢

转载自blog.csdn.net/engineer_james/article/details/81543484