📄 rtp.c
字号:
* @session: The RTP session.
* @optname: The option name, see #rtp_option.
* @optval: The return value.
*
* Retrieves the value of a session option. See #rtp_option for
* documentation on the options and their legal values.
*
* Returns: TRUE and the value of the option in optval on success, else FALSE.
*/
int rtp_get_option(struct rtp *session, rtp_option optname, int *optval)
{
switch (optname) {
case RTP_OPT_WEAK_VALIDATION:
*optval = session->opt->wait_for_rtcp;
break;
case RTP_OPT_PROMISC:
*optval = session->opt->promiscuous_mode;
break;
case RTP_OPT_FILTER_MY_PACKETS:
*optval = session->opt->filter_my_packets;
break;
case RTP_OPT_REUSE_PACKET_BUFS:
*optval = session->opt->reuse_bufs;
break;
default:
*optval = 0;
debug_msg("Ignoring unknown option (%d) in call to rtp_get_option().\n", optname);
return FALSE;
}
return TRUE;
}
/**
* rtp_get_userdata:
* @session: The RTP session.
*
* This function returns the userdata pointer that was passed to the
* rtp_init() or rtp_init_if() function when creating this session.
*
* Returns: pointer to userdata.
*/
uint8_t *rtp_get_userdata(struct rtp *session)
{
check_database(session);
return session->userdata;
}
/**
* rtp_my_ssrc:
* @session: The RTP Session.
*
* Returns: The SSRC we are currently using in this session. Note that our
* SSRC can change at any time (due to collisions) so applications must not
* store the value returned, but rather should call this function each time
* they need it.
*/
uint32_t rtp_my_ssrc(struct rtp *session)
{
check_database(session);
return session->my_ssrc;
}
static void process_rtp(struct rtp *session, uint32_t curr_rtp_ts, rtp_packet *packet, source *s)
{
int i, d, transit;
rtp_event event;
struct timeval event_ts;
if (packet->cc > 0) {
for (i = 0; i < packet->cc; i++) {
create_source(session, packet->csrc[i], FALSE);
}
}
/* Update the source database... */
if (s->sender == FALSE) {
s->sender = TRUE;
session->sender_count++;
}
transit = curr_rtp_ts - packet->ts;
d = transit - s->transit;
s->transit = transit;
if (d < 0) {
d = -d;
}
s->jitter += d - ((s->jitter + 8) / 16);
/* Callback to the application to process the packet... */
if (!filter_event(session, packet->ssrc)) {
gettimeofday(&event_ts, NULL);
event.ssrc = packet->ssrc;
event.type = RX_RTP;
event.data = (void *) packet; /* The callback function MUST free this! */
event.ts = &event_ts;
session->callback(session, &event);
}
}
static int validate_rtp2(rtp_packet *packet, int len)
{
/* Check for valid payload types..... 72-76 are RTCP payload type numbers, with */
/* the high bit missing so we report that someone is running on the wrong port. */
if (packet->pt >= 72 && packet->pt <= 76) {
debug_msg("rtp_header_validation: payload-type invalid");
if (packet->m) {
debug_msg(" (RTCP packet on RTP port?)");
}
debug_msg("\n");
return FALSE;
}
/* Check that the length of the packet is sensible... */
if (len < (12 + (4 * packet->cc))) {
debug_msg("rtp_header_validation: packet length is smaller than the header\n");
return FALSE;
}
/* Check that the amount of padding specified is sensible. */
/* Note: have to include the size of any extension header! */
if (packet->p) {
int payload_len = len - 12 - (packet->cc * 4);
if (packet->x) {
/* extension header and data */
payload_len -= 4 * (1 + packet->extn_len);
}
if (packet->data[packet->data_len - 1] > payload_len) {
debug_msg("rtp_header_validation: padding greater than payload length\n");
return FALSE;
}
if (packet->data[packet->data_len - 1] < 1) {
debug_msg("rtp_header_validation: padding zero\n");
return FALSE;
}
}
return TRUE;
}
static inline int
validate_rtp(struct rtp *session, rtp_packet *packet, int len)
{
/* This function checks the header info to make sure that the packet */
/* is valid. We return TRUE if the packet is valid, FALSE otherwise. */
/* See Appendix A.1 of the RTP specification. */
/* We only accept RTPv2 packets... */
if (packet->v != 2) {
debug_msg("rtp_header_validation: v != 2\n");
return FALSE;
}
if (!session->opt->wait_for_rtcp) {
/* We prefer speed over accuracy... */
return TRUE;
}
return validate_rtp2(packet, len);
}
static void
rtp_recv_data(struct rtp *session, uint32_t curr_rtp_ts)
{
/* This routine preprocesses an incoming RTP packet, deciding whether to process it. */
static rtp_packet *packet = NULL;
static uint8_t *buffer = NULL;
static uint8_t *buffer12 = NULL;
int buflen;
source *s;
if (!session->opt->reuse_bufs || (packet == NULL)) {
packet = (rtp_packet *) xmalloc(RTP_MAX_PACKET_LEN);
buffer = ((uint8_t *) packet) + RTP_PACKET_HEADER_SIZE;
buffer12 = buffer + 12;
}
buflen = udp_recv(session->rtp_socket, buffer, RTP_MAX_PACKET_LEN - RTP_PACKET_HEADER_SIZE);
if (buflen > 0) {
if (session->encryption_enabled) {
uint8_t initVec[8] = {0,0,0,0,0,0,0,0};
(session->decrypt_func)(session, buffer, buflen, initVec);
}
/* Convert header fields to host byte order... */
packet->seq = ntohs(packet->seq);
packet->ts = ntohl(packet->ts);
packet->ssrc = ntohl(packet->ssrc);
/* Setup internal pointers, etc... */
if (packet->cc) {
int i;
packet->csrc = (uint32_t *)(buffer12);
for (i = 0; i < packet->cc; i++) {
packet->csrc[i] = ntohl(packet->csrc[i]);
}
} else {
packet->csrc = NULL;
}
if (packet->x) {
packet->extn = buffer12 + (packet->cc * 4);
packet->extn_len = (packet->extn[2] << 8) | packet->extn[3];
packet->extn_type = (packet->extn[0] << 8) | packet->extn[1];
} else {
packet->extn = NULL;
packet->extn_len = 0;
packet->extn_type = 0;
}
packet->data = buffer12 + (packet->cc * 4);
packet->data_len = buflen - (packet->cc * 4) - 12;
if (packet->extn != NULL) {
packet->data += ((packet->extn_len + 1) * 4);
packet->data_len -= ((packet->extn_len + 1) * 4);
}
if (validate_rtp(session, packet, buflen)) {
if (session->opt->wait_for_rtcp) {
s = create_source(session, packet->ssrc, TRUE);
} else {
s = get_source(session, packet->ssrc);
}
if (session->opt->promiscuous_mode) {
if (s == NULL) {
create_source(session, packet->ssrc, FALSE);
s = get_source(session, packet->ssrc);
}
process_rtp(session, curr_rtp_ts, packet, s);
return; /* We don't free "packet", that's done by the callback function... */
}
if (s != NULL) {
if (s->probation == -1) {
s->probation = MIN_SEQUENTIAL;
s->max_seq = packet->seq - 1;
}
if (update_seq(s, packet->seq)) {
process_rtp(session, curr_rtp_ts, packet, s);
return; /* we don't free "packet", that's done by the callback function... */
} else {
/* This source is still on probation... */
debug_msg("RTP packet from probationary source ignored...\n");
}
} else {
debug_msg("RTP packet from unknown source ignored\n");
}
} else {
session->invalid_rtp_count++;
debug_msg("Invalid RTP packet discarded\n");
}
}
if (!session->opt->reuse_bufs) {
xfree(packet);
}
}
static int validate_rtcp(uint8_t *packet, int len)
{
/* Validity check for a compound RTCP packet. This function returns */
/* TRUE if the packet is okay, FALSE if the validity check fails. */
/* */
/* The following checks can be applied to RTCP packets [RFC1889]: */
/* o RTP version field must equal 2. */
/* o The payload type field of the first RTCP packet in a compound */
/* packet must be equal to SR or RR. */
/* o The padding bit (P) should be zero for the first packet of a */
/* compound RTCP packet because only the last should possibly */
/* need padding. */
/* o The length fields of the individual RTCP packets must total to */
/* the overall length of the compound RTCP packet as received. */
rtcp_t *pkt = (rtcp_t *) packet;
rtcp_t *end = (rtcp_t *) (((char *) pkt) + len);
rtcp_t *r = pkt;
int l = 0;
int pc = 1;
int p = 0;
/* All RTCP packets must be compound packets (RFC1889, section 6.1) */
if (((ntohs(pkt->common.length) + 1) * 4) == len) {
debug_msg("Bogus RTCP packet: not a compound packet\n");
return FALSE;
}
/* Check the RTCP version, payload type and padding of the first in */
/* the compund RTCP packet... */
if (pkt->common.version != 2) {
debug_msg("Bogus RTCP packet: version number != 2 in the first sub-packet\n");
return FALSE;
}
if (pkt->common.p != 0) {
debug_msg("Bogus RTCP packet: padding bit is set on first packet in compound\n");
return FALSE;
}
if ((pkt->common.pt != RTCP_SR) && (pkt->common.pt != RTCP_RR)) {
debug_msg("Bogus RTCP packet: compund packet does not start with SR or RR\n");
return FALSE;
}
/* Check all following parts of the compund RTCP packet. The RTP version */
/* number must be 2, and the padding bit must be zero on all apart from */
/* the last packet. */
do {
if (p == 1) {
debug_msg("Bogus RTCP packet: padding bit set before last in compound (sub-packet %d)\n", pc);
return FALSE;
}
if (r->common.p) {
p = 1;
}
if (r->common.version != 2) {
debug_msg("Bogus RTCP packet: version number != 2 in sub-packet %d\n", pc);
return FALSE;
}
l += (ntohs(r->common.length) + 1) * 4;
r = (rtcp_t *) (((uint32_t *) r) + ntohs(r->common.length) + 1);
pc++; /* count of sub-packets, for debugging... */
} while (r < end);
/* Check that the length of the packets matches the length of the UDP */
/* packet in which they were received... */
if (l != len) {
debug_msg("Bogus RTCP packet: RTCP packet length does not match UDP packet length (%d != %d)\n", l, len);
return FALSE;
}
if (r != end) {
debug_msg("Bogus RTCP packet: RTCP packet length does not match UDP packet length (%p != %p)\n", r, end);
return FALSE;
}
return TRUE;
}
static void process_report_blocks(struct rtp *session, rtcp_t *packet, uint32_t ssrc, rtcp_rr *rrp, struct timeval *event_ts)
{
int i;
rtp_event event;
rtcp_rr *rr;
/* ...process RRs... */
if (packet->common.count == 0) {
if (!filter_event(session, ssrc)) {
event.ssrc = ssrc;
event.type = RX_RR_EMPTY;
event.data = NULL;
event.ts = event_ts;
session->callback(session, &event);
}
} else {
for (i = 0; i < packet->common.count; i++, rrp++) {
rr = (rtcp_rr *) xmalloc(sizeof(rtcp_rr));
rr->ssrc = ntohl(rrp->ssrc);
rr->fract_lost = rrp->fract_lost; /* Endian conversion handled in the */
rr->total_lost = rrp->total_lost; /* definition of the rtcp_rr type. */
rr->last_seq = ntohl(rrp->last_seq);
rr->jitter = ntohl(rrp->jitter);
rr->lsr = ntohl(rrp->lsr);
rr->dlsr = ntohl(rrp->dlsr);
/* Create a database entry for this SSRC, if one doesn't already exist... */
create_source(session, rr->ssrc, FALSE);
/* Store the RR for later use... */
insert_rr(session, ssrc, rr, event_ts);
/* Call the event handler... */
if (!filter_event(session, ssrc)) {
event.ssrc = ssrc;
event.type = RX_RR;
event.data = (void *) rr;
event.ts = event_ts;
session->callback(session, &event);
}
}
}
}
static void process_rtcp_sr(struct rtp *session, rtcp_t *packet, struct timeval *event_ts)
{
uint32_t ssrc;
rtp_event event;
rtcp_sr *sr;
source *s;
ssrc = ntohl(packet->r.sr.sr.ssrc);
s = create_source(session, ssrc, FALSE);
if (s == NULL) {
debug_msg("Source 0x%08x invalid, skipping...\n", ssrc);
return;
}
/* Mark as an active sender, if we get a sender report... */
if (s->sender == FALSE) {
s->sender = TRUE;
session->sender_count++;
}
/* Process the SR... */
sr = (rtcp_sr *) xmalloc(sizeof(rtcp_sr));
sr->ssrc = ssrc;
sr->ntp_sec = ntohl(packet->r.sr.sr.ntp_sec);
sr->ntp_frac = ntohl(packet->r.sr.sr.ntp_frac);
sr->rtp_ts = ntohl(packet->r.sr.sr.rtp_ts);
sr->sender_pcount = ntohl(packet->r.sr.sr.sender_pcount);
sr->sender_bcount = ntohl(packet->r.sr.sr.sender_bcount);
/* Store the SR for later retrieval... */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -