@@ -38,10 +38,10 @@ int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
int main(int argc, char **argv)
{
- char buf[4096] = {0};
+ char buf[4096] = {0x80, 0};
int cc, rc;
- struct mgcp_rtp_end dst_end = {0};
- struct mgcp_rtp_end src_end = {0};
+ struct mgcp_rtp_end *dst_end;
+ struct mgcp_rtp_end *src_end;
struct mgcp_trunk_config tcfg = {{0}};
struct mgcp_endpoint endp = {0};
struct mgcp_process_rtp_state *state;
@@ -52,39 +52,63 @@ int main(int argc, char **argv)
tcfg.endpoints = &endp;
tcfg.number_endpoints = 1;
endp.tcfg = &tcfg;
+ mgcp_free_endp(&endp);
+
+ dst_end = &endp.bts_end;
+ src_end = &endp.net_end;
if (argc <= 2)
errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16}");
- if ((src_end.payload_type = audio_name_to_type(argv[1])) == -1)
+ if ((src_end->payload_type = audio_name_to_type(argv[1])) == -1)
errx(1, "invalid input format '%s'", argv[1]);
- if ((dst_end.payload_type = audio_name_to_type(argv[2])) == -1)
+ if ((dst_end->payload_type = audio_name_to_type(argv[2])) == -1)
errx(1, "invalid output format '%s'", argv[2]);
- rc = mgcp_transcoding_setup(&endp, &dst_end, &src_end);
+ rc = mgcp_transcoding_setup(&endp, dst_end, src_end);
if (rc < 0)
errx(1, "setup failed: %s", strerror(-rc));
- state = dst_end.rtp_process_data;
+ state = dst_end->rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, 160, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
+ buf[1] = src_end->payload_type;
+ *(uint16_t*)(buf+2) = htons(1);
+ *(uint32_t*)(buf+4) = htonl(0);
+ *(uint32_t*)(buf+8) = htonl(0xaabbccdd);
+
while ((cc = read(0, buf + 12, in_size))) {
+ int cont;
+ int len;
+
if (cc != in_size)
err(1, "read");
cc += 12; /* include RTP header */
- rc = mgcp_transcoding_process_rtp(&endp, &dst_end,
- buf, &cc, sizeof(buf));
- if (rc < 0)
- errx(1, "processing failed: %s", strerror(-rc));
+ len = cc;
+
+ do {
+ cont = mgcp_transcoding_process_rtp(&endp, dst_end,
+ buf, &len, sizeof(buf));
+ if (cont == -EAGAIN) {
+ fprintf(stderr, "Got EAGAIN\n");
+ break;
+ }
+
+ if (cont < 0)
+ errx(1, "processing failed: %s", strerror(-cont));
+
+ len -= 12; /* ignore RTP header */
+
+ if (write(1, buf + 12, len) != len)
+ err(1, "write");
- cc -= 12; /* ignore RTP header */
- if (write(1, buf + 12, cc) != cc)
- err(1, "write");
+ len = cont;
+ } while (len > 0);
}
return 0;
}
@@ -87,7 +87,8 @@ typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int stat
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
-typedef int (*mgcp_processing)(struct mgcp_rtp_end *dst_end,
+typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
+ struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
@@ -181,6 +182,8 @@ struct mgcp_config {
struct mgcp_port_range transcoder_ports;
int endp_dscp;
+ int bts_force_ptime;
+
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
@@ -90,6 +90,7 @@ struct mgcp_rtp_end {
char *audio_name;
char *subtype_name;
int output_enabled;
+ int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
@@ -202,7 +203,7 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *);
/* payload processing default functions */
-int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end,
+int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
@@ -348,7 +348,7 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
return timestamp_error;
}
-int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end,
+int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size)
{
return 0;
@@ -622,12 +622,28 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
if (!rtp_end->output_enabled)
rtp_end->dropped_packets += 1;
else if (is_rtp) {
- mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, rc);
- endp->cfg->rtp_processing_cb(rtp_end, buf, &rc, RTP_BUF_SIZE);
- forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], buf, rc);
- return mgcp_udp_send(rtp_end->rtp.fd,
- &rtp_end->addr,
- rtp_end->rtp_port, buf, rc);
+ int cont;
+ int nbytes = 0;
+ int len = rc;
+ mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len);
+ do {
+ cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
+ buf, &len, RTP_BUF_SIZE);
+ if (cont < 0)
+ break;
+
+ forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx],
+ buf, len);
+ rc = mgcp_udp_send(rtp_end->rtp.fd,
+ &rtp_end->addr,
+ rtp_end->rtp_port, buf, len);
+
+ if (rc <= 0)
+ return rc;
+ nbytes += rc;
+ len = cont;
+ } while (len > 0);
+ return nbytes;
} else if (!tcfg->omit_rtcp) {
return mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
@@ -604,6 +604,12 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp,
rtp->channels = channels;
rtp->subtype_name = talloc_strdup(ctx, audio_codec);
rtp->audio_name = talloc_strdup(ctx, audio_name);
+
+ if (!strcmp(audio_codec, "G729")) {
+ rtp->frame_duration_num = 10;
+ rtp->frame_duration_den = 1000;
+ }
+
if (channels != 1)
LOGP(DMGCP, LOGL_NOTICE,
"Channels != 1 in SDP: '%s'\n", audio_name);
@@ -916,11 +922,16 @@ mgcp_header_done:
set_audio_info(p->cfg, &endp->bts_end, tcfg->audio_payload, tcfg->audio_name);
endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
- if (have_sdp) {
+ if (have_sdp)
parse_sdp_data(&endp->net_end, p);
- setup_rtp_processing(endp);
+
+ if (p->cfg->bts_force_ptime) {
+ endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
+ endp->bts_end.force_output_ptime = 1;
}
+ setup_rtp_processing(endp);
+
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
@@ -362,6 +362,26 @@ ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
RTP_STR
"Apply IP_TOS to the audio stream\n" "The DSCP value\n")
+#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS"
+DEFUN(cfg_mgcp_rtp_force_ptime,
+ cfg_mgcp_rtp_force_ptime_cmd,
+ "rtp force-ptime (10|20|40)",
+ RTP_STR FORCE_PTIME_STR
+ "The required ptime (packet duration) in ms\n")
+{
+ g_cfg->bts_force_ptime = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_rtp_force_ptime,
+ cfg_mgcp_no_rtp_force_ptime_cmd,
+ "no rtp force-ptime",
+ NO_STR RTP_STR FORCE_PTIME_STR)
+{
+ g_cfg->bts_force_ptime = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
"sdp audio fmtp-extra .NAME",
@@ -1084,6 +1104,8 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd);
@@ -1,5 +1,4 @@
/*
- * (C) 2014 by Sysmocom s.f.m.c. GmbH
* (C) 2014 by On-Waves
* All Rights Reserved
*
@@ -22,7 +21,8 @@
#include <string.h>
#include <errno.h>
-#include "bscconfig.h"
+
+#include "../../bscconfig.h"
#include "g711common.h"
#include <gsm.h>
@@ -70,6 +70,14 @@ struct mgcp_process_rtp_state {
} dst;
size_t dst_frame_size;
size_t dst_samples_per_frame;
+ int dst_packet_duration;
+
+ int is_running;
+ uint16_t next_seq;
+ uint32_t next_time;
+ int16_t samples[10*160];
+ size_t sample_cnt;
+ size_t sample_offs;
};
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
@@ -302,6 +310,9 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
break;
}
+ if (dst_end->force_output_ptime)
+ state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
+
LOGP(DMGCP, LOGL_INFO,
"Initialized RTP processing on: 0x%x "
"conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
@@ -330,44 +341,21 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
*audio_name = endp->net_end.audio_name;
}
-
-int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- char *data, int *len, int buf_size)
+static int decode_audio(struct mgcp_process_rtp_state *state,
+ uint8_t **src, size_t *nbytes)
{
- struct mgcp_process_rtp_state *state = dst_end->rtp_process_data;
- size_t rtp_hdr_size = 12;
- char *payload_data = data + rtp_hdr_size;
- int payload_len = *len - rtp_hdr_size;
- size_t sample_cnt = 0;
- size_t sample_idx;
- int16_t samples[10*160];
- uint8_t *src = (uint8_t *)payload_data;
- uint8_t *dst = (uint8_t *)payload_data;
- size_t nbytes = payload_len;
- size_t frame_remainder;
-
- if (!state)
- return 0;
-
- if (state->src_fmt == state->dst_fmt)
- return 0;
-
- /* TODO: check payload type (-> G.711 comfort noise) */
-
- /* Decode src into samples */
- while (nbytes >= state->src_frame_size) {
- if (sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(samples)) {
+ while (*nbytes >= state->src_frame_size) {
+ if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
LOGP(DMGCP, LOGL_ERROR,
"Sample buffer too small: %d > %d.\n",
- sample_cnt + state->src_samples_per_frame,
- ARRAY_SIZE(samples));
+ state->sample_cnt + state->src_samples_per_frame,
+ ARRAY_SIZE(state->samples));
return -ENOSPC;
}
switch (state->src_fmt) {
case AF_GSM:
if (gsm_decode(state->src.gsm_handle,
- (gsm_byte *)src, samples + sample_cnt) < 0) {
+ (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
LOGP(DMGCP, LOGL_ERROR,
"Failed to decode GSM.\n");
return -EINVAL;
@@ -375,54 +363,44 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
break;
#ifdef HAVE_BCG729
case AF_G729:
- bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt);
+ bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
break;
#endif
case AF_PCMA:
- alaw_decode(src, samples + sample_cnt,
+ alaw_decode(*src, state->samples + state->sample_cnt,
state->src_samples_per_frame);
break;
case AF_S16:
- memmove(samples + sample_cnt, src,
+ memmove(state->samples + state->sample_cnt, *src,
state->src_frame_size);
break;
case AF_L16:
- l16_decode(src, samples + sample_cnt,
+ l16_decode(*src, state->samples + state->sample_cnt,
state->src_samples_per_frame);
break;
default:
break;
}
- src += state->src_frame_size;
- nbytes -= state->src_frame_size;
- sample_cnt += state->src_samples_per_frame;
- }
-
- /* Add silence if necessary */
- frame_remainder = sample_cnt % state->dst_samples_per_frame;
- if (frame_remainder) {
- size_t silence = state->dst_samples_per_frame - frame_remainder;
- if (sample_cnt + silence > ARRAY_SIZE(samples)) {
- LOGP(DMGCP, LOGL_ERROR,
- "Sample buffer too small for silence: %d > %d.\n",
- sample_cnt + silence,
- ARRAY_SIZE(samples));
- return -ENOSPC;
- }
-
- while (silence > 0) {
- samples[sample_cnt] = 0;
- sample_cnt += 1;
- silence -= 1;
- }
+ *src += state->src_frame_size;
+ *nbytes -= state->src_frame_size;
+ state->sample_cnt += state->src_samples_per_frame;
}
+ return 0;
+}
+static int encode_audio(struct mgcp_process_rtp_state *state,
+ uint8_t *dst, size_t buf_size, size_t max_samples)
+{
+ int nbytes = 0;
+ size_t nsamples = 0;
/* Encode samples into dst */
- sample_idx = 0;
- nbytes = 0;
- while (sample_idx + state->dst_samples_per_frame <= sample_cnt) {
+ while (nsamples + state->dst_samples_per_frame <= max_samples) {
if (nbytes + state->dst_frame_size > buf_size) {
- LOGP(DMGCP, LOGL_ERROR,
+ if (nbytes > 0)
+ break;
+
+ /* Not even one frame fits into the buffer */
+ LOGP(DMGCP, LOGL_INFO,
"Encoding (RTP) buffer too small: %d > %d.\n",
nbytes + state->dst_frame_size, buf_size);
return -ENOSPC;
@@ -430,23 +408,24 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
switch (state->dst_fmt) {
case AF_GSM:
gsm_encode(state->dst.gsm_handle,
- samples + sample_idx, dst);
+ state->samples + state->sample_offs, dst);
break;
#ifdef HAVE_BCG729
case AF_G729:
bcg729Encoder(state->dst.g729_enc,
- samples + sample_idx, dst);
+ state->samples + state->sample_offs, dst);
break;
#endif
case AF_PCMA:
- alaw_encode(samples + sample_idx, dst,
+ alaw_encode(state->samples + state->sample_offs, dst,
state->src_samples_per_frame);
break;
case AF_S16:
- memmove(dst, samples + sample_idx, state->dst_frame_size);
+ memmove(dst, state->samples + state->sample_offs,
+ state->dst_frame_size);
break;
case AF_L16:
- l16_encode(samples + sample_idx, dst,
+ l16_encode(state->samples + state->sample_offs, dst,
state->src_samples_per_frame);
break;
default:
@@ -454,12 +433,121 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
}
dst += state->dst_frame_size;
nbytes += state->dst_frame_size;
- sample_idx += state->dst_samples_per_frame;
+ state->sample_offs += state->dst_samples_per_frame;
+ nsamples += state->dst_samples_per_frame;
}
+ state->sample_cnt -= nsamples;
+ return nbytes;
+}
- *len = rtp_hdr_size + nbytes;
- /* Patch payload type */
- data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f);
+int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
+ struct mgcp_rtp_end *dst_end,
+ char *data, int *len, int buf_size)
+{
+ struct mgcp_process_rtp_state *state = dst_end->rtp_process_data;
+ size_t rtp_hdr_size = 12;
+ char *payload_data = data + rtp_hdr_size;
+ int payload_len = *len - rtp_hdr_size;
+ // size_t sample_idx;
+ uint8_t *src = (uint8_t *)payload_data;
+ uint8_t *dst = (uint8_t *)payload_data;
+ size_t nbytes = payload_len;
+ // size_t frame_remainder;
+ size_t nsamples;
+ size_t max_samples;
+ uint32_t ts_no;
+ int rc;
- return 0;
+ if (!state)
+ return 0;
+
+ if (state->src_fmt == state->dst_fmt) {
+ if (!state->dst_packet_duration)
+ return 0;
+
+ /* TODO: repackage without transcoding */
+ }
+
+ /* If the remaining samples do not fit into a fixed ptime,
+ * a) discard them, if the next packet is much later
+ * b) add silence and * send it, if the current packet is not
+ * yet too late
+ * c) append the sample data, if the timestamp matches exactly
+ */
+
+ /* TODO: check payload type (-> G.711 comfort noise) */
+
+ if (payload_len > 0) {
+ ts_no = ntohl(*(uint32_t*)(data+4));
+ if (!state->is_running)
+ state->next_seq = ntohs(*(uint32_t*)(data+4));
+
+ state->is_running = 1;
+
+ if (state->sample_cnt > 0) {
+ int32_t delta = ts_no - state->next_time;
+ /* TODO: check sequence? reordering? packet loss? */
+
+ if (delta > state->sample_cnt)
+ /* There is a time gap between the last packet
+ * and the current one. Just discard the
+ * partial data that is left in the buffer.
+ * TODO: This can be improved by adding silence
+ * instead if the delta is small enough.
+ */
+ state->sample_cnt = 0;
+ else if (delta < 0) {
+ LOGP(DMGCP, LOGL_NOTICE,
+ "RTP time jumps backwards, delta = %d, "
+ "discarding buffered samples\n",
+ delta);
+ state->sample_cnt = 0;
+ state->sample_offs = 0;
+ return -EAGAIN;
+ }
+
+ /* Make sure the samples start without offset */
+ fprintf(stderr, "Moving %d samples to buffer start (offset %d)\n", state->sample_cnt, state->sample_offs);
+ if (state->sample_offs && state->sample_cnt)
+ memmove(&state->samples[0],
+ &state->samples[state->sample_offs],
+ state->sample_cnt * sizeof(state->samples[0]));
+ }
+
+ state->sample_offs = 0;
+
+ /* Append decoded audio to samples */
+ decode_audio(state, &src, &nbytes);
+
+ if (nbytes > 0)
+ LOGP(DMGCP, LOGL_NOTICE,
+ "Skipped audio frame in RTP packet: %d octets\n",
+ nbytes);
+ } else
+ ts_no = state->next_time;
+
+ if (state->sample_cnt < state->dst_packet_duration)
+ return -EAGAIN;
+
+ max_samples =
+ state->dst_packet_duration ?
+ state->dst_packet_duration : state->sample_cnt;
+
+ nsamples = state->sample_cnt;
+
+ rc = encode_audio(state, dst, buf_size, max_samples);
+ if (rc <= 0)
+ return rc;
+
+ nsamples -= state->sample_cnt;
+ fprintf(stderr, "Wrote %d samples to buffer (offset %d)\n", nsamples, state->sample_offs);
+
+ *len = rtp_hdr_size + rc;
+ *(uint16_t*)(data+2) = htonl(state->next_seq);
+ *(uint32_t*)(data+4) = htonl(ts_no);
+
+ state->next_seq += 1;
+ state->next_time = ts_no + nsamples;
+
+ return nsamples ? rtp_hdr_size : 0;
}