一、引言
本文对应《RFC 3550》的附录A(Appendix A. Algorithms)。
二、Appendix A. Algorithms
根据《RFC 3550》第62页,《RFC 3550》提供了有关RTP发送方和接收方算法的C代码示例。在特定的运行环境下,可能还有其他更快或更有优势的实现方法。这些实施说明仅供参考,旨在阐明RTP规范。
以下定义用于所有示例;为简洁明了起见,结构定义(structure definitions)仅适用于32位大八位(32-bit big-endian,最重要八位在前)架构。比特字段(Bit fields)假定按大十进制比特顺序(big-endian bit order)紧密排列,没有额外的填充。要构建可移植的实现,需要进行修改:
/*
* rtp.h -- RTP header file
*/
#include <sys/types.h>
/*
* The type definitions below are valid for 32-bit architectures and
* may have to be adjusted for 16- or 64-bit architectures.
*/
typedef unsigned char u_int8;
typedef unsigned short u_int16;
typedef unsigned int u_int32;
typedef short int16;
/*
* Current protocol version.
*/
#define RTP_VERSION 2
#define RTP_SEQ_MOD (1<<16)
#define RTP_MAX_SDES 255 /* maximum text length for SDES */
typedef enum {
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
RTCP_BYE = 203,
RTCP_APP = 204
} rtcp_type_t;
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
RTCP_SDES_EMAIL = 3,
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8
} rtcp_sdes_type_t;
/*
* RTP data header
*/
typedef struct {
unsigned int version:2; /* protocol version */
unsigned int p:1; /* padding flag */
unsigned int x:1; /* header extension flag */
unsigned int cc:4; /* CSRC count */
unsigned int m:1; /* marker bit */
unsigned int pt:7; /* payload type */
unsigned int seq:16; /* sequence number */
u_int32 ts; /* timestamp */
u_int32 ssrc; /* synchronization source */
u_int32 csrc[1]; /* optional CSRC list */
} rtp_hdr_t;
/*
* RTCP common header word
*/
typedef struct {
unsigned int version:2; /* protocol version */
unsigned int p:1; /* padding flag */
unsigned int count:5; /* varies by packet type */
unsigned int pt:8; /* RTCP packet type */
u_int16 length; /* pkt len in words, w/o this word */
} rtcp_common_t;
/*
* Big-endian mask for version, padding bit and packet type pair
*/
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
#define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR)
/*
* Reception report block
*/
typedef struct {
u_int32 ssrc; /* data source being reported */
unsigned int fraction:8; /* fraction lost since last SR/RR */
int lost:24; /* cumul. no. pkts lost (signed!) */
u_int32 last_seq; /* extended last seq. no. received */
u_int32 jitter; /* interarrival jitter */
u_int32 lsr; /* last SR packet from this source */
u_int32 dlsr; /* delay since last SR packet */
} rtcp_rr_t;
/*
* SDES item
*/
typedef struct {
u_int8 type; /* type of item (rtcp_sdes_type_t) */
u_int8 length; /* length of item (in octets) */
char data[1]; /* text, not null-terminated */
} rtcp_sdes_item_t;
/*
* One RTCP packet
*/
typedef struct {
rtcp_common_t common; /* common header */
union {
/* sender report (SR) */
struct {
u_int32 ssrc; /* sender generating this report */
u_int32 ntp_sec; /* NTP timestamp */
u_int32 ntp_frac;
u_int32 rtp_ts; /* RTP timestamp */
u_int32 psent; /* packets sent */
u_int32 osent; /* octets sent */
rtcp_rr_t rr[1]; /* variable-length list */
} sr;
/* reception report (RR) */
struct {
u_int32 ssrc; /* receiver generating this report */
rtcp_rr_t rr[1]; /* variable-length list */
} rr;
/* source description (SDES) */
struct rtcp_sdes {
u_int32 src; /* first SSRC/CSRC */
rtcp_sdes_item_t item[1]; /* list of SDES items */
} sdes;
/* BYE */
struct {
u_int32 src[1]; /* list of sources */
/* can’t express trailing text for reason */
} bye;
} r;
} rtcp_t;
typedef struct rtcp_sdes rtcp_sdes_t;
/*
* Per-source state information
*/
typedef struct {
u_int16 max_seq; /* highest seq. number seen */
u_int32 cycles; /* shifted count of seq. number cycles */
u_int32 base_seq; /* base seq number */
u_int32 bad_seq; /* last ’bad’ seq number + 1 */
u_int32 probation; /* sequ. packets till source is valid */
u_int32 received; /* packets received */
u_int32 expected_prior; /* packet expected at last interval */
u_int32 received_prior; /* packet received at last interval */
u_int32 transit; /* relative trans time for prev pkt */
u_int32 jitter; /* estimated jitter */
/* ... */
} source;
三、RTP Data Header Validity Checks
根据《RFC 3550》第65页,RTP接收器应检查传入数据包上RTP标头(RTP header)的有效性(validity ),因为这些数据包可能被加密,或可能来自不同的应用程序,而这些应用程序恰好被错误编址。同样,如果启用了《RFC 3550》第 9节所述的加密方法,则需要进行报头有效性检查,以验证传入数据包是否已正确解密,尽管报头有效性检查(header validity check)失败(如未知有效载荷类型)不一定表示解密失败:
1.RTP版本字段(RTP version field)必须等于2。
2.必须知道有效载荷类型(payload type),尤其不能等于SR或RR。
3.如果设置了P位(P bit),那么数据包的最后一个八位位组必须包含有效的八位位组计数,特别是小于数据包总长度减去报头(header)大小。
4.如果配置文件(profile)未指定可以使用报头扩展(header extension)机制,则X位(X bit)必须为0。否则,扩展长度(extension length)字段必须小于数据包总大小减去固定报头(fixed header)长度和填充。
5.数据包的长度必须与CC和有效载荷类型(payload type)一致(如果有效载荷有已知长度)。
后三项检查有些复杂,而且并不总是可行,因此只剩下前两项,总共只有几个比特。如果数据包中的SSRC标识符(SSRC identifier)是以前接收过的标识符,那么数据包可能是有效的,检查序列号是否在预期范围内可进一步验证。如果SSRC标识符以前从未出现过,那么携带该标识符的数据包可能会被视为无效,直到有少量带有连续序列号(sequence numbers)的数据包到达为止。这些无效数据包可能会被丢弃,也可能会被储存起来,在验证完成后再发送,如果由此造成的延迟可以接受的话。
下图所示的例程update_seq可确保只有在按顺序收到MIN_SEQUENTIAL数据包后,才宣布数据源有效。它还会验证新接收数据包的序列号seq,并更新s所指向结构中数据包源的序列状态。
当首次听到一个新信源时,即其SSRC标识不在表中(见《RFC 3550》第8.2节),并为其分配了每个信源状态时,s->probation将被设置为宣布信源有效前所需的连续数据包数(参数 MIN_SEQUENTIAL),其他变量也将被初始化:
init_seq(s, seq);
s->max_seq = seq - 1;
s->probation = MIN_SEQUENTIAL;
非0的s->probation会将源代码标记为无效,因此状态可能会在短时间超时后而不是长时间超时后被丢弃,《RFC 3550》第6.2.1节对此进行了讨论。
在源被视为有效后,如果序列号比s->max_seq超前不超过MAX_DROPOUT,也不落后于 MAX_MISORDER,则序列号被视为有效。如果新的序列号在 RTP 序列号范围(16 位)的模数 max_seq 之前,但小于 max_seq,则表示序列号已被包围,序列号周期的(移位)计数将递增。
否则,返回值为 0,表示验证失败,并存储坏序列号(bad sequence number)加 1。如果接收到的下一个数据包带有下一个更高的序列号,则该数据包被认为是新数据包序列的有效起始,可能是由于扩展的丢包或源重启造成的。由于可能遗漏了多个完整的序列号周期,因此数据包丢失统计(packet loss statistics)将被重置。
所示为参数的典型值,基于50个数据包/秒的最大错序时间为2 秒,最大丢包时间为1分钟。丢弃参数MAX_DROPOUT应为16位序列号空间的一小部分,以提供一个合理的概率,即重启后的新序列号不会落在重启前序列号的可接受范围内。
void init_seq(source *s, u_int16 seq)
{
s->base_seq = seq;
s->max_seq = seq;
s->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
s->cycles = 0;
s->received = 0;
s->received_prior = 0;
s->expected_prior = 0;
/* other initialization */
}
int update_seq(source *s, u_int16 seq)
{
u_int16 udelta = seq - s->max_seq;
const int MAX_DROPOUT = 3000;
const int MAX_MISORDER = 100;
const int MIN_SEQUENTIAL = 2;
/*
* Source is not valid until MIN_SEQUENTIAL packets with
* sequential sequence numbers have been received.
*/
if (s->probation) {
/* packet is in sequence */
if (seq == s->max_seq + 1) {
s->probation--;
s->max_seq = seq;
if (s->probation == 0) {
init_seq(s, seq);
s->received++;
return 1;
}
} else {
s->probation = MIN_SEQUENTIAL - 1;
s->max_seq = seq;
}
return 0;
} else if (udelta < MAX_DROPOUT) {
/* in order, with permissible gap */
if (seq < s->max_seq) {
/*
* Sequence number wrapped - count another 64K cycle.
*/
s->cycles += RTP_SEQ_MOD;
}
s->max_seq = seq;
} else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) {
/* the sequence number made a very large jump */
if (seq == s->bad_seq) {
/*
* Two sequential packets -- assume that the other side
* restarted without telling us so just re-sync
* (i.e., pretend this was the first packet).
*/
init_seq(s, seq);
}
else {
s->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
return 0;
}
} else {
/* duplicate or reordered packet */
}
s->received++;
return 1;
}
可以加强有效性检查,要求两个以上的数据包依次传送。其缺点是会有更多的初始数据包被丢弃(或在队列中延迟),而且高数据包丢失率可能会妨碍验证。不过,由于RTCP报头验证相对较强,如果在数据包之前收到来自源的RTCP数据包,则可以调整计数,使其只需要两个数据包的顺序。如果可以容忍几秒钟的初始数据丢失,应用程序可以选择丢弃来自数据源的所有数据包,直到收到来自该数据源的有效RTCP数据包。
根据应用和编码的不同,算法可能会利用有关有效载荷格式的其他知识进行进一步验证。对于所有数据包的时间戳增量都相同的有效载荷类型,可以利用序列号差值(假设有效载荷类型没有变化)从同一来源收到的前一个数据包中预测时间戳值。
可以进行强 “快速路径(fast-path)”检查,因为新接收到的RTP数据包头部的前四个八进制数很可能与来自同一SSRC的上一个数据包的前四个八进制数相同,只是序列号增加了一个。同样,在通常一次只从一个数据源接收数据的应用中,单入口高速缓存(single-entry cache)可用于加快SSRC查找速度。
四、RTCP Header Validity Checks
根据《RFC 3550》第69页,应对RTCP数据包进行以下检查。
1.RTP版本(RTP version)字段必须等于2。
2.复合数据包中第一个RTCP数据包的有效载荷类型(payload type )字段必须等于 SR或RR。
3.复合RTCP数据包的第一个数据包的填充位 (P) 应为0,因为只有在需要时才会在最后一个数据包中使用填充。
4.单个RTCP数据包的长度(length)字段必须与接收到的复合 RTCP 数据包的总长度相加。这是一种相当严格的检查。
下面的代码片段将执行所有这些检查。对后续数据包不检查数据包类型,因为可能存在未知的数据包类型,应予以忽略:
u_int32 len; /* length of compound RTCP packet in words */
rtcp_t *r; /* RTCP header */
rtcp_t *end; /* end of compound RTCP packet */
if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
/* something wrong with packet format */
}
end = (rtcp_t *)((u_int32 *)r + len);
do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
while (r < end && r->common.version == 2);
if (r != end) {
/* something wrong with packet format */
}
五、Determining Number of Packets Expected and Lost
根据《RFC 3550》第69页,为了计算数据包丢失率,需要使用下面代码中通过指针s引用的struct source中定义的每个数据源状态信息,了解每个数据源预计和实际接收到的RTP数据包数量。接收到的数据包数量只是数据包到达时的计数,包括任何延迟或重复的数据包。接收方可以根据收到的最高序列号(s->max_seq)和收到的第一个序列号(s->base_seq)之差来计算预计的数据包数量。由于序列号只有16 位并会缠绕(wrap around),因此有必要用序列号缠绕的(移位)次数(s->cycles)来扩展最高序列号。接收到的数据包计数和周期计数都由《RFC 3550》附录A.1中的RTP报头有效性检查(RTP header validity check)例程进行维护。
extended_max = s->cycles + s->max_seq;
expected = extended_max - s->base_seq + 1;
数据包丢失数(The number of packets lost)的定义是预期数据包数减去实际收到的数据包数:
lost = expected - s->received;
由于这个带符号的数字以24位为单位,因此,如果是正损耗,则应将其箝位(clamped at)在0x7fffff处,如果是负损耗,则应将其箝位在0x800000处,而不是绕来绕去。
上次报告间隔内(自上次SR或RR 数据包发送后)丢失的数据包数量是根据整个间隔内预计数据包数量和已接收数据包数量的差异计算得出的,其中预计数据包数量和已接收数据包数量是生成上次接收报告时保存的值:
expected_interval = expected - s->expected_prior;
s->expected_prior = expected;
received_interval = s->received - s->received_prior;
s->received_prior = s->received;
lost_interval = expected_interval - received_interval;
if (expected_interval == 0 || lost_interval <= 0) fraction = 0;
else fraction = (lost_interval << 8) / expected_interval;
得出的分数是一个8位定点数,二进制点位于左边缘。
六、Generating RTCP SDES Packets
该函数在缓冲区b中建立一个SDES块,缓冲区b由数组type、value和length中的argc项组成。它返回一个指向b中下一个可用位置的指针:
char *rtp_write_sdes(char *b, u_int32 src, int argc,
rtcp_sdes_type_t type[], char *value[],
int length[])
{
rtcp_sdes_t *s = (rtcp_sdes_t *)b;
rtcp_sdes_item_t *rsp;
int i;
int len;
int pad;
/* SSRC header */
s->src = src;
rsp = &s->item[0];
/* SDES items */
for (i = 0; i < argc; i++) {
rsp->type = type[i];
len = length[i];
if (len > RTP_MAX_SDES) {
/* invalid length, may want to take other action */
len = RTP_MAX_SDES;
}
rsp->length = len;
memcpy(rsp->data, value[i], len);
rsp = (rtcp_sdes_item_t *)&rsp->data[len];
}
/* terminate with end marker and pad to next 4-octet boundary */
len = ((char *) rsp) - b;
pad = 4 - (len & 0x3);
b = (char *) rsp;
while (pad--) *b++ = RTCP_SDES_END;
return b;
}
七、Parsing RTCP SDES Packets
该函数解析SDES数据包,调用function find_member() 和 member_sdes(),前者用于根据SSRC 标识找到指向会话成员信息的指针,后者用于存储该成员的新SDES信息。该函数需要一个指向 RTCP数据包头部的指针:
void rtp_read_sdes(rtcp_t *r)
{
int count = r->common.count;
rtcp_sdes_t *sd = &r->r.sdes;
rtcp_sdes_item_t *rsp, *rspn;
rtcp_sdes_item_t *end = (rtcp_sdes_item_t *)
((u_int32 *)r + r->common.length + 1);
source *s;
while (--count >= 0) {
rsp = &sd->item[0];
if (rsp >= end) break;
s = find_member(sd->src);
for (; rsp->type; rsp = rspn ) {
rspn = (rtcp_sdes_item_t *)((char*)rsp+rsp->length+2);
if (rspn >= end) {
rsp = rspn;
break;
}
member_sdes(s, rsp->type, rsp->data, rsp->length);
}
sd = (rtcp_sdes_t *)
((u_int32 *)sd + (((char *)rsp - (char *)sd) >> 2)+1);
}
if (count >= 0) {
/* invalid packet format */
}
}
八、Generating a Random 32-bit Identifier
下面的子程序使用《RFC 1321》中公布的MD5例程生成一个32位随机标识符。这些系统例程可能不存在于所有操作系统中,但它们可以作为提示,说明可以使用哪些类型的信息。其他适用的系统调用包括:
1.getdomainname(),
2.getwd(),或
3.getrusage().
“实时”视频或音频样本也是很好的随机数源,但必须注意避免使用关闭的麦克风或盲目的摄像头作为随机数源。
建议使用该例程或类似例程为随机数生成器生成RTCP周期的初始种子(如《RFC 3550》附录A.7 所示),生成序列号(sequence number)和时间戳(timestamp)的初始值,并生成SSRC值。由于该例程可能需要大量CPU资源,因此不适合直接用于生成RTCP周期(RTCP periods),因为可预测性不是问题。请注意,除非为类型参数提供不同的值,否则重复调用此例程将产生相同的结果,直到系统时钟的值发生变化:
/*
* Generate a random 32-bit quantity.
*/
#include <sys/types.h> /* u_long */
#include <sys/time.h> /* gettimeofday() */
#include <unistd.h> /* get..() */
#include <stdio.h> /* printf() */
#include <time.h> /* clock() */
#include <sys/utsname.h> /* uname() */
#include "global.h" /* from RFC 1321 */
#include "md5.h" /* from RFC 1321 */
#define MD_CTX MD5_CTX
#define MDInit MD5Init
#define MDUpdate MD5Update
#define MDFinal MD5Final
static u_long md_32(char *string, int length)
{
MD_CTX context;
union {
char c[16];
u_long x[4];
} digest;
u_long r;
int i;
MDInit (&context);
MDUpdate (&context, string, length);
MDFinal ((unsigned char *)&digest, &context);
r = 0;
for (i = 0; i < 3; i++) {
r ^= digest.x[i];
}
return r;
} /* md_32 */
/*
* Return random unsigned 32-bit quantity. Use ’type’ argument if
* you need to generate several different values in close succession.
*/
u_int32 random32(int type)
{
struct {
int type;
struct timeval tv;
clock_t cpu;
pid_t pid;
u_long hid;
uid_t uid;
gid_t gid;
struct utsname name;
} s;
gettimeofday(&s.tv, 0);
uname(&s.name);
s.type = type;
s.cpu = clock();
s.pid = getpid();
s.hid = gethostid();
s.uid = getuid();
s.gid = getgid();
/* also: system uptime */
return md_32((char *)&s, sizeof(s));
} /* random32 */
九、Computing the RTCP Transmission Interval
以下函数实现了《RFC 3550》第6.2 节中描述的 RTCP发送和接收规则。这些规则编码在多个函数中:
1.rtcp interval() :以秒为单位计算确定性计算的时间间隔。参数定义见《RFC 3550》第6.3节。
2.OnExpire():当RTCP传输计时器到期时,会调用OnExpire()。
3.OnReceive():每当接收到RTCP数据包时,就会调用OnReceive()。
OnExpire() 和OnReceive() 的参数都是事件e。这是该参与者的下一个预定事件,可以是RTCP报告,也可以是BYE数据包。假定以下函数可用:
1.Schedule(time t, event e) :安排事件e在时间t发生。
2.Reschedule(time t, event e) :将先前安排的事件e重新安排到时间t。
3.SendRTCPReport(event e):发送RTCP报告。
4.SendBYEPacket(event e) :发送BYE数据包。
5.TypeOfEvent(event e) :如果处理的事件是要发送BYE数据包,则返回EVENT BYE,否则返回 EVENT REPORT。
6.PacketType(p) :如果数据包p是RTCP报告(非 BYE),PacketType(p) 将返回PACKET RTCP REPORT;如果是BYE RTCP数据包,则返回PACKET BYE;如果是普通RTP数据包,则返回PACKET RTP。
7.ReceivedPacketSize() 和 SentPacketSize() :以八进制为单位返回引用数据包的大小。
8.NewMember(p) :如果发送信息包p的参与者当前不在成员列表中,NewMember(p) 返回1,否则返回0。需要注意的是,这个函数还不足以实现完整的功能,因为RTP数据包中的每个CSRC标识和BYE数据包中的每个SSRC都需要处理。
9.NewSender(p) :如果发送数据包p的参与者目前不在成员列表的发送者子列表中,NewSender(p) 返回1,否则返回0。
10.AddMember() 和 RemoveMember() :从成员名单中添加和删除参与者。
11.AddSender() 和 RemoveSender() :用于从成员列表的发件人子列表中添加和删除参与者。
必须对这些函数进行扩展,以实现允许将发送方和非发送方的RTCP带宽分数指定为明确参数,而不是25%和75%的固定值。如果其中一个参数为零,rtcp interval() 的扩展实现将需要避免除以零。
double rtcp_interval(int members,
int senders,
double rtcp_bw,
int we_sent,
double avg_rtcp_size,
int initial)
{
/*
* Minimum average time between RTCP packets from this site (in
* seconds). This time prevents the reports from ‘clumping’ when
* sessions are small and the law of large numbers isn’t helping
* to smooth out the traffic. It also keeps the report interval
* from becoming ridiculously small during transient outages like
* a network partition.
*/
double const RTCP_MIN_TIME = 5.;
/*
* Fraction of the RTCP bandwidth to be shared among active
* senders. (This fraction was chosen so that in a typical
* session with one or two active senders, the computed report
* time would be roughly equal to the minimum report time so that
* we don’t unnecessarily slow down receiver reports.) The
* receiver fraction must be 1 - the sender fraction.
*/
double const RTCP_SENDER_BW_FRACTION = 0.25;
double const RTCP_RCVR_BW_FRACTION = (1-RTCP_SENDER_BW_FRACTION);
/*
/* To compensate for "timer reconsideration" converging to a
* value below the intended average.
*/
double const COMPENSATION = 2.71828 - 1.5;
double t; /* interval */
double rtcp_min_time = RTCP_MIN_TIME;
int n; /* no. of members for computation */
/*
* Very first call at application start-up uses half the min
* delay for quicker notification while still allowing some time
* before reporting for randomization and to learn about other
* sources so the report interval will converge to the correct
* interval more quickly.
*/
if (initial) {
rtcp_min_time /= 2;
}
/*
* Dedicate a fraction of the RTCP bandwidth to senders unless
* the number of senders is large enough that their share is
* more than that fraction.
*/
n = members;
if (senders <= members * RTCP_SENDER_BW_FRACTION) {
if (we_sent) {
rtcp_bw *= RTCP_SENDER_BW_FRACTION;
n = senders;
} else {
rtcp_bw *= RTCP_RCVR_BW_FRACTION;
n -= senders;
}
}
/*
* The effective number of sites times the average packet size is
* the total number of octets sent when each site sends a report.
* Dividing this by the effective bandwidth gives the time
* interval over which those packets must be sent in order to
* meet the bandwidth target, with a minimum enforced. In that
* time interval we send one report so this time is also our
* average time between reports.
*/
t = avg_rtcp_size * n / rtcp_bw;
if (t < rtcp_min_time) t = rtcp_min_time;
/*
* To avoid traffic bursts from unintended synchronization with
* other sites, we then pick our actual next report interval as a
* random number uniformly distributed between 0.5*t and 1.5*t.
*/
t = t * (drand48() + 0.5);
t = t / COMPENSATION;
return t;
}
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 "timer 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;
}
}
void OnReceive(packet p,
event e,
int *members,
int *pmembers,
int *senders,
double *avg_rtcp_size,
double *tp,
double tc,
double tn)
{
/* What we do depends on whether we have left the group, and are
* waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or an RTCP
* report. p represents the packet that was just received. */
if (PacketType(p) == PACKET_RTCP_REPORT) {
if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
AddMember(p);
*members += 1;
}
*avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
(15./16.)*(*avg_rtcp_size);
} else if (PacketType(p) == PACKET_RTP) {
if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
AddMember(p);
*members += 1;
}
if (NewSender(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
AddSender(p);
*senders += 1;
}
} else if (PacketType(p) == PACKET_BYE) {
*avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
(15./16.)*(*avg_rtcp_size);
if (TypeOfEvent(e) == EVENT_REPORT) {
if (NewSender(p) == FALSE) {
RemoveSender(p);
*senders -= 1;
}
if (NewMember(p) == FALSE) {
RemoveMember(p);
*members -= 1;
}
if (*members < *pmembers) {
tn = tc +
(((double) *members)/(*pmembers))*(tn - tc);
*tp = tc -
(((double) *members)/(*pmembers))*(tc - *tp);
/* Reschedule the next report for time tn */
Reschedule(tn, e);
*pmembers = *members;
}
} else if (TypeOfEvent(e) == EVENT_BYE) {
*members += 1;
}
}
}
十、Estimating the Interarrival Jitter
下面的代码片段实现了《RFC 3550》第6.4.1节中给出的算法,用于计算 RTP数据到达时间统计方差的估计值,并将其插入接收报告的到达时间抖动字段中。输入为r->ts,即传入数据包的时间戳,以及arrival,即当前时间(单位相同)。接收报告的抖动字段以时间戳为单位,用无符号整数表示,但抖动估计值用浮点数表示。每个数据包到达时,抖动估计值都会更新:
int transit = arrival - r->ts;
int d = transit - s->transit;
s->transit = transit;
if (d < 0) d = -d;
s->jitter += (1./16.) * ((double)d - s->jitter);
为该成员生成接收报告块(rr指向该报告块)时,将返回当前的抖动估计值:
rr->jitter = (u_int32) s->jitter;
或者,抖动估计值可以保留为整数,但可以按比例缩放以减少舍入误差。除最后一行外,计算方法相同:
s->jitter += d - ((s->jitter + 8) >> 4);
在这种情况下,接收报告的估计值取样为:
rr->jitter = s->jitter >> 4;