live555中默认支持RTCP,如果要监视网络状态就要了解RTCP。我们这里以openRTSP为例看看RTCP的过程。
在前面的openRTSP分析中分析了openRTSP的流程,其中在在continueAfterDESCRIBE中有subsession->initiate(simpleRTPoffsetArg),在这里进行了RTP socket和RTCP socket的建立。
- if (isSSM()) {
- fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
- } else {
- fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
- }
if (isSSM()) {
fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
} else {
fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
}
这里建立了RTCP 的socket,下面初始化fRTCPInstance
- if (fRTPSource != NULL && fRTCPSocket != NULL) {// Finally, create our RTCP instance. (It starts running automatically)
- // If bandwidth is specified, use it and add 5% for RTCP overhead.
- // Otherwise make a guess at 500 kbps.
- unsigned totSessionBandwidth
- = fBandwidth ? fBandwidth + fBandwidth / 20 : 500;
- fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,
- totSessionBandwidth,
- (unsigned char const*)
- fParent.CNAME(),
- NULL /* we're a client */,
- fRTPSource);
- if (fRTCPInstance == NULL) {
- env().setResultMsg("Failed to create RTCP instance");
- break;
- }
- }
if (fRTPSource != NULL && fRTCPSocket != NULL) {// Finally, create our RTCP instance. (It starts running automatically)
// If bandwidth is specified, use it and add 5% for RTCP overhead.
// Otherwise make a guess at 500 kbps.
unsigned totSessionBandwidth
= fBandwidth ? fBandwidth + fBandwidth / 20 : 500;
fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,
totSessionBandwidth,
(unsigned char const*)
fParent.CNAME(),
NULL /* we're a client */,
fRTPSource);
if (fRTCPInstance == NULL) {
env().setResultMsg("Failed to create RTCP instance");
break;
}
}
根据注释提示,fRTCPInstance会自动开始运行,如何运行的呢,我们再来看看。
- RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs,
- unsigned totSessionBW,
- unsigned char const* cname,
- RTPSink* sink, RTPSource const* source,
- Boolean isSSMSource) {
- return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source,
- isSSMSource);
- }
RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs,
unsigned totSessionBW,
unsigned char const* cname,
RTPSink* sink, RTPSource const* source,
Boolean isSSMSource) {
return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source,
isSSMSource);
}
为了过程的清晰,这里还是贴出所有代码
- RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,
- unsigned totSessionBW,
- unsigned char const* cname,
- RTPSink* sink, RTPSource const* source,
- Boolean isSSMSource)
- : Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW),
- fSink(sink), fSource(source), fIsSSMSource(isSSMSource),
- fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1),
- fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0),
- fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0),
- fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE),
- fHaveJustSentPacket(False), fLastPacketSentSize(0),
- fByeHandlerTask(NULL), fByeHandlerClientData(NULL),
- fSRHandlerTask(NULL), fSRHandlerClientData(NULL),
- fRRHandlerTask(NULL), fRRHandlerClientData(NULL),
- fSpecificRRHandlerTable(NULL) {
- #ifdef DEBUG
- fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this);
- #endif
- if (fTotSessionBW == 0) { // not allowed!
- env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n";
- fTotSessionBW = 1;
- }
- if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicast
- double timeNow = dTimeNow();
- fPrevReportTime = fNextReportTime = timeNow;
- fKnownMembers = new RTCPMemberDatabase(*this);
- fInBuf = new unsigned char[maxPacketSize];
- if (fKnownMembers == NULL || fInBuf == NULL) return;
- fNumBytesAlreadyRead = 0;
- // A hack to save buffer space, because RTCP packets are always small:
- unsigned savedMaxSize = OutPacketBuffer::maxSize;
- OutPacketBuffer::maxSize = maxPacketSize;
- fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize);
- OutPacketBuffer::maxSize = savedMaxSize;
- if (fOutBuf == NULL) return;
- // Arrange to handle incoming reports from others:
- TaskScheduler::BackgroundHandlerProc* handler
- = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;
- fRTCPInterface.startNetworkReading(handler);
- // Send our first report.
- fTypeOfEvent = EVENT_REPORT;
- onExpire(this);
- }
RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,
unsigned totSessionBW,
unsigned char const* cname,
RTPSink* sink, RTPSource const* source,
Boolean isSSMSource)
: Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW),
fSink(sink), fSource(source), fIsSSMSource(isSSMSource),
fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1),
fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0),
fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0),
fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE),
fHaveJustSentPacket(False), fLastPacketSentSize(0),
fByeHandlerTask(NULL), fByeHandlerClientData(NULL),
fSRHandlerTask(NULL), fSRHandlerClientData(NULL),
fRRHandlerTask(NULL), fRRHandlerClientData(NULL),
fSpecificRRHandlerTable(NULL) {
#ifdef DEBUG
fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this);
#endif
if (fTotSessionBW == 0) { // not allowed!
env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n";
fTotSessionBW = 1;
}
if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicast
double timeNow = dTimeNow();
fPrevReportTime = fNextReportTime = timeNow;
fKnownMembers = new RTCPMemberDatabase(*this);
fInBuf = new unsigned char[maxPacketSize];
if (fKnownMembers == NULL || fInBuf == NULL) return;
fNumBytesAlreadyRead = 0;
// A hack to save buffer space, because RTCP packets are always small:
unsigned savedMaxSize = OutPacketBuffer::maxSize;
OutPacketBuffer::maxSize = maxPacketSize;
fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize);
OutPacketBuffer::maxSize = savedMaxSize;
if (fOutBuf == NULL) return;
// Arrange to handle incoming reports from others:
TaskScheduler::BackgroundHandlerProc* handler
= (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;
fRTCPInterface.startNetworkReading(handler);
// Send our first report.
fTypeOfEvent = EVENT_REPORT;
onExpire(this);
}
可以看到这里加入了handler,handler为incomingReportHandler。看看这个startNetworkReading。
- void RTPInterface
- ::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) {
- // Normal case: Arrange to read UDP packets:
- envir().taskScheduler().
- turnOnBackgroundReadHandling(fGS->socketNum(), handlerProc, fOwner);
- // Also, receive RTP over TCP, on each of our TCP connections:
- fReadHandlerProc = handlerProc;
- for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
- streams = streams->fNext) {
- // Get a socket descriptor for "streams->fStreamSocketNum":
- SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum);
- // Tell it about our subChannel:
- socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this);
- }
- }
void RTPInterface
::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) {
// Normal case: Arrange to read UDP packets:
envir().taskScheduler().
turnOnBackgroundReadHandling(fGS->socketNum(), handlerProc, fOwner);
// Also, receive RTP over TCP, on each of our TCP connections:
fReadHandlerProc = handlerProc;
for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
streams = streams->fNext) {
// Get a socket descriptor for "streams->fStreamSocketNum":
SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum);
// Tell it about our subChannel:
socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this);
}
}
第一句代码表示默认是通过udp来发送rtcp的包,如果rtp是tcp的就通过下面的代码处理。
这里的turnOnBackgroundReadHanding就是调用setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);在前面分析过就是将socket加入到select set集中进行监视并传入handlerProc。这里不做重复讨论,接着看下面的注释是send our first report,接着调用了onExpire(this),来看看
- void RTCPInstance::onExpire(RTCPInstance* instance) {
- instance->onExpire1();
- }
void RTCPInstance::onExpire(RTCPInstance* instance) {
instance->onExpire1();
}
- void RTCPInstance::onExpire1() {
- // Note: fTotSessionBW is kbits per second
- double rtcpBW = 0.05*fTotSessionBW*1024/8; // -> bytes per second
- OnExpire(this, // event
- numMembers(), // members
- (fSink != NULL) ? 1 : 0, // senders
- rtcpBW, // rtcp_bw
- (fSink != NULL) ? 1 : 0, // we_sent
- &fAveRTCPSize, // ave_rtcp_size
- &fIsInitial, // initial
- dTimeNow(), // tc
- &fPrevReportTime, // tp
- &fPrevNumMembers // pmembers
- );
- }
void RTCPInstance::onExpire1() {
// Note: fTotSessionBW is kbits per second
double rtcpBW = 0.05*fTotSessionBW*1024/8; // -> bytes per second
OnExpire(this, // event
numMembers(), // members
(fSink != NULL) ? 1 : 0, // senders
rtcpBW, // rtcp_bw
(fSink != NULL) ? 1 : 0, // we_sent
&fAveRTCPSize, // ave_rtcp_size
&fIsInitial, // initial
dTimeNow(), // tc
&fPrevReportTime, // tp
&fPrevNumMembers // pmembers
);
}
再来看看这个OnExpire
- void OnExpire(event e,
- int members,
- int senders,
- double rtcp_bw,
- int we_sent,
- double *avg_rtcp_size,
- int *initial,
- time_tp tc,
- time_tp *tp,
- int *pmembers)
- {
- /* This function is responsible for deciding whether to send
- * an RTCP report or BYE packet now, or to reschedule transmission.
- * It is also responsible for updating the pmembers, initial, tp,
- * and avg_rtcp_size state variables. This function should be called
- * upon expiration of the event timer used by Schedule(). */
- double t; /* Interval */
- double tn; /* Next transmit time */
- /* In the case of a BYE, we use "unconditional reconsideration" to
- * reschedule the transmission of the BYE if necessary */
- if (TypeOfEvent(e) == EVENT_BYE) {
- t = rtcp_interval(members,
- senders,
- rtcp_bw,
- we_sent,
- *avg_rtcp_size,
- *initial);
- tn = *tp + t;
- if (tn <= tc) {
- SendBYEPacket(e);
- exit(1);
- } else {
- Schedule(tn, e);
- }
- } else if (TypeOfEvent(e) == EVENT_REPORT) {
- t = rtcp_interval(members,
- senders,
- rtcp_bw,
- we_sent,
- *avg_rtcp_size,
- *initial);
- tn = *tp + t;
- if (tn <= tc) {
- SendRTCPReport(e);
- *avg_rtcp_size = (1./16.)*SentPacketSize(e) +
- (15./16.)*(*avg_rtcp_size);
- *tp = tc;
- /* We must redraw the interval. Don't reuse the
- one computed above, since its not actually
- distributed the same, as we are conditioned
- on it being small enough to cause a packet to
- be sent */
- t = rtcp_interval(members,
- senders,
- rtcp_bw,
- we_sent,
- *avg_rtcp_size,
- *initial);
- Schedule(t+tc,e);
- *initial = 0;
- } else {
- Schedule(tn, e);
- }
- *pmembers = members;
- }
- }