diff --git a/rtp.c b/rtp.c index 9eb70617e..bbc63ad8a 100644 --- a/rtp.c +++ b/rtp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Sippy Software, Inc., http://www.sippysoft.com + * Copyright (c) 2007-2008 Sippy Software, Inc., http://www.sippysoft.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: rtp.c,v 1.6 2007/11/19 22:44:31 sobomax Exp $ + * $Id: rtp.c,v 1.7 2008/04/17 22:38:00 sobomax Exp $ * */ @@ -31,6 +31,7 @@ #include #include #include +#include #include "rtp.h" #include "rtpp_util.h" @@ -38,53 +39,175 @@ /* Linked list of free packets */ static struct rtp_packet *rtp_packet_pool = NULL; -size_t -rtp_samples2bytes(int codec_id, int nsamples) +static int +g723_len(unsigned char ch) { - switch (codec_id) { - case RTP_PCMU: - case RTP_PCMA: - return nsamples; - case RTP_G729: - return nsamples / 8; - case RTP_GSM: - return (nsamples / 160) * 33; - case RTP_G723: - return (nsamples / 240) * 24; - default: - return RTP_NSAMPLES_UNKNOWN; + switch (ch & 3) { + case 2: + /* Silence Insertion Descriptor (SID) frame */ + return 4; + + case 0: + /* 6.3 kbit/s frame */ + return 24; + + case 1: + /* 5.3 kbit/s frame */ + return 20; + + default: + return RTP_NSAMPLES_UNKNOWN; + } +} + +static int +g723_samples(const unsigned char *buf, int maxlen) +{ + int pos, samples, n; + + for (pos = 0, samples = 0; pos < maxlen; pos += n) { + samples += 240; + n = g723_len(buf[pos]); + if (n == RTP_NSAMPLES_UNKNOWN) + return RTP_NSAMPLES_UNKNOWN; } + return samples; } -int -rtp_bytes2samples(int codec_id, size_t nbytes) +static int +rtp_calc_samples(int codec_id, size_t nbytes, const unsigned char *data) { switch (codec_id) { - case RTP_PCMU: - case RTP_PCMA: - return nbytes; - case RTP_G729: - return nbytes * 8; - case RTP_GSM: - return 160 * (nbytes / 33); - case RTP_G723: - if (nbytes % 24 == 0) - return 240 * (nbytes / 24); -#if defined(NOTYET) - else if (nbytes % 20 == 0) - return 240 * (nbytes / 20); -#endif - default: - return RTP_NSAMPLES_UNKNOWN; + case RTP_PCMU: + case RTP_PCMA: + return nbytes; + + case RTP_G729: + return (nbytes / 10) * 80 + (nbytes % 10 == 0 ? 0 : 80); + + case RTP_GSM: + return 160 * (nbytes / 33); + + case RTP_G723: + return g723_samples(data, nbytes); + + default: + return RTP_NSAMPLES_UNKNOWN; + } +} + +static void +rtp_packet_chunk_find_g711(struct rtp_packet *pkt, struct rtp_packet_chunk *ret, int min_nsamples) +{ + + ret->nsamples = min_nsamples; + ret->bytes = min_nsamples; +} + +static void +rtp_packet_chunk_find_g729(struct rtp_packet *pkt, struct rtp_packet_chunk *ret, int min_nsamples) +{ + int frames, samples; + + frames = min_nsamples / 80 + ((min_nsamples % 80) == 0 ? 0 : 1); + samples = frames * 80; + + if (samples >= pkt->nsamples) { + ret->whole_packet_matched = 1; + return; + } + ret->nsamples = samples; + ret->bytes = frames * 10; +} + +static void +rtp_packet_chunk_find_gsm(struct rtp_packet *pkt, struct rtp_packet_chunk *ret, int min_nsamples) +{ + int frames, samples; + + frames = min_nsamples / 160 + ((min_nsamples % 160) == 0 ? 0 : 1); + samples = frames * 160; + + if (samples >= pkt->nsamples) { + ret->whole_packet_matched = 1; + return; + } + ret->nsamples = samples; + ret->bytes = frames * 33; +} + +static void +rtp_packet_chunk_find_g723(struct rtp_packet *pkt, struct rtp_packet_chunk *ret, int min_nsamples) +{ + int frames, samples, pos, found_samples, n; + unsigned char *buf; + + frames = min_nsamples / 240 + ((min_nsamples % 240) == 0 ? 0 : 1); + samples = frames * 240; + + pos = 0; + found_samples = 0; + if (samples >= pkt->nsamples) { + ret->whole_packet_matched = 1; + return; + } + + buf = &pkt->buf[pkt->data_offset]; + while (pos < pkt->data_size && samples > found_samples) { + found_samples += 240; + n = g723_len(buf[pos]); + assert(n != RTP_NSAMPLES_UNKNOWN); + pos += n; + } + ret->nsamples = found_samples; + ret->bytes = (pos < pkt->data_size ? pos : pkt->data_size); +} + +/* + * Find the head of the packet with the length at least + * of min_nsamples. + * + * Warning! When whole packet has been matched the chunk can be uninitialized. + */ +void +rtp_packet_first_chunk_find(struct rtp_packet *pkt, struct rtp_packet_chunk *ret, int min_nsamples) +{ + + assert(pkt->nsamples > min_nsamples); + ret->whole_packet_matched = 0; + + switch (pkt->header.pt) { + case RTP_PCMU: + case RTP_PCMA: + rtp_packet_chunk_find_g711(pkt, ret, min_nsamples); + break; + + case RTP_G729: + rtp_packet_chunk_find_g729(pkt, ret, min_nsamples); + break; + + case RTP_GSM: + rtp_packet_chunk_find_gsm(pkt, ret, min_nsamples); + break; + + case RTP_G723: + rtp_packet_chunk_find_g723(pkt, ret, min_nsamples); + break; + + default: + ret->whole_packet_matched = 1; + break; } } void rtp_packet_parse(struct rtp_packet *pkt) { - int padding_size = 0; + int padding_size; + + padding_size = 0; pkt->data_size = 0; pkt->data_offset = 0; @@ -99,9 +222,17 @@ rtp_packet_parse(struct rtp_packet *pkt) padding_size = ((unsigned char *) pkt)[pkt->size - 1]; pkt->data_size = pkt->size - pkt->data_offset - padding_size; - pkt->nsamples = rtp_bytes2samples(pkt->header.pt, pkt->data_size); + pkt->nsamples = rtp_calc_samples(pkt->header.pt, pkt->data_size, &pkt->buf[pkt->data_offset]); pkt->ts = ntohl(pkt->header.ts); pkt->seq = ntohs(pkt->header.seq); + + pkt->appendable = 1; + /* + * G.729 comfort noise frame as the last frame causes + * packet to be non-appendable + */ + if (pkt->header.pt == RTP_G729 && (pkt->data_size % 10) != 0) + pkt->appendable = 0; } struct rtp_packet * @@ -120,6 +251,7 @@ rtp_packet_alloc() void rtp_packet_free(struct rtp_packet *pkt) { + pkt->next = rtp_packet_pool; pkt->prev = NULL; rtp_packet_pool = pkt; diff --git a/rtp.h b/rtp.h index 2e67ad4c8..811bc75f7 100644 --- a/rtp.h +++ b/rtp.h @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: rtp.h,v 1.7 2008/03/28 23:15:19 sobomax Exp $ + * $Id: rtp.h,v 1.8 2008/04/17 22:38:00 sobomax Exp $ * */ @@ -85,7 +85,7 @@ struct rtp_packet { int nsamples; uint32_t ts; uint16_t seq; - int resizeable; + int appendable; double rtime; struct rtp_packet *next; @@ -102,6 +102,12 @@ struct rtp_packet { }; }; +struct rtp_packet_chunk { + int bytes; + int nsamples; + int whole_packet_matched; +}; + #define RTP_HDR_LEN(rhp) (sizeof(*(rhp)) + ((rhp)->cc * sizeof((rhp)->csrc[0]))) void rtp_packet_parse(struct rtp_packet *); @@ -112,7 +118,7 @@ void rtp_packet_free(struct rtp_packet *); void rtp_packet_set_seq(struct rtp_packet *, uint16_t seq); void rtp_packet_set_ts(struct rtp_packet *, uint32_t ts); -size_t rtp_samples2bytes(int codec_id, int nsamples); +void rtp_packet_first_chunk_find(struct rtp_packet *, struct rtp_packet_chunk *, int min_nsamples); #define ts_less(ts1, ts2) (((ts1) - (ts2)) > (uint32_t) (1 << 31)) diff --git a/rtp_resizer.c b/rtp_resizer.c index 1e04729ee..f79d275c6 100644 --- a/rtp_resizer.c +++ b/rtp_resizer.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: rtp_resizer.c,v 1.1 2007/11/16 08:43:26 sobomax Exp $ + * $Id: rtp_resizer.c,v 1.2 2008/04/17 22:38:00 sobomax Exp $ * */ @@ -37,21 +37,6 @@ #include "rtp.h" #include "rtp_resizer.h" -static int -min_nsamples(int codec_id) -{ - - switch (codec_id) - { - case RTP_GSM: - return 160; /* 20ms */ - case RTP_G723: - return 240; /* 30ms */ - default: - return 80; - } -} - static int max_nsamples(int codec_id) { @@ -79,7 +64,7 @@ rtp_resizer_free(struct rtp_resizer *this) } } -void +void rtp_resizer_enqueue(struct rtp_resizer *this, struct rtp_packet **pkt) { struct rtp_packet *p; @@ -91,10 +76,6 @@ rtp_resizer_enqueue(struct rtp_resizer *this, struct rtp_packet **pkt) if ((*pkt)->nsamples == RTP_NSAMPLES_UNKNOWN) return; - (*pkt)->resizeable = 1; - if ((*pkt)->header.pt == RTP_G729 && (*pkt)->data_size < 10) /* G.729 comfort noise frame */ - (*pkt)->resizeable = 0; - if (this->last_sent_ts_inited && ts_less((*pkt)->ts, this->last_sent_ts)) { /* Packet arrived too late. Drop it. */ @@ -155,6 +136,65 @@ rtp_resizer_enqueue(struct rtp_resizer *this, struct rtp_packet **pkt) *pkt = NULL; /* take control over the packet */ } +static void +detach_queue_head(struct rtp_resizer *this) +{ + + this->queue.first = this->queue.first->next; + if (this->queue.first == NULL) + this->queue.last = NULL; + else + this->queue.first->prev = NULL; +} + +static void +append_packet(struct rtp_packet *dst, struct rtp_packet *src) +{ + + memcpy(&dst->buf[dst->data_offset + dst->data_size], + &src->buf[src->data_offset], src->data_size); + dst->nsamples += src->nsamples; + dst->data_size += src->data_size; + dst->size += src->data_size; + dst->appendable = src->appendable; +} + +static void +append_chunk(struct rtp_packet *dst, struct rtp_packet *src, const struct rtp_packet_chunk *chunk) +{ + + /* Copy chunk */ + memcpy(&dst->buf[dst->data_offset + dst->data_size], + &src->buf[src->data_offset], chunk->bytes); + dst->nsamples += chunk->nsamples; + dst->data_size += chunk->bytes; + dst->size += chunk->bytes; + + /* Truncate the source packet */ + src->nsamples -= chunk->nsamples; + rtp_packet_set_ts(src, src->ts + chunk->nsamples); + src->data_size -= chunk->bytes; + src->size -= chunk->bytes; + memmove(&src->buf[src->data_offset], &src->buf[src->data_offset + chunk->bytes], src->data_size); +} + +static void +move_chunk(struct rtp_packet *dst, struct rtp_packet *src, const struct rtp_packet_chunk *chunk) +{ + /* Copy chunk */ + memcpy(&dst->buf[dst->data_offset], &src->buf[src->data_offset], chunk->bytes); + dst->nsamples = chunk->nsamples; + dst->data_size = chunk->bytes; + dst->size = dst->data_size + dst->data_offset; + + /* Truncate the source packet */ + src->nsamples -= chunk->nsamples; + rtp_packet_set_ts(src, src->ts + chunk->nsamples); + src->data_size -= chunk->bytes; + src->size -= chunk->bytes; + memmove(&src->buf[src->data_offset], &src->buf[src->data_offset + chunk->bytes], src->data_size); +} + struct rtp_packet * rtp_resizer_get(struct rtp_resizer *this, double ctime) { @@ -164,9 +204,9 @@ rtp_resizer_get(struct rtp_resizer *this, double ctime) int count = 0; int split = 0; int nsamples_left; - int bytes_left; int output_nsamples; int max; + struct rtp_packet_chunk chunk; if (this->queue.first == NULL) return NULL; @@ -180,12 +220,8 @@ rtp_resizer_get(struct rtp_resizer *this, double ctime) return NULL; } + output_nsamples = this->output_nsamples; max = max_nsamples(this->queue.first->header.pt); - output_nsamples = this->output_nsamples - (this->output_nsamples % min_nsamples(this->queue.first->header.pt)); - - if (output_nsamples == 0) - output_nsamples = this->queue.first->nsamples; - if (max > 0 && output_nsamples > max) output_nsamples = max; @@ -196,40 +232,29 @@ rtp_resizer_get(struct rtp_resizer *this, double ctime) if (ret == NULL) { /* Look if the first packet is to be split */ - if (p->nsamples > output_nsamples && p->resizeable) - { - bytes_left = rtp_samples2bytes(p->header.pt, output_nsamples); - if (bytes_left > 0) - { - ret = rtp_packet_alloc(); - /* Copy only portion that is in use */ - memcpy(ret, p, offsetof(struct rtp_packet, buf) + p->size); - - ret->nsamples = output_nsamples; - ret->data_size = bytes_left; - ret->size = ret->data_offset + ret->data_size; - - /* truncate the input packet */ - p->nsamples -= output_nsamples; - rtp_packet_set_ts(p, p->ts + output_nsamples); - p->data_size -= bytes_left; - p->size -= bytes_left; - memmove(&p->buf[p->data_offset], &p->buf[p->data_offset + bytes_left], p->data_size); - - this->nsamples_total -= output_nsamples; - - ++split; - ++count; - break; - } - } + if (p->nsamples > output_nsamples) { + rtp_packet_first_chunk_find(p, &chunk, output_nsamples); + if (chunk.whole_packet_matched) { + ret = p; + detach_queue_head(this); + } else { + ret = rtp_packet_alloc(); + if (ret == NULL) + break; + memcpy(ret, p, offsetof(struct rtp_packet, buf)); + move_chunk(ret, p, &chunk); + ++split; + } + if (!this->seq_initialized) { + this->seq = ret->seq; + this->seq_initialized = 1; + } + ++count; + break; + } } else /* ret != NULL */ { - /* Next packet is not resizeable, send current packet immediately */ - if (!p->resizeable) - break; - /* detect holes and payload changes in RTP stream */ if ((ret->ts + ret->nsamples) != p->ts || ret->header.pt != p->header.pt) @@ -240,31 +265,26 @@ rtp_resizer_get(struct rtp_resizer *this, double ctime) /* Break the input packet into pieces to create output packet * of specified size */ - if (nsamples_left > 0 && nsamples_left < p->nsamples && p->resizeable) - { - /* Take required number of bytes only */ - bytes_left = rtp_samples2bytes(ret->header.pt, nsamples_left); - if (bytes_left > 0) - { - memcpy(&ret->buf[ret->data_offset + ret->data_size], - &p->buf[p->data_offset], bytes_left); - ret->nsamples += nsamples_left; - ret->data_size += bytes_left; - ret->size += bytes_left; - - /* truncate the input packet */ - p->nsamples -= nsamples_left; - rtp_packet_set_ts(p, p->ts + nsamples_left); - p->data_size -= bytes_left; - p->size -= bytes_left; - memmove(&p->buf[p->data_offset], &p->buf[p->data_offset + bytes_left], p->data_size); - - this->nsamples_total -= nsamples_left; - - ++split; - ++count; - break; - } + if (nsamples_left > 0 && nsamples_left < p->nsamples) { + rtp_packet_first_chunk_find(p, &chunk, nsamples_left); + if (chunk.whole_packet_matched) { + /* Prevent RTP packet buffer overflow */ + if ((ret->size + p->data_size) > sizeof(ret->buf)) + break; + append_packet(ret, p); + detach_queue_head(this); + rtp_packet_free(p); + } + else { + /* Prevent RTP packet buffer overflow */ + if ((ret->size + chunk.bytes) > sizeof(ret->buf)) + break; + /* Append chunk to output */ + append_chunk(ret, p, &chunk); + ++split; + } + ++count; + break; } } ++count; @@ -276,42 +296,35 @@ rtp_resizer_get(struct rtp_resizer *this, double ctime) break; /* Detach head packet from the queue */ - this->queue.first = p->next; - if (p->next == NULL) - this->queue.last = NULL; - else - p->next->prev = NULL; + detach_queue_head(this); /* * Add the packet to the output */ if (ret == NULL) { ret = p; /* use the first packet as the result container */ - this->nsamples_total -= p->nsamples; if (!this->seq_initialized) { this->seq = p->seq; this->seq_initialized = 1; } - /* Send non-resizeable packet immediately */ - if (!ret->resizeable) - break; } else { - memcpy(&ret->buf[ret->data_offset + ret->data_size], - &p->buf[p->data_offset], p->data_size); - ret->nsamples += p->nsamples; - ret->data_size += p->data_size; - ret->size += p->data_size; - this->nsamples_total -= p->nsamples; + append_packet(ret, p); rtp_packet_free(p); } + /* Send non-appendable packet immediately */ + if (!ret->appendable) + break; } - rtp_packet_set_seq(ret, this->seq); - ++this->seq; - this->last_sent_ts_inited = 1; - this->last_sent_ts = ret->ts + ret->nsamples; + if (ret != NULL) { + this->nsamples_total -= ret->nsamples; + rtp_packet_set_seq(ret, this->seq); + ++this->seq; + this->last_sent_ts_inited = 1; + this->last_sent_ts = ret->ts + ret->nsamples; /* - printf("Payload %d, %d packets aggregated, %d splits done, final size %dms\n", ret->header.pt, count, split, ret->nsamples / 8); + printf("Payload %d, %d packets aggregated, %d splits done, final size %dms\n", ret->header.pt, count, split, ret->nsamples / 8); */ + } return ret; }