Live555源代码解读(5-1)

本文详细解析了RTSP协议中的SETUP命令处理流程及RTP会话建立过程,包括如何根据客户端请求创建ServerMediaSubsession实例,设置传输参数,并最终形成RTSP响应消息。

六、建立RTP会话

     首先更正一个概念:ServerMediaSession原先说代表一个流,其实是不准确的。它代表的是server端的一个媒体的名字,而说ServerMediaSubsession代表一个Track是准确的。以后流指的是那些有数据流动的组合。

RTP的建立:RTP的建立过程无非是这样:client告诉server自己的rtp/rtcp端口号,server建立自己的rtp/rtcp socket,然后在收到PLAY请求时向客户端发数据。看起来非常简单。在收到SETUP请求时才建立连接,让我们看一下处理这个命令的函数:


void RTSPServer::RTSPClientSession::handleCmd_SETUP(

char const* cseq,

char const* urlPreSuffix,

char const* urlSuffix,

char const* fullRequestStr)

{

// Normally, "urlPreSuffix" should be the session (stream) name,

// and "urlSuffix" should be the subsession (track) name.

// However (being "liberal in what we accept"), we also handle

// 'aggregate' SETUP requests (i.e., without a track name),

// in the special case where we have only a single track.  I.e.,

// in this case, we also handle:

//    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or

//    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween)

//    is the session (stream) name.

char const* streamName = urlPreSuffix; // in the normal case

char const* trackId = urlSuffix; // in the normal case

char* concatenatedStreamName = NULL; // in the normal case



do {

// First, make sure the specified stream name exists:

fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);

if (fOurServerMediaSession == NULL) {

// Check for the special case (noted above), before we up:

if (urlPreSuffix[0] == '\0') {

streamName = urlSuffix;

} else {

concatenatedStreamName = new char[strlen(urlPreSuffix)

+ strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'

sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix,

urlSuffix);

streamName = concatenatedStreamName;

}

trackId = NULL;



// Check again:

fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);

}

if (fOurServerMediaSession == NULL) {

handleCmd_notFound(cseq);

break;

}



fOurServerMediaSession->incrementReferenceCount();



//为一个流中所有的track都分配一个stream state

if (fStreamStates == NULL) {

// This is the first "SETUP" for this session.  Set up our

// array of states for all of this session's subsessions (tracks):

ServerMediaSubsessionIterator iter(*fOurServerMediaSession);

for (fNumStreamStates = 0; iter.next() != NULL;++fNumStreamStates) {

} // begin by counting the number of subsessions (tracks)



fStreamStates = new struct streamState[fNumStreamStates];



iter.reset();

ServerMediaSubsession* subsession;

for (unsigned i = 0; i < fNumStreamStates; ++i) {

subsession = iter.next();

fStreamStates[i].subsession = subsession;

fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later

}

}



//查找当前请求的track的信息

// Look up information for the specified subsession (track):

ServerMediaSubsession* subsession = NULL;

unsigned streamNum;

if (trackId != NULL && trackId[0] != '\0') { // normal case

for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {

subsession = fStreamStates[streamNum].subsession;

if (subsession != NULL&& strcmp(trackId, subsession->trackId()) == 0)

break; //找到啦!

}

if (streamNum >= fNumStreamStates) {

// The specified track id doesn't exist, so this request fails:

handleCmd_notFound(cseq);

break;

}

} else {

// Weird case: there was no track id in the URL.

// This works only if we have only one subsession:

if (fNumStreamStates != 1) {

handleCmd_bad(cseq);

break;

}

streamNum = 0;

subsession = fStreamStates[streamNum].subsession;

}

// ASSERT: subsession != NULL



//分析RTSP请求字符串中的传输要求

// Look for a "Transport:" header in the request string, to extract client parameters:

StreamingMode streamingMode;

char* streamingModeString = NULL; // set when RAW_UDP streaming is specified

char* clientsDestinationAddressStr;

u_int8_t clientsDestinationTTL;

portNumBits clientRTPPortNum, clientRTCPPortNum;

unsigned char rtpChannelId, rtcpChannelId;

parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,

clientsDestinationAddressStr, clientsDestinationTTL,

clientRTPPortNum, clientRTCPPortNum, rtpChannelId,

rtcpChannelId);

if (streamingMode == RTP_TCP && rtpChannelId == 0xFF

|| streamingMode != RTP_TCP&&

fClientOutputSocket != fClientInputSocket) {

// An anomolous situation, caused by a buggy client.  Either:

//     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or

//     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).

// In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:

streamingMode = RTP_TCP;

rtpChannelId = fTCPStreamIdCount;

rtcpChannelId = fTCPStreamIdCount + 1;

}

fTCPStreamIdCount += 2;



Port clientRTPPort(clientRTPPortNum);

Port clientRTCPPort(clientRTCPPortNum);



// Next, check whether a "Range:" header is present in the request.

// This isn't legal, but some clients do this to combine "SETUP" and "PLAY":

double rangeStart = 0.0, rangeEnd = 0.0;

fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart,

rangeEnd) || parsePlayNowHeader(fullRequestStr);



// Then, get server parameters from the 'subsession':

int tcpSocketNum = streamingMode == RTP_TCP ? fClientOutputSocket : -1;

netAddressBits destinationAddress = 0;

u_int8_t destinationTTL = 255;

#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING

if (clientsDestinationAddressStr != NULL) {

// Use the client-provided "destination" address.

// Note: This potentially allows the server to be used in denial-of-service

// attacks, so don't enable this code unless you're sure that clients are

// trusted.

destinationAddress = our_inet_addr(clientsDestinationAddressStr);

}

// Also use the client-provided TTL.

destinationTTL = clientsDestinationTTL;

#endif

delete[] clientsDestinationAddressStr;

Port serverRTPPort(0);

Port serverRTCPPort(0);



// Make sure that we transmit on the same interface that's used by

// the client (in case we're a multi-homed server):

struct sockaddr_in sourceAddr;

SOCKLEN_T namelen = sizeof sourceAddr;

getsockname(fClientInputSocket, (struct sockaddr*) &sourceAddr,&namelen);

netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;

netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;

// NOTE: The following might not work properly, so we ifdef it out for now:

#ifdef HACK_FOR_MULTIHOMED_SERVERS

ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;

#endif



//获取rtp连接信息,在其中已建立起了server端的rtp和rtcp socket,返回

//fStreamStates[streamNum].streamToken表示数据流已经建立起来了

subsession->getStreamParameters(fOurSessionId,

fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort,

tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress,

destinationTTL, fIsMulticast, serverRTPPort, serverRTCPPort,

fStreamStates[streamNum].streamToken);

SendingInterfaceAddr = origSendingInterfaceAddr;

ReceivingInterfaceAddr = origReceivingInterfaceAddr;



//形成RTSP回应字符串

struct in_addr destinationAddr;

destinationAddr.s_addr = destinationAddress;

char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));

char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));

if (fIsMulticast) {

switch (streamingMode) {

case RTP_UDP:

snprintf(

(char*) fResponseBuffer,

sizeof 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\r\n\r\n", cseq, dateHeader(),

destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()),

ntohs(serverRTCPPort.num()), destinationTTL,

fOurSessionId);

break;

case RTP_TCP:

// multicast streams can't be sent via TCP

handleCmd_unsupportedTransport(cseq);

break;

case RAW_UDP:

snprintf(

(char*) fResponseBuffer,

sizeof fResponseBuffer,

"RTSP/1.0 200 OK\r\n"

"CSeq: %s\r\n"

"%s"

"Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"

"Session: %08X\r\n\r\n", cseq, dateHeader(),

streamingModeString, destAddrStr, sourceAddrStr,

ntohs(serverRTPPort.num()), destinationTTL,

fOurSessionId);

break;

}

} else {

switch (streamingMode) {

case RTP_UDP: {

snprintf(

(char*) fResponseBuffer,

sizeof fResponseBuffer,

"RTSP/1.0 200 OK\r\n"

"CSeq: %s\r\n"

"%s"

"Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"

"Session: %08X\r\n\r\n", cseq, dateHeader(),

destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()),

ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()),

ntohs(serverRTCPPort.num()), fOurSessionId);

break;

}

case RTP_TCP: {

snprintf(

(char*) fResponseBuffer,

sizeof fResponseBuffer,

"RTSP/1.0 200 OK\r\n"

"CSeq: %s\r\n"

"%s"

"Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"

"Session: %08X\r\n\r\n", cseq, dateHeader(),

destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,

fOurSessionId);

break;

}

case RAW_UDP: {

snprintf(

(char*) fResponseBuffer,

sizeof fResponseBuffer,

"RTSP/1.0 200 OK\r\n"

"CSeq: %s\r\n"

"%s"

"Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"

"Session: %08X\r\n\r\n", cseq, dateHeader(),

streamingModeString, destAddrStr, sourceAddrStr,

ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),

fOurSessionId);

break;

}

}

}

delete[] destAddrStr;

delete[] sourceAddrStr;

delete[] streamingModeString;

} while (0);



delete[] concatenatedStreamName;

//返回后,回应字符串会被立即发送

}



     live555 中有两个 streamstate,一个是类 StreamState ,一个是此处的结构 struct streamState。类 SteamState 就是 streamToken,而 struct streamState 中保存了 MediaSubsession (即track) 和类 StreamState 的对应。类 StreamState 代表一个真正流动起来的数据流。这个数据流是从源流到 Sink 。客户端与服务端的一个 rtp 会话中,有两个数据流,服务端是从 XXXFileSouce 流到 RTPSink,而客户端则是从 RTPSource 流到 XXXFileSink 。建立数据流的过程就是把 Source 与 Sink 连接起来。

   

转载于:https://my.oschina.net/seanx/blog/620629

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值