rtp.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 314 行

C
314
字号
/* $Id: rtp.c 974 2007-02-19 01:13:53Z bennylp $ *//*  * Copyright (C) 2003-2007 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. */    ses->out_extseq = pj_rand();    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_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) * 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;    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_status seq_st;    /* 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 (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;    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?