前言
在DESCRIBE 请求流程,已经将 live555 处理机制走了一遍,所以在SETUP请求过程中也是适用的
SETUP
rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
unsigned RTSPClient::sendSetupCommand(MediaSubsession& subsession
, responseHandler* responseHandler
,Boolean streamOutgoing
, Boolean forceMulticastOnUnspecified
, Authenticator* authenticator) {
//...
return sendRequest(new RequestRecord(++fCSeq, "SETUP", responseHandler
, NULL, &subsession, booleanFlags));
}
所以在sendRequest方法中因为已经打开了socket所以只是将 SETUP请求放入 fRequestsAwaitingConnection队列中
unsigned RTSPClient::sendRequest(RequestRecord* request) {
//....
if (fTunnelOverHTTPPortNum == 0
|| strcmp(request->commandName(), "POST") != 0) {
fRequestsAwaitingResponse.enqueue(request);
}
}
在事件轮询的时候,即 SingleStep 中处理
最后回调request 请求的回调函数 handlerProc ,SETUP请求的回调是continueAfterSETUP
continueAfterSETUP
- 创建sink 用来准备接收数据
- 准备接收
- 发起PLAY请求
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode
, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
//创建sink 接收数据
scs.subsession->sink = DummySink::createNew(env, *scs.subsession
, rtspClient->url());
//开始播放
scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
subsessionAfterPlaying, scs.subsession);
} while (0);
// Set up the next subsession, if any:
setupNextSubsession(rtspClient);
}
startPlaying
主流程如下图:
最关键数据读取和处理在MultiFramedRTPSource::doGetNextFrame
- 将网络传输的RTP包读取放在事件轮询中处理,通过networkReadHandler 读取
- doGetNextFrame1 中判断读取buffer数据的情况,进行下一帧读取或者结束
void MultiFramedRTPSource::doGetNextFrame() {
if (!fAreDoingNetworkReads) {
// Turn on background read handling of incoming packets:
fAreDoingNetworkReads = True;
TaskScheduler::BackgroundHandlerProc* handler
= (TaskScheduler::BackgroundHandlerProc*)&networkReadHandler;
fRTPInterface.startNetworkReading(handler);
}
fSavedTo = fTo;
fSavedMaxSize = fMaxSize;
fFrameSize = 0; // for now
fNeedDelivery = True;
doGetNextFrame1();
}
通过了轮询不断读取服务器端读过来的数据,即使用networkReadHandler 不断轮询回调,而networkReadHandler具体操作
void MultiFramedRTPSource::networkReadHandler1() {
BufferedPacket* bPacket = fPacketReadInProgress;
if (bPacket == NULL) {
bPacket = fReorderingBuffer->getFreePacket(this);
}
// Read the network packet, and perform sanity checks on the RTP header:
Boolean readSuccess = False;
do {
if (!bPacket->fillInData(fRTPInterface, fromAddress, packetReadWasIncomplete)) {
fPacketReadInProgress = NULL;
break;
}
/××
× 对RTP包进行预处理,RTP Header 处理
×/
if (!fReorderingBuffer->storePacket(bPacket)) break;
readSuccess = True;
} while (0);
doGetNextFrame1();
// If we didn't get proper data this time, we'll get another chance
}
在bPacket中填充数据 fillInData
readSuccess = fGS->handleRead(buffer, bufferMaxSize, bytesRead, fromAddress);
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
struct sockaddr_in& fromAddressAndPort) {
//... 从之前创建的socket 中读取数据通过numBytes判断成功还是失败
int numBytes = readSocket(env(), socketNum(),
buffer, maxBytesToRead, fromAddressAndPort);
}
补充 FU-A 组包过程
在获取网络RTP包的时候, MultiFramedRTPSource::networkReadHandler1处理,首先检查RTP header 共12个字节,并且剔除,保留NALU
取出 RTP 头
// Check for the 12-byte RTP header:
if (bPacket->dataSize() < 12) break;
unsigned rtpHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
Boolean rtpMarkerBit = (rtpHdr&0x00800000) != 0;
unsigned rtpTimestamp = ntohl(*(u_int32_t*)(bPacket->data()));ADVANCE(4);
unsigned rtpSSRC = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
FU-A 数据 拼成完整的一帧
H264VideoRTPSource::processSpecialHeader 中处理 FU-A 的FU indicator 和FU Header
//当处于FU-A start 的时候
headerStart[1] = (headerStart[0]&0xE0)|(headerStart[1]&0x1F); //将FU-A indicator 和 Header 转化为 NALU header
numBytesToSkip = 1; //设置跳过1个字节
//当不是start 的时候,直接跳过 2个字节
fCurrentPacketBeginsFrame = False;
numBytesToSkip = 2;
resultSpecialHeaderSize = numBytesToSkip;
nextPacket->skip(specialHeaderSize);
FU 处理完 indicator和 header 将剩余的一起拼起来组成完整一帧
nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
fCurPacketMarkerBit);
memmove(to, newFramePtr, bytesUsed);//将新的packet 移动到 fReceiveBuffer
而doGetNextFrame1 如果继续下一帧读取,会继续回调DummySink::afterGettingFrame ,又是读取一帧的流程,因为在SETUP 步骤,还没有发送PLAY请求,所以服务器端不会有数据传过来,一直轮询等待
判断地方:
void MultiFramedRTPSource::doGetNextFrame1() {
BufferedPacket* nextPacket
= fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);
if (nextPacket == NULL) break;
//.....
}
最后接收数据都会在 fReceiveBuffer,因为
Boolean DummySink::continuePlaying() {
if (fSource == NULL) return False; // sanity check (should not happen)
// Request the next frame of data from our input source. "afterGettingFrame()" will get called later, when it arrives:
fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
afterGettingFrame, this,
onSourceClosure, this);
return True;
}
因为DummySink 没有对接收的包处理,所以暂时只能抓数据但是没有解码出来
对SETUP请求response消息处理
在事件轮询中已经将 SETUP PLAY 等返回消息进行处理
void RTSPClient::handleResponseBytes(int newBytesRead) {
if (responseCode == 200) {
// Do special-case response handling for some commands:
if (strcmp(foundRequest->commandName(), "SETUP") == 0) {
if (!handleSETUPResponse(*foundRequest->subsession(), sessionParamsStr, transportParamsStr, foundRequest->booleanFlags()&0x1)) break;
} else if (strcmp(foundRequest->commandName(), "PLAY") == 0) {
if (!handlePLAYResponse(foundRequest->session(), foundRequest->subsession(), scaleParamsStr, speedParamsStr, rangeParamsStr, rtpInfoParamsStr)) break;
} else if (strcmp(foundRequest->commandName(), "TEARDOWN") == 0) {
if (!handleTEARDOWNResponse(*foundRequest->session(), *foundRequest->subsession())) break;
} else if (strcmp(foundRequest->commandName(), "GET_PARAMETER") == 0) {
if (!handleGET_PARAMETERResponse(foundRequest->contentStr(), bodyStart, responseEnd)) break;
}
}
}