summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/rtp.c
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
committerDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
commitf3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch)
treed00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjmedia/src/pjmedia/rtp.c
Import pjproject-2.0.1
Diffstat (limited to 'pjmedia/src/pjmedia/rtp.c')
-rw-r--r--pjmedia/src/pjmedia/rtp.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c
new file mode 100644
index 0000000..7fe6f89
--- /dev/null
+++ b/pjmedia/src/pjmedia/rtp.c
@@ -0,0 +1,366 @@
+/* $Id: rtp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/rtp.h>
+#include <pjmedia/errno.h>
+#include <pj/log.h>
+#include <pj/sock.h> /* pj_htonx, pj_htonx */
+#include <pj/assert.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "rtp.c"
+
+#define RTP_VERSION 2
+
+#define RTP_SEQ_MOD (1 << 16)
+#define MAX_DROPOUT ((pj_int16_t)3000)
+#define MAX_MISORDER ((pj_int16_t)100)
+#define MIN_SEQUENTIAL ((pj_int16_t)2)
+
+static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl,
+ pj_uint16_t seq);
+
+
+PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses,
+ int default_pt,
+ pj_uint32_t sender_ssrc )
+{
+ PJ_LOG(5, (THIS_FILE,
+ "pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
+ ses, default_pt, sender_ssrc));
+
+ /* Check RTP header packing. */
+ if (sizeof(struct pjmedia_rtp_hdr) != 12) {
+ pj_assert(!"Wrong RTP header packing!");
+ return PJMEDIA_RTP_EINPACK;
+ }
+
+ /* If sender_ssrc is not specified, create from random value. */
+ if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
+ sender_ssrc = pj_htonl(pj_rand());
+ } else {
+ sender_ssrc = pj_htonl(sender_ssrc);
+ }
+
+ /* Initialize session. */
+ pj_bzero(ses, sizeof(*ses));
+
+ /* Initial sequence number SHOULD be random, according to RFC 3550. */
+ /* According to RFC 3711, it should be random within 2^15 bit */
+ ses->out_extseq = pj_rand() & 0x7FFF;
+ ses->peer_ssrc = 0;
+
+ /* Build default header for outgoing RTP packet. */
+ ses->out_hdr.v = RTP_VERSION;
+ ses->out_hdr.p = 0;
+ ses->out_hdr.x = 0;
+ ses->out_hdr.cc = 0;
+ ses->out_hdr.m = 0;
+ ses->out_hdr.pt = (pj_uint8_t) default_pt;
+ ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
+ ses->out_hdr.ts = 0;
+ ses->out_hdr.ssrc = sender_ssrc;
+
+ /* Keep some arguments as session defaults. */
+ ses->out_pt = (pj_uint16_t) default_pt;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_rtp_session_init2(
+ pjmedia_rtp_session *ses,
+ pjmedia_rtp_session_setting settings)
+{
+ pj_status_t status;
+ int pt = 0;
+ pj_uint32_t sender_ssrc = 0;
+
+ if (settings.flags & 1)
+ pt = settings.default_pt;
+ if (settings.flags & 2)
+ sender_ssrc = settings.sender_ssrc;
+
+ status = pjmedia_rtp_session_init(ses, pt, sender_ssrc);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (settings.flags & 4) {
+ ses->out_extseq = settings.seq;
+ ses->out_hdr.seq = pj_htons((pj_uint16_t)ses->out_extseq);
+ }
+ if (settings.flags & 8)
+ ses->out_hdr.ts = pj_htonl(settings.ts);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses,
+ int pt, int m,
+ int payload_len, int ts_len,
+ const void **rtphdr, int *hdrlen )
+{
+ PJ_UNUSED_ARG(payload_len);
+
+ /* Update timestamp */
+ ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
+
+ /* If payload_len is zero, bail out.
+ * This is a clock frame; we're not really transmitting anything.
+ */
+ if (payload_len == 0)
+ return PJ_SUCCESS;
+
+ /* Update session. */
+ ses->out_extseq++;
+
+ /* Create outgoing header. */
+ ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
+ ses->out_hdr.m = (pj_uint16_t) m;
+ ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
+
+ /* Return values */
+ *rtphdr = &ses->out_hdr;
+ *hdrlen = sizeof(pjmedia_rtp_hdr);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses,
+ const void *pkt, int pkt_len,
+ const pjmedia_rtp_hdr **hdr,
+ const void **payload,
+ unsigned *payloadlen)
+{
+ int offset;
+
+ PJ_UNUSED_ARG(ses);
+
+ /* Assume RTP header at the start of packet. We'll verify this later. */
+ *hdr = (pjmedia_rtp_hdr*)pkt;
+
+ /* Check RTP header sanity. */
+ if ((*hdr)->v != RTP_VERSION) {
+ return PJMEDIA_RTP_EINVER;
+ }
+
+ /* Payload is located right after header plus CSRC */
+ offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
+
+ /* Adjust offset if RTP extension is used. */
+ if ((*hdr)->x) {
+ pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*)
+ (((pj_uint8_t*)pkt) + offset);
+ offset += ((pj_ntohs(ext->length)+1) * sizeof(pj_uint32_t));
+ }
+
+ /* Check that offset is less than packet size */
+ if (offset > pkt_len)
+ return PJMEDIA_RTP_EINLEN;
+
+ /* Find and set payload. */
+ *payload = ((pj_uint8_t*)pkt) + offset;
+ *payloadlen = pkt_len - offset;
+
+ /* Remove payload padding if any */
+ if ((*hdr)->p && *payloadlen > 0) {
+ pj_uint8_t pad_len;
+
+ pad_len = ((pj_uint8_t*)(*payload))[*payloadlen - 1];
+ if (pad_len <= *payloadlen)
+ *payloadlen -= pad_len;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
+ const pjmedia_rtp_hdr *hdr,
+ pjmedia_rtp_status *p_seq_st)
+{
+ pjmedia_rtp_session_update2(ses, hdr, p_seq_st, PJ_TRUE);
+}
+
+PJ_DEF(void) pjmedia_rtp_session_update2( pjmedia_rtp_session *ses,
+ const pjmedia_rtp_hdr *hdr,
+ pjmedia_rtp_status *p_seq_st,
+ pj_bool_t check_pt)
+{
+ pjmedia_rtp_status seq_st;
+
+ /* for now check_pt MUST be either PJ_TRUE or PJ_FALSE.
+ * In the future we might change check_pt from boolean to
+ * unsigned integer to accommodate more flags.
+ */
+ pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE);
+
+ /* Init status */
+ seq_st.status.value = 0;
+ seq_st.diff = 0;
+
+ /* Check SSRC. */
+ if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+
+ if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
+ seq_st.status.flag.badssrc = 1;
+ ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+ }
+
+ /* Check payload type. */
+ if (check_pt && hdr->pt != ses->out_pt) {
+ if (p_seq_st) {
+ p_seq_st->status.value = seq_st.status.value;
+ p_seq_st->status.flag.bad = 1;
+ p_seq_st->status.flag.badpt = 1;
+ }
+ return;
+ }
+
+ /* Initialize sequence number on first packet received. */
+ if (ses->received == 0)
+ pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
+
+ /* Check sequence number to see if remote session has been restarted. */
+ pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st);
+ if (seq_st.status.flag.restart) {
+ ++ses->received;
+
+ } else if (!seq_st.status.flag.bad) {
+ ++ses->received;
+ }
+
+ if (p_seq_st) {
+ p_seq_st->status.value = seq_st.status.value;
+ p_seq_st->diff = seq_st.diff;
+ }
+}
+
+
+
+void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
+{
+ sess->base_seq = seq;
+ sess->max_seq = seq;
+ sess->bad_seq = RTP_SEQ_MOD + 1;
+ sess->cycles = 0;
+}
+
+
+void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
+{
+ pjmedia_rtp_seq_restart(sess, seq);
+
+ sess->max_seq = (pj_uint16_t) (seq - 1);
+ sess->probation = MIN_SEQUENTIAL;
+}
+
+
+void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess,
+ pj_uint16_t seq,
+ pjmedia_rtp_status *seq_status)
+{
+ pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq);
+ pjmedia_rtp_status st;
+
+ /* Init status */
+ st.status.value = 0;
+ st.diff = 0;
+
+ /*
+ * Source is not valid until MIN_SEQUENTIAL packets with
+ * sequential sequence numbers have been received.
+ */
+ if (sess->probation) {
+
+ st.status.flag.probation = 1;
+
+ if (seq == sess->max_seq+ 1) {
+ /* packet is in sequence */
+ st.diff = 1;
+ sess->probation--;
+ sess->max_seq = seq;
+ if (sess->probation == 0) {
+ st.status.flag.probation = 0;
+ }
+ } else {
+
+ st.diff = 0;
+
+ st.status.flag.bad = 1;
+ if (seq == sess->max_seq)
+ st.status.flag.dup = 1;
+ else
+ st.status.flag.outorder = 1;
+
+ sess->probation = MIN_SEQUENTIAL - 1;
+ sess->max_seq = seq;
+ }
+
+
+ } else if (udelta == 0) {
+
+ st.status.flag.dup = 1;
+
+ } else if (udelta < MAX_DROPOUT) {
+ /* in order, with permissible gap */
+ if (seq < sess->max_seq) {
+ /* Sequence number wrapped - count another 64K cycle. */
+ sess->cycles += RTP_SEQ_MOD;
+ }
+ sess->max_seq = seq;
+
+ st.diff = udelta;
+
+ } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
+ /* the sequence number made a very large jump */
+ if (seq == sess->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).
+ */
+ pjmedia_rtp_seq_restart(sess, seq);
+ st.status.flag.restart = 1;
+ st.status.flag.probation = 1;
+ st.diff = 1;
+ }
+ else {
+ sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+ st.status.flag.bad = 1;
+ st.status.flag.outorder = 1;
+ }
+ } else {
+ /* old duplicate or reordered packet.
+ * Not necessarily bad packet (?)
+ */
+ st.status.flag.outorder = 1;
+ }
+
+
+ if (seq_status) {
+ seq_status->diff = st.diff;
+ seq_status->status.value = st.status.value;
+ }
+}
+
+