www.pudn.com > streamrtp.rar > rtcpcycle.c


/*-------------------------------------------------------------------------
 * rtcpcycle.c - rtcpcycle, rtcpcycleupdate
 *-------------------------------------------------------------------------
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*------------------------------------------------------------------------
 * rtcpcycle - do cyclic update on streams and genereate receiver reports.
 * Rtcycle returns seconds to wait until next rtcpcycle.
 *------------------------------------------------------------------------
 */
double
rtcpcycle(struct session *psn)
{

	struct rtcp         *prtcp, *ppacket;
	struct rr           *prr;
	struct rblock       *prb;
	int                 i, count, packets, bytes, active;
	int                 streamcount;
	ssrc_t              *sources;
	struct stream       **pstreams;

	/*
	 * Get pointers to pre-allocated (in rtpopen) memory.
	 */
	sources = psn->sn_cyclesources;
	pstreams = (struct stream **) sources;

	/* 
	 * Get list of sources in session.
	 */
	streamcount = rtpsources(psn, sources, RTCP_RRMAXRBLOCKS);
	if (streamcount <= 0)
		return rtcpinterval(1, 0,
				    psn->sn_bw * RTCP_BWFRAC, FALSE, 
				    0, &psn->sn_avgrtcp, FALSE);
  
	/*
	 * Iterate over SSRC list. Lookup each stream and
	 * determins if stream was active in previous RTCP
	 * cycle.
	 */
	for (i = 0, active = 0; i < streamcount; i++) {
		/*
		 * Hold on to the 32-bit pointers that will be returned
		 * by rtpgetstream in the space previously used by the
		 * 32-bit SSRC ID. The pointer is needed to release the
		 * stream structure later by calling rtpreleasestream.
		 */
		pstreams[i] = rtpgetstream(psn, sources[i]);
		if (pstreams[i] == NULL)
			continue;

		if (!RTP_INACTIVE(pstreams[i]->stm_inactive) && RTP_DATASEEN(pstreams[i])) {
			active++;
			/*
			 * Mark this stream for rblock generation.
			 */
			pstreams[i]->stm_mark = TRUE;
		}
	}

	/*
	 * Determine how many RTCP packets (within a compund packet
	 * must be sent. Each RTCP receiver report packet can hold only
	 * 31 reveiver report blocks (rblocks).
	 */
	packets = active / RTCP_MAXRBLOCKS + ((active % RTCP_MAXRBLOCKS) != 0);
	bytes = active * sizeof(struct rblock) + packets * (RTCP_HEADERSZ + sizeof(ssrc_t));

	ppacket = (struct rtcp *) psn->sn_cyclebuf;
	prtcp = ppacket;
	prr = (struct rr *) prtcp->rtcp_data;
	prb = (struct rblock *) prr->rr_rb;

	/*
	 * Iterate over the list of streams again, calling 
	 * rtcpcycleupdate on each to update its statistics
	 * and generate an rblock if the streams stm_mark
	 * flag is set.
	 */
	for (i = 0, count = 0; i < streamcount; i++) {
		if (pstreams[i] == NULL)
			continue;

		if (rtcpcycleupdate(psn, pstreams[i], prb) == FALSE) 
			continue;

		count++;
		prb++;      
    
		if (count == RTCP_MAXRBLOCKS || i + 1 == streamcount) {  
			/* 
			 * An RTCP receiver report can only hold 31 report blocks.
			 * When the maximum is reached *or* the last source in 
			 * the list of SSRCs is processed, generate an RTCP header.
			 */
			rtcpheader(prtcp, count, RTCP_RR, sizeof(struct rblock) * count + RTCP_HEADERSZ + sizeof(ssrc_t));
			prtcp->rtcp_length = htons(prtcp->rtcp_length);
			prr->rr_ssrc = htonl(psn->sn_ssrc);
      
			/*
			 * If there are streams remaining in the list,
			 * advance the pointers to the next rtcp packet.
			 */
			if (i + 1 != streamcount) {
				prtcp = (struct rtcp *) (prb + 1);
				prr = (struct rr *) prtcp->rtcp_data;
				prb = (struct rblock *) prr->rr_rb;
				count = 0;
			}
		}
	}
  
	/*
	 * Send the compound RTCP packet.
	 */
	rtcpsend(psn, ppacket, bytes);

	/* 
	 * Release all the streams.
	 */
	for (i = 0; i < streamcount; i++) 
		if (pstreams[i] != NULL)
			rtpreleasestream(psn, pstreams[i]);

	/*
	 * Return the number of seconds to wait before invoking
	 * rtcpcycle again.
	 */
	return rtcpinterval(streamcount + 1, active, psn->sn_bw * RTCP_BWFRAC, FALSE, bytes + 28, &psn->sn_avgrtcp, FALSE);
}

/*------------------------------------------------------------------------
 * rtcpcycleupdate - update stream stats and generate an rblock if
 * requested. Rtcpcycleupdate returns TRUE if an rblock is generated.
 *------------------------------------------------------------------------
 */

bool
rtcpcycleupdate(struct session *psn, struct stream *pstm, struct rblock *prb)
{

	int	        exthiseq, cumexpected, lostcycle, expcycle, reccycle;
	int		cumlost;
	struct timespec	now;

	pthread_mutex_lock(&pstm->stm_mutex);

	/* 
	 * Determine if the participant has timed out.
	 */
	if (RTP_DATASEEN(pstm) == FALSE && !RTP_INACTIVE(pstm->stm_inactive))
		if (++pstm->stm_inactive >= pstm->stm_inactthresh &&
		    pstm->stm_inactthresh != RTP_INFINITEINACTIVETHRESH) {
			pstm->stm_inactive = RTP_TIMEDOUT;
			pthread_mutex_unlock(&pstm->stm_mutex);
			rtpstreamcleanup(psn, pstm);
			rtppostevent(psn, EVENT_PARTICIPANT_TIMEOUT, pstm->stm_ssrc, NULL, 0);
			return FALSE;
		}

	/*
	 * Update statistics that must be updated once per 
	 * RTCP cycle.
	 */
	exthiseq = (pstm->stm_roll << 16) | pstm->stm_hiseq;
	cumexpected = exthiseq - pstm->stm_firstseq + 1;
	cumlost = cumexpected - pstm->stm_packets;
  
	/*
	 * If the stream is marked for rblock generation
	 * write the rblock in the area pointed to by prb.
	 */
	if (pstm->stm_mark == TRUE && prb != NULL) {
		prb->rb_ssrc = htonl(pstm->stm_ssrc);
		prb->rb_hiseq = htonl(exthiseq);
		prb->rb_jitter = htonl((unsigned int) pstm->stm_jitter);

		/*
		 * Cap cumlost to appropriate max/min values for 
		 * a 24-bit signed integer.
		 */
		prb->rb_cumlost = hton24(max(min(cumexpected - (int) pstm->stm_packets, 0x7fffff), -(1 << 23)));
		expcycle = cumexpected - pstm->stm_expprior;
		reccycle = pstm->stm_packets - pstm->stm_recprior;
		lostcycle = max(expcycle - reccycle, 0);
		prb->rb_fraclost = (expcycle == 0 ? 0 : (lostcycle << 8) / expcycle);
    
		/*
		 * Extract middle 32 bits of last sender
		 * report NTP timestamp.
		 */
		prb->rb_lastsrts = htonl(((pstm->stm_lastntp[1] & 0x0000ffff) << 16) |
					 ((pstm->stm_lastntp[0] & 0xffff0000) >> 16));

		/*
		 * Compute the delay since last sender
		 * report, if one has been received.
		 */
		if (pstm->stm_lastsr.tv_sec != 0) {
			clock_gettime(CLOCK_REALTIME, &now);
			prb->rb_delay = htonl(timeflatten(timesub(now, pstm->stm_lastsr), 65536));
		}    
	}

	pstm->stm_expprior = cumexpected;
	pstm->stm_recprior = pstm->stm_packets;

	pthread_mutex_unlock(&pstm->stm_mutex);

	if (pstm->stm_mark == TRUE) {
		pstm->stm_mark = FALSE;
		return TRUE;
	}
	else
		return FALSE;
}