summaryrefslogtreecommitdiffstats
path: root/openbsc/src/osmo-bsc_mgcp
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-04-14 10:31:47 +0200
committerJacob Erlbeck <jerlbeck@sysmocom.de>2014-06-05 14:08:45 +0200
commit42a833e89f443116fb165c35654c9f21ceed6876 (patch)
tree12c99fe7ba58aa888147366a46f04feddf61d903 /openbsc/src/osmo-bsc_mgcp
parent136a319e910eec81ea9ff8f8a34c324557109d03 (diff)
mgcp: Add packet size (ptime) conversion
The current transcoder implemenation always does a 1:1 recoding concerning the duration of a packet. So RTP timestamps and sequence numbers are not modified. This is not sufficient in some cases, e.g. when the BTS does only allow for a single fixed ptime. This patch decouples encoding from decoding and moves the decoded samples to the state structure so that samples can be combined or drain according to the packaging of incoming and outgoing packets. This patch incorporates parts of Holger's experimental fixes in 0e669e05^..9eba68f9. Ticket: OW#1111 Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src/osmo-bsc_mgcp')
-rw-r--r--openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c229
1 files changed, 157 insertions, 72 deletions
diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c
index 91c0c38..581cd32 100644
--- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c
+++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c
@@ -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,118 @@ 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;
+ uint8_t *src = (uint8_t *)payload_data;
+ uint8_t *dst = (uint8_t *)payload_data;
+ size_t nbytes = payload_len;
+ 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 */
+ 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;
+
+ *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;
}