前言
在 live555 rtspserver端 创建并且接收客户端数据 根据rtsp 协议需要根据 handleRequestBytes 处理请求数据
而在 SETUP 请求中,已经准备好了 RTP_UDP 传输的socket,所以进行 PLAY 请求的处理
LIVE555 主要在函数 handleCmd_PLAY处理
但还是从handleRequestBytes 开始分析
PLAY请求数据 解析
首先是解析请求数据parseRTSPRequestString
log 如下:
···
PLAY rtsp://192.168.0.10:8554/H264Video.mkv/ RTSP/1.0
CSeq: 4
Range: npt=0-
Session: 817D3E3E
Date: Sat, 01 Jan 2000 04:50:00 GMT
cmdName “PLAY”, urlPreSuffix “H264Video.mkv”, urlSuffix “”, CSeq “4”, Content-Length 0, with 0 bytes following the message
sending REPORT
sending RTCP packet
80c80006 18be4fac bc17c32c 3d19dece 12d9fa8b 00000000 00000000 81ca0004 18be4fac 01096c6f 63616c68 6f737400
sending response:
RTSP/1.0 200 OK
CSeq: 4
Date: Sat, Jan 01 2000 00:05:00 GMT
Range: npt=0.000-
Session: 817D3E3E
RTP-Info: url=rtsp://192.168.0.10:8554/H264Video/track1;seq=30437;rtptime=316301736
···
处理 PLAY请求
clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer)
handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);
void RTSPServer::RTSPClientSession
::handleCmd_PLAY(RTSPServer::RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession, char const* fullRequestStr) {
// Create a "RTP-Info:" line. It will get filled in from each subsession's state:
char const* rtpInfoFmt =
"%s" // "RTP-Info:", plus any preceding rtpInfo items
"%s" // comma separator, if needed
"url=%s/%s"
";seq=%d"
";rtptime=%u"
;
// Create the "Range:" header that we'll send back in our response.
// Now, start streaming:
for (i = 0; i < fNumStreamStates; ++i) {
if (subsession == NULL /* means: aggregated operation */
|| subsession == fStreamStates[i].subsession) {
fStreamStates[i].subsession->startStream(fOurSessionId,
fStreamStates[i].streamToken,
(TaskFunc*)noteClientLiveness, this,
rtpSeqNum, rtpTimestamp,
RTSPServer::RTSPClientConnection::handleAlternativeRequestByte,
ourClientConnection);
sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo, numRTPInfoItems++ == 0 ? "" : ",",rtspURL,
urlSuffix,rtpSeqNum,rtpTimestamp);
}
}
// Fill in the response:
snprintf((char*)ourClientConnection->fResponseBuffer,
sizeof ourClientConnection->fResponseBuffer,
"RTSP/1.0 200 OK\r\n"
"CSeq: %s\r\n"
"%s"
"%s"
"%s"
"Session: %08X\r\n"
"%s\r\n",
ourClientConnection->fCurrentCSeq,
dateHeader(),
scaleHeader,
rangeHeader,
fOurSessionId,
rtpInfo);
}
cmd play 请求的时候,会开始发送数据流
fStreamStates[i].subsession->startStream(…)
startStream 参数准备
下一步就是分析数据流,如何发送给客户端
传输的参数:
fOurSessionId,fStreamStates[i].streamToken
,(TaskFunc*)noteClientLiveness, this, rtpSeqNum, rtpTimestamp,
RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection
- fOurSessionId :
在CMD “SETUP”请求处理过程中在 createNewClientSessionWithId 创建ClientSession 产生的随机数
sessionId = (u_int32_t)our_random32();
- fStreamStates[i].streamToken : 用来保存流的中状态 在”SETUP” 中创建
struct streamState {
ServerMediaSubsession* subsession;
int tcpSocketNum;
void* streamToken;
} * fStreamStates;
void RTSPServer::RTSPClientSession
::handleCmd_SETUP(RTSPServer::RTSPClientConnection* ourClientConnection,
char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
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):
fStreamStates = new struct streamState[fNumStreamStates];
for (unsigned i = 0; i < fNumStreamStates; ++i) {
subsession = iter.next();
fStreamStates[i].subsession = subsession;
fStreamStates[i].tcpSocketNum = -1; // for now; may get set for RTP-over-TCP streaming
fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
}
}
}
而 session的建立 也是在SETUP中
ServerMediaSession* sms
= fOurServer.lookupServerMediaSession(streamName, fOurServerMediaSession == NULL);
tcpSocketNum RTP-over-TCP 我们用的是RTP-over-UDP,不关心
streamToken: 在处理SETUP 请求handleCmd_SETUP会创建
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) {
// Set up the state of the stream. The stream will get started later:
streamToken = fLastStreamToken
= new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,
streamBitrate, mediaSource,
rtpGroupsock, rtcpGroupsock);
}
- (TaskFunc*)noteClientLiveness
回调函数 用来说明 session的connection , 还能判断是否超时rtcp处理 看log
Client session (id "817D3E3E", stream name "H264Video.mkv"): Liveness indication
- rtpSeqNum = 0
rtpTimestamp 时间戳
RTSPServer::RTSPClientConnection::handleAlternativeRequestByte回调函数
ourClientConnection 指的是 RTSPServer::RTSPClientConnection 实例
开始传输视频流的流程
void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,
void* streamToken,
TaskFunc* rtcpRRHandler,
void* rtcpRRHandlerClientData,
unsigned short& rtpSeqNum,
unsigned& rtpTimestamp,
ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
void* serverRequestAlternativeByteHandlerClientData) {
StreamState* streamState = (StreamState*)streamToken;
if (streamState != NULL) {
streamState->startPlaying(destinations, clientSessionId,
rtcpRRHandler, rtcpRRHandlerClientData,
serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
}
}
RTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
/*
RTPSink ------------H264VideoRTPSink
fMediaSource--------H264VideoStreamFramer / H264VideoStreamDiscreteFramer
this --------- StreamState
*/
最后走的方法是
MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket)
然后就是打包一帧
packFrame();
最后就是发送包
sendPacketIfNecessary();
上面的流程是大体的,还需要后面具体分析打包 过程
发送 RESPONSE 消息
上面的代码. fResponseBuffer 最后会被发送,通过 send 函数