摘要
DESCRIBE 请求收到返回SDP 就会根据SDP 创建rtp socket
轮询处理handleResponseBytes
在上一篇testRTSPClient中,已经走到
handleResponseBytes(bytesRead);//处理reponse 数据
主要处理了
1. 解析返回的response 数据
2. 根据返回response, 后续进一步处理(即回调函数continueAfterDESCRIBE() 等)
3. 根据返回response,OK 就会进行下一步的请求发送 (DESCRIBE–>SETUP)
具体code:
void RTSPClient::handleResponseBytes(int newBytesRead) {
//parse reponse code
while ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
//.... 判断是否是当前request
if (request->cseq() == cseq) {
foundRequest = request;
break;
}
}
//SETUP GET_PARAMETER TEAR_DOWN PLAY 请求reponse 处理
//......
//返回成功回调
if (responseSuccess) {
if (responseCode == 200) {
resultString = numBodyBytes > 0 ? strDup(bodyStart)
:strDup(publicParamsStr);
//...
}
//在DESCRIBE请求之后就是 continueAfterDESCRIBE回调
(*foundRequest->handler())(this, resultCode, resultString);
}
这里的cseq是根据RTSP 服务器返回的会话中提取
foundRequest通过fRequestsAwaitingResponse 队列获取,而在send 第一创建 request的时候,会放入队列中
unsigned RTSPClient::sendRequest(RequestRecord* request) {
//...
fRequestsAwaitingResponse.enqueue(request);
}
可以看出来RequestQueue 是一个单链表
void RTSPClient::RequestQueue::enqueue(RequestRecord* request) {
if (fTail == NULL) {
fHead = request;
} else {
fTail->next() = request;
}
fTail = request;
}
回调continueAfterDESCRIBE 函数
- 根据返回的DESCRIBE消息 ,本地创建MediaSession
- 发起SETUP请求
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
char* const sdpDescription = resultString;
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
// SETUP请求发起
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
} while (0);
}
创建MediaSession
创建MediaSession,作用就是解析sdpDescription 消息,获取信息
用wireshark解析如下图
wireshark 解析log地址
上面标记了MIME Type H264 告诉客户端解码用H264
Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
//....
if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
//....解析获取解码方式H264
}
setupNextSubsession 做了两件事情
- 创建socket 发送报文 即RTP
- 发起SETUP请求
void setupNextSubsession(RTSPClient* rtspClient) {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
scs.subsession = scs.iter->next();
if (scs.subsession != NULL) {
if (!scs.subsession->initiate()) {//创建RTP socket
//....
} else {
// Continue setting up this subsession, by sending a RTSP "SETUP" command:
rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP
, False, REQUEST_STREAMING_OVER_TCP);
}
return;
}
}
创建 RTP socket
scs.subsession->initiate()
Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
//创建RTP socket
fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum);
//创建 RTCP socket
fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
// Try to use a big receive buffer for RTP - at least 0.1 second of
// specified bandwidth and at least 50 KB
unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
if (rtpBufSize < 50 * 1024){
rtpBufSize = 50 * 1024;
increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);
}
// Create "fRTPSource" and "fReadSource":
if (!createSourceObjects(useSpecialRTPoffset)) break;
}
需要根据解析的解码方式创建不同的解码
Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
if (strcmp(fCodecName, "H264") == 0) {
fReadSource = fRTPSource
= H264VideoRTPSource::createNew(env(), fRTPSocket,//根据socket 作为数据入口
fRTPPayloadFormat,
fRTPTimestampFrequency);
}
}
然后就是SETUP发起流程