From 00b6026f64cec5406299aaad0f1711be59859e51 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 9 Jul 2012 19:39:55 +0200 Subject: add osmux support --- src/Makefile.am | 1 + src/osmux.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 src/osmux.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 0a3d3db..e68077c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,7 @@ libosmonetif_la_LIBADD = channel/libosmonetif-channel.la libosmonetif_la_SOURCES = channel.c \ datagram.c \ ipa.c \ + osmux.c \ rs232.c \ rtp.c \ stream.c diff --git a/src/osmux.c b/src/osmux.c new file mode 100644 index 0000000..66fcfca --- /dev/null +++ b/src/osmux.c @@ -0,0 +1,218 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * (C) 2012 by On Waves ehf + * + * 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. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#define OSMUX_BATCH_MAX 1480 /* XXX: MTU - iphdr (20 bytes) */ + +struct osmux_hdr *osmux_xfrm_output_pull(struct msgb *msg) +{ + struct osmux_hdr *osmuxh = NULL; + + if (msg->len > sizeof(struct osmux_hdr)) { + osmuxh = (struct osmux_hdr *)msg->data; + + /* XXX: 15 bytes because AMR-CMR is 2, fix this. */ + msgb_pull(msg, sizeof(struct osmux_hdr) + 15); + + } else if (msg->len > 0) { + printf("remaining %d bytes, broken osmuxhdr?\n", msg->len); + } + + return osmuxh; +} + +struct msgb * +osmux_xfrm_output(struct osmux_hdr *osmuxh, struct osmux_out_handle *h) +{ + struct msgb *out_msg; + struct rtp_hdr *rtph; + struct amr_hdr *amrh; + + out_msg = msgb_alloc(sizeof(struct rtp_hdr) + + sizeof(struct amr_hdr) + + 15, "OSMUX test"); + if (out_msg == NULL) + return NULL; + + /* Reconstruct RTP header */ + rtph = (struct rtp_hdr *)out_msg->data; + rtph->csrc_count = (sizeof(struct amr_hdr) + 15) >> 2; + rtph->extension = 0; + rtph->version = RTP_VERSION; + rtph->payload_type = 98; + rtph->marker = osmuxh->rtp_marker; + /* ... emulate timestamp and ssrc */ + rtph->timestamp = htonl(h->rtp_timestamp); + rtph->sequence = htons(h->rtp_seq); + rtph->ssrc = osmuxh->circuit_id; + + msgb_put(out_msg, sizeof(struct rtp_hdr)); + + /* Reconstruct AMR header */ + amrh = (struct amr_hdr *)out_msg->tail; + amrh->cmr = osmuxh->amr_cmr; + amrh->f = osmuxh->amr_f; + amrh->ft = osmuxh->amr_ft; + amrh->q = osmuxh->amr_q; + + msgb_put(out_msg, sizeof(struct amr_hdr)); + + /* add AMR speech data */ + memcpy(out_msg->tail, osmux_get_payload(osmuxh), 15); + msgb_put(out_msg, 15); + + /* bump last RTP sequence number and timestamp that has been used */ + h->rtp_seq++; + h->rtp_timestamp++; + + return out_msg; +} + +static struct osmux_batch { + struct osmo_timer_list timer; + struct msgb *msg; + uint8_t seq; +} batch; + +static int osmux_batch_has_room(int msg_len) +{ + return batch.msg == NULL ? 1 : msg_len < msgb_tailroom(batch.msg); +} + +void osmux_xfrm_input_deliver(struct osmux_in_handle *h) +{ + printf("invoking delivery function\n"); + h->deliver(batch.msg); + msgb_free(batch.msg); + batch.msg = NULL; + osmo_timer_del(&batch.timer); +} + +static void osmux_batch_timer_expired(void *data) +{ + struct osmux_in_handle *h = data; + + printf("batch timeout!\n"); + osmux_xfrm_input_deliver(h); +} + +static struct msgb *osmux_batch_get(void) +{ + if (batch.msg == NULL) { + batch.msg = msgb_alloc(OSMUX_BATCH_MAX, "OSMUX"); + if (batch.msg == NULL) { + fprintf(stderr, "Not enough memory\n"); + return NULL; + } + + osmo_timer_schedule(&batch.timer, 0, 160000); /* XXX */ + } + + return batch.msg; +} + +static int +osmux_batch_add(struct msgb *msg, struct rtp_hdr *rtph, struct amr_hdr *amrh, + uint32_t amr_payload_len, uint8_t circuit_id, uint8_t seq) +{ + struct osmux_hdr *osmuxh; + + osmuxh = (struct osmux_hdr *)batch.msg->tail; + osmuxh->ft = OSMUX_FT_VOICE_AMR; + osmuxh->circuit_id = circuit_id; + osmuxh->seq = seq; + osmuxh->amr_cmr = amrh->cmr; + osmuxh->amr_f = amrh->f; + osmuxh->amr_ft = amrh->ft; + osmuxh->amr_q = amrh->q; + osmuxh->rtp_marker = rtph->marker; + msgb_put(batch.msg, sizeof(struct osmux_hdr)); + + memcpy(batch.msg->tail, osmo_amr_get_payload(amrh), amr_payload_len); + msgb_put(batch.msg, amr_payload_len); + + return 0; +} + +static int osmux_xfrm_encore_amr(struct rtp_hdr *rtph, struct msgb *msg) +{ + struct amr_hdr *amrh; + struct msgb *out_msg; + uint32_t amr_len; + uint32_t amr_payload_len; + + amrh = osmo_rtp_get_payload(rtph, msg, &amr_len); + if (amrh == NULL) + return -1; + + amr_payload_len = amr_len - sizeof(struct amr_hdr); + + if (!osmux_batch_has_room(sizeof(struct osmux_hdr) + amr_payload_len)) + return 1; + + out_msg = osmux_batch_get(); + if (out_msg == NULL) + return -1; + + if (osmux_batch_add(out_msg, rtph, amrh, amr_payload_len, 0, + batch.seq++) < 0) + return -1; + + return 0; +} + +/** + * osmux_xfrm_input - add RTP message to OSmux batch + * \param msg: RTP message that you want to batch into one OSmux message + * + * This function returns -1 on error. If 0 is returned, this indicates + * that the message has been batched. If 1 is returned, you have to + * invoke osmux_xfrm_input_deliver and try again. + */ +int osmux_xfrm_input(struct msgb *msg) +{ + int ret; + struct rtp_hdr *rtph; + + rtph = osmo_rtp_get_hdr(msg); + if (rtph == NULL) + return -1; + + switch(rtph->payload_type) { + case RTP_PT_RTCP: + return 0; + case RTP_PT_AMR: + ret = osmux_xfrm_encore_amr(rtph, msg); + break; + default: + /* Only AMR supported so far, sorry. */ + ret = -1; + break; + } + return ret; +} + +void osmux_xfrm_input_init(struct osmux_in_handle *h) +{ + batch.timer.cb = osmux_batch_timer_expired; + batch.timer.data = h; +} -- cgit v1.2.3