This patch adds the callbacks rtp_processing_cb and setup_rtp_processing_cb to mgcp_config to support arbitrary RTP payload processing.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 10 +++++++++ openbsc/include/openbsc/mgcp_internal.h | 8 ++++++++ openbsc/src/libmgcp/mgcp_network.c | 20 +++++++++++++++--- openbsc/src/libmgcp/mgcp_protocol.c | 34 ++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 1d74078..6ba0769 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -65,6 +65,7 @@ static inline int rtp_calculate_port(int multiplex, int base) struct mgcp_endpoint; struct mgcp_config; struct mgcp_trunk_config; +struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1 #define MGCP_ENDP_DLCX 2 @@ -86,6 +87,11 @@ 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, + char *data, int *len, int buf_size); +typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); #define PORT_ALLOC_STATIC 0 #define PORT_ALLOC_DYNAMIC 1
@@ -156,6 +162,10 @@ struct mgcp_config { struct in_addr transcoder_in; int transcoder_remote_base;
+ /* RTP processing */ + mgcp_processing rtp_processing_cb; + mgcp_processing_setup setup_rtp_processing_cb; + struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 9b97165..56c280d 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -91,6 +91,7 @@ struct mgcp_rtp_end { /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ int force_aligned_timing; + void *rtp_process_data;
/* * Each end has a socket... @@ -197,5 +198,12 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); 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, + char *data, int *len, int buf_size); + +int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end);
#endif diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8a5656a..4d1ad35 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -77,6 +77,7 @@ struct rtp_hdr { #define RTP_SEQ_MOD (1 << 16) #define RTP_MAX_DROPOUT 3000 #define RTP_MAX_MISORDER 100 +#define RTP_BUF_SIZE 4096
enum { @@ -347,6 +348,18 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, return timestamp_error; }
+int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size) +{ + return 0; +} + +int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + return 0; +}
/** * The RFC 3550 Appendix A assumes there are multiple sources but @@ -597,6 +610,7 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, 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, @@ -635,7 +649,7 @@ static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *
static int rtp_data_net(struct osmo_fd *fd, unsigned int what) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; @@ -719,7 +733,7 @@ static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_
static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; @@ -781,7 +795,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, int dest, struct osmo_fd *fd) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_config *cfg; int rc, proto; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 5c88c9d..0f8614c 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -107,6 +107,8 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *data); static void create_transcoder(struct mgcp_endpoint *endp); static void delete_transcoder(struct mgcp_endpoint *endp);
+static void setup_rtp_processing(struct mgcp_endpoint *endp); + static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
static uint32_t generate_call_id(struct mgcp_config *cfg) @@ -827,8 +829,10 @@ mgcp_header_done: endp->bts_end.payload_type = tcfg->audio_payload; 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); + }
/* policy CB */ if (p->cfg->policy_cb) { @@ -929,6 +933,8 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options);
+ setup_rtp_processing(endp); + /* policy CB */ if (p->cfg->policy_cb) { int rc; @@ -1169,6 +1175,9 @@ struct mgcp_config *mgcp_config_alloc(void) cfg->bts_ports.base_port = RTP_PORT_DEFAULT; cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
+ cfg->rtp_processing_cb = &mgcp_rtp_processing_default; + cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default; + /* default trunk handling */ cfg->trunk.cfg = cfg; cfg->trunk.trunk_nr = 0; @@ -1234,6 +1243,8 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) end->local_alloc = -1; talloc_free(end->fmtp_extra); end->fmtp_extra = NULL; + talloc_free(end->rtp_process_data); + end->rtp_process_data = NULL;
/* Set default values */ end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; @@ -1388,6 +1399,27 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) return send_agent(endp->cfg, buf, len); }
+static void setup_rtp_processing(struct mgcp_endpoint *endp) +{ + struct mgcp_config *cfg = endp->cfg; + + if (endp->type != MGCP_RTP_DEFAULT) + return; + + if (endp->conn_mode == MGCP_CONN_LOOPBACK) + return; + + if (endp->conn_mode & MGCP_CONN_SEND_ONLY) + cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); + else + cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL); + + if (endp->conn_mode & MGCP_CONN_RECV_ONLY) + cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); + else + cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL); +} + static void create_transcoder(struct mgcp_endpoint *endp) { int port;
This patch adds the fields channels, subtype_name, and audio_name to the struct. The field audio_name contains the full string that has been used for the last part of a SDP a=rtpmap line. The others contain decoded parts of that string. If no a=rtpmap line has been given (e.g. because dynamic payload types are not used), values are assigned when the payload type matches one of the predefined ones (GSM, G729, PCMA).
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 3 ++ openbsc/src/libmgcp/mgcp_protocol.c | 82 +++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 21 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 56c280d..72ac8e9 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -81,11 +81,14 @@ struct mgcp_rtp_end { /* per endpoint data */ int payload_type; uint32_t rate; + int channels; uint32_t frame_duration_num; uint32_t frame_duration_den; int frames_per_packet; uint32_t packet_duration_ms; char *fmtp_extra; + char *audio_name; + char *subtype_name; int output_enabled;
/* RTP patching */ diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 0f8614c..761a35c 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -28,6 +28,7 @@ #include <time.h> #include <limits.h> #include <unistd.h> +#include <errno.h>
#include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> @@ -77,6 +78,7 @@ char *strline_r(char *str, char **saveptr) #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 +#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
@@ -231,6 +233,8 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, { const char *addr = endp->cfg->local_ip; const char *fmtp_extra = endp->bts_end.fmtp_extra; + const char *audio_name = endp->bts_end.audio_name; + int payload_type = endp->bts_end.payload_type; char sdp_record[4096]; int len;
@@ -244,11 +248,12 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n" + "a=rtpmap:%d%s%s\r\n" "%s%s", endp->ci, endp->ci, addr, addr, - endp->net_end.local_port, endp->bts_end.payload_type, - endp->bts_end.payload_type, endp->tcfg->audio_name, + endp->net_end.local_port, payload_type, + payload_type, + audio_name ? " " : "", audio_name ? audio_name : "", fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : "");
if (len < 0 || len >= sizeof(sdp_record)) @@ -514,6 +519,47 @@ static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp) return ret; }
+static int set_audio_info(struct mgcp_rtp_end *rtp, + int payload_type, const char *audio_name) +{ + int rate = 8000; + int channels = 1; + char audio_codec[64]; + + talloc_free(rtp->subtype_name); + rtp->subtype_name = NULL; + talloc_free(rtp->audio_name); + rtp->audio_name = NULL; + + rtp->payload_type = payload_type; + + if (!audio_name) { + switch (payload_type) { + case 3: audio_name = "GSM/8000/1"; break; + case 8: audio_name = "PCMA/8000/1"; break; + case 18: audio_name = "G729/8000/1"; break; + default: + rtp->rate = 8000; + rtp->channels = 1; + return 0; + } + } + + if (sscanf(audio_name, "%63[^/]/%d/%d", + audio_codec, &rate, &channels) < 2) + return -EINVAL; + + rtp->rate = rate; + rtp->channels = channels; + rtp->subtype_name = talloc_strdup(NULL, audio_codec); + rtp->audio_name = talloc_strdup(NULL, audio_name); + if (channels != 1) + LOGP(DMGCP, LOGL_NOTICE, + "Channels != 1 in SDP: '%s'\n", audio_name); + + return 0; +} + static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, struct mgcp_port_range *range, int (*alloc)(struct mgcp_endpoint *endp, int port)) @@ -600,29 +646,18 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) break; case 'a': { int payload; - int rate; - int channels = 1; int ptime, ptime2 = 0; char audio_name[64]; - char audio_codec[64];
if (audio_payload == -1) break;
- if (sscanf(line, "a=rtpmap:%d %64s", + if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { if (payload != audio_payload) break;
- if (sscanf(audio_name, "%[^/]/%d/%d", - audio_codec, &rate, &channels) < 2) - break; - - rtp->rate = rate; - if (channels != 1) - LOGP(DMGCP, LOGL_NOTICE, - "Channels != 1 in SDP: '%s' on 0x%x\n", - line, ENDPOINT_NUMBER(p->endp)); + set_audio_info(rtp, payload, audio_name); } else if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) { if (ptime2 > 0 && ptime2 != ptime) @@ -645,8 +680,8 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) &port, &audio_payload) == 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); - rtp->payload_type = audio_payload; found_media = 1; + set_audio_info(rtp, audio_payload, NULL); } break; } @@ -826,7 +861,7 @@ mgcp_header_done: endp->allocated = 1;
/* set up RTP media parameters */ - endp->bts_end.payload_type = tcfg->audio_payload; + set_audio_info(&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) { @@ -1252,6 +1287,7 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) end->frames_per_packet = 0; /* unknown */ end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; + end->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; end->output_enabled = 0; }
@@ -1329,6 +1365,9 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; + const char *fmtp_extra = endp->bts_end.fmtp_extra; + const char *audio_name = endp->bts_end.audio_name; + int payload_type = endp->bts_end.payload_type;
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf), @@ -1338,10 +1377,11 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, "\r\n" "c=IN IP4 %s\r\n" "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", + "a=rtpmap:%d%s%s\r\n", msg, endpoint, mode, endp->cfg->source_addr, - port, endp->tcfg->audio_payload, - endp->tcfg->audio_payload, endp->tcfg->audio_name); + port, payload_type, + payload_type, + audio_name ? " " : "", audio_name ? audio_name : "");
if (len < 0) return;
On Mon, May 12, 2014 at 12:38:58PM +0200, Jacob Erlbeck wrote:
- rtp->subtype_name = talloc_strdup(NULL, audio_codec);
- rtp->audio_name = talloc_strdup(NULL, audio_name);
Attach this to a context that at least belongs to a MGCP context. Make sure to release these strings in mgcp_free_endp or in the reset one. Or did I miss a call to set_audio_info?
This patch adds the get_net_downlink_format_cb() callback to provide payload_type, subtype_name, and fmtp_extra suitable for use in a MGCP response sent to the network. Per default, the BTS side values are returned since these must be honoured by the net peer when sending audio to the media gateway (unless transcoding is done).
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 8 ++++++++ openbsc/include/openbsc/mgcp_internal.h | 5 +++++ openbsc/src/libmgcp/mgcp_network.c | 13 +++++++++++++ openbsc/src/libmgcp/mgcp_protocol.c | 20 ++++++++++++++------ 4 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 6ba0769..b595aba 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -92,6 +92,12 @@ typedef int (*mgcp_processing)(struct mgcp_rtp_end *dst_end, typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end); + +typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, + int *payload_type, + const char**subtype_name, + const char**fmtp_extra); + #define PORT_ALLOC_STATIC 0 #define PORT_ALLOC_DYNAMIC 1
@@ -166,6 +172,8 @@ struct mgcp_config { mgcp_processing rtp_processing_cb; mgcp_processing_setup setup_rtp_processing_cb;
+ mgcp_get_format get_net_downlink_format_cb; + struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 72ac8e9..e74b9fa 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -209,4 +209,9 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end);
+void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, + int *payload_type, + const char**subtype_name, + const char**fmtp_extra); + #endif diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 4d1ad35..dcbb97a 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -361,6 +361,19 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, return 0; }
+void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + /* Use the BTS side parameters when passing the SDP data (for + * downlink) to the net peer. + */ + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; +} + /** * The RFC 3550 Appendix A assumes there are multiple sources but * some of the supported endpoints (e.g. the nanoBTS) can only handle diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 761a35c..b23a56a 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -232,12 +232,15 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id) { const char *addr = endp->cfg->local_ip; - const char *fmtp_extra = endp->bts_end.fmtp_extra; - const char *audio_name = endp->bts_end.audio_name; - int payload_type = endp->bts_end.payload_type; + const char *fmtp_extra; + const char *audio_name; + int payload_type; char sdp_record[4096]; int len;
+ endp->cfg->get_net_downlink_format_cb(endp, &payload_type, + &audio_name, &fmtp_extra); + if (!addr) addr = endp->cfg->source_addr;
@@ -1213,6 +1216,8 @@ struct mgcp_config *mgcp_config_alloc(void) cfg->rtp_processing_cb = &mgcp_rtp_processing_default; cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
+ cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default; + /* default trunk handling */ cfg->trunk.cfg = cfg; cfg->trunk.trunk_nr = 0; @@ -1365,9 +1370,12 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; - const char *fmtp_extra = endp->bts_end.fmtp_extra; - const char *audio_name = endp->bts_end.audio_name; - int payload_type = endp->bts_end.payload_type; + const char *fmtp_extra; + const char *audio_name; + int payload_type; + + endp->cfg->get_net_downlink_format_cb(endp, &payload_type, + &audio_name, &fmtp_extra);
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf),
Don't show media related lines if the payload type has not been set. Don't show a 'a=rtpmap' line if the audio_name has not been set.
This patch unifies the SDP generation of create_response_with_sdp() and send_msg().
Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_protocol.c | 129 +++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 42 deletions(-)
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index b23a56a..62bf481 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -228,55 +228,103 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp, return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); }
-static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, - const char *msg, const char *trans_id) +static int write_response_sdp(struct mgcp_endpoint *endp, + char *sdp_record, size_t size, const char *addr) { - const char *addr = endp->cfg->local_ip; const char *fmtp_extra; const char *audio_name; int payload_type; - char sdp_record[4096]; int len; + int nchars;
endp->cfg->get_net_downlink_format_cb(endp, &payload_type, &audio_name, &fmtp_extra);
- if (!addr) - addr = endp->cfg->source_addr; - - len = snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %u\n\n" + len = snprintf(sdp_record, size, "v=0\r\n" "o=- %u 23 IN IP4 %s\r\n" "c=IN IP4 %s\r\n" - "t=0 0\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d%s%s\r\n" - "%s%s", - endp->ci, endp->ci, addr, addr, - endp->net_end.local_port, payload_type, - payload_type, - audio_name ? " " : "", audio_name ? audio_name : "", - fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); - - if (len < 0 || len >= sizeof(sdp_record)) + "t=0 0\r\n", + endp->ci, addr, addr); + + if (len < 0 || len >= size) goto buffer_too_small;
+ if (payload_type >= 0) { + nchars = snprintf(sdp_record + len, size - len, + "m=audio %d RTP/AVP %d\r\n", + endp->net_end.local_port, payload_type); + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + + if (audio_name) { + nchars = snprintf(sdp_record + len, size - len, + "a=rtpmap:%d %s\r\n", + payload_type, audio_name); + + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + } + + if (fmtp_extra) { + nchars = snprintf(sdp_record + len, size - len, + "a=rtpmap:%d %s\r\n", + payload_type, audio_name); + + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + } + } if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { - int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len, - "a=ptime:%d\r\n", - endp->bts_end.packet_duration_ms); - if (nchars < 0 || nchars >= sizeof(sdp_record) - len) + nchars = snprintf(sdp_record + len, size - len, + "a=ptime:%d\r\n", + endp->bts_end.packet_duration_ms); + if (nchars < 0 || nchars >= size - len) goto buffer_too_small;
len += nchars; } - return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); + + return len;
buffer_too_small: LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n", - sizeof(sdp_record), len); - return NULL; + size, len); + return -1; +} + +static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, + const char *msg, const char *trans_id) +{ + const char *addr = endp->cfg->local_ip; + char sdp_record[4096]; + int len; + int nchars; + + if (!addr) + addr = endp->cfg->source_addr; + + len = snprintf(sdp_record, sizeof(sdp_record), "I: %u\n\n", endp->ci); + + if (len < 0) + return NULL; + + nchars = write_response_sdp(endp, sdp_record + len, + sizeof(sdp_record) - len - 1, addr); + if (nchars < 0) + return NULL; + + len += nchars; + + sdp_record[sizeof(sdp_record) - 1] = '\0'; + + return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); }
/* @@ -711,9 +759,10 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
if (found_media) LOGP(DMGCP, LOGL_NOTICE, - "Got media info via SDP: port %d, payload %d, " + "Got media info via SDP: port %d, payload %d (%s), " "duration %d, addr %s\n", ntohs(rtp->rtp_port), rtp->payload_type, + rtp->subtype_name ? rtp->subtype_name : "unknown", rtp->packet_duration_ms, inet_ntoa(rtp->addr));
return found_media; @@ -1370,30 +1419,26 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; - const char *fmtp_extra; - const char *audio_name; - int payload_type; - - endp->cfg->get_net_downlink_format_cb(endp, &payload_type, - &audio_name, &fmtp_extra); + int nchars;
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf), "%s 42 %x@mgw MGCP 1.0\r\n" "C: 4256\r\n" "M: %s\r\n" - "\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d%s%s\r\n", - msg, endpoint, mode, endp->cfg->source_addr, - port, payload_type, - payload_type, - audio_name ? " " : "", audio_name ? audio_name : ""); + "\r\n", + msg, endpoint, mode);
if (len < 0) return;
+ nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, + endp->cfg->source_addr); + if (nchars < 0) + return; + + len += nchars; + buf[sizeof(buf) - 1] = '\0';
send_trans(endp->cfg, buf, len);
On Mon, May 12, 2014 at 12:39:00PM +0200, Jacob Erlbeck wrote:
Don't show media related lines if the payload type has not been set. Don't show a 'a=rtpmap' line if the audio_name has not been set.
Is creating a regression easy here? Specially for the missing a=rtpmap line?
This patch implements audio transcoding between the formats GSM, PCMA, L16, and optionally G.729.
The feature needs to be enabled by using the autoconf option '--enable-mgcp-transcoding'. In this case mgcp_transcode.c will be compiled and linked to osmo-bsc_mgcp, and the transcoding functions provided will be registered as processing callbacks.
If G.729 support is required, libcg729 needs to be installed and '--with-g729' must be passed to ./configure.
Ticket: OW#1111 Sponsored-by: On-Waves ehf --- openbsc/configure.ac | 15 + openbsc/src/osmo-bsc_mgcp/Makefile.am | 10 +- openbsc/src/osmo-bsc_mgcp/g711common.h | 187 ++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 15 + openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 459 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 34 +++ 6 files changed, 718 insertions(+), 2 deletions(-) create mode 100644 openbsc/src/osmo-bsc_mgcp/g711common.h create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h
diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 978f526..ead05af 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -56,6 +56,21 @@ fi AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") AC_SUBST(osmo_ac_build_smpp)
+# Enable/disable transcoding within osmo-bsc_mgcp? +AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], + [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) +AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) + +if test "$osmo_ac_mgcp_transcoding" = "yes" ; then + AC_SEARCH_LIBS(gsm_create, gsm) + if test "$osmo_ac_with_g729" = "yes" ; then + PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) + fi + AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) +fi +AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") +AC_SUBST(osmo_ac_mgcp_transcoding) +
found_libgtp=yes PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no) diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index 0456cf1..a620e7a 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,10 +1,16 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBBCG729_CFLAGS)
bin_PROGRAMS = osmo-bsc_mgcp
osmo_bsc_mgcp_SOURCES = mgcp_main.c +if BUILD_MGCP_TRANSCODING + osmo_bsc_mgcp_SOURCES += mgcp_transcode.c +endif osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ - $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) + $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) + +noinst_HEADERS = g711common.h mgcp_transcode.h diff --git a/openbsc/src/osmo-bsc_mgcp/g711common.h b/openbsc/src/osmo-bsc_mgcp/g711common.h new file mode 100644 index 0000000..cb35fc6 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/g711common.h @@ -0,0 +1,187 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara abramo@alsa-project.org + * + * Wrapper for linphone Codec class by Simon Morlat simon.morlat@linphone.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 + */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; /*7 = 4 + 3*/ + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. + */ + +static inline unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) /*256 = 32 << 3*/ + aval = pcm_val >> 4; /*4 = 1 + 3*/ + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static inline int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = 0x84 - pcm_val; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static inline int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 14ec221..5ac4c26 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -31,6 +31,11 @@
#include <sys/socket.h>
+#include "g711common.h" +#include <gsm.h> +#include <bcg729/decoder.h> +#include <bcg729/encoder.h> + #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/mgcp.h> @@ -49,6 +54,10 @@
#include "../../bscconfig.h"
+#ifdef BUILD_MGCP_TRANSCODING +#include "mgcp_transcode.h" +#endif + /* this is here for the vty... it will never be called */ void subscr_put() { abort(); }
@@ -207,6 +216,12 @@ int main(int argc, char **argv) if (!cfg) return -1;
+#ifdef BUILD_MGCP_TRANSCODING + cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; + cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; + cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; +#endif + vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(&log_info); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c new file mode 100644 index 0000000..c6b2508 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -0,0 +1,459 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + + +#include "../../bscconfig.h" + +#include "g711common.h" +#include <gsm.h> +#ifdef HAVE_BCG729 +#include <bcg729/decoder.h> +#include <bcg729/encoder.h> +#endif + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include <osmocom/core/talloc.h> + +enum audio_format { + AF_INVALID, + AF_S16, + AF_L16, + AF_GSM, + AF_G729, + AF_PCMA +}; + +struct mgcp_process_rtp_state { + /* decoding */ + enum audio_format src_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729DecoderChannelContextStruct *g729_dec; +#endif + } src; + size_t src_frame_size; + size_t src_samples_per_frame; + + /* processing */ + + /* encoding */ + enum audio_format dst_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729EncoderChannelContextStruct *g729_enc; +#endif + } dst; + size_t dst_frame_size; + size_t dst_samples_per_frame; +}; + +static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) +{ + if (rtp_end->subtype_name) { + if (!strcmp("GSM", rtp_end->subtype_name)) + return AF_GSM; + if (!strcmp("PCMA", rtp_end->subtype_name)) + return AF_PCMA; +#ifdef HAVE_BCG729 + if (!strcmp("G729", rtp_end->subtype_name)) + return AF_G729; +#endif + if (!strcmp("L16", rtp_end->subtype_name)) + return AF_L16; + } + + switch (rtp_end->payload_type) { + case 3 /* GSM */: + return AF_GSM; + case 8 /* PCMA */: + return AF_PCMA; +#ifdef HAVE_BCG729 + case 18 /* G.729 */: + return AF_G729; +#endif + case 11 /* L16 */: + return AF_L16; + default: + return AF_INVALID; + } +} + +static void l16_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) { + buf[0] = sample[0] >> 8; + buf[1] = sample[0] & 0xff; + } +} + +static void l16_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) + sample[0] = ((short)buf[0] << 8) | buf[1]; +} + +static void alaw_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n) + *(buf++) = s16_to_alaw(*(sample++)); +} + +static void alaw_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n) + *(sample++) = alaw_to_s16(*(buf++)); +} + +static int processing_state_destructor(struct mgcp_process_rtp_state *state) +{ + switch (state->src_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->src.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->src.g729_dec) + closeBcg729DecoderChannel(state->src.g729_dec); + break; +#endif + default: + break; + } + switch (state->dst_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->dst.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->dst.g729_enc) + closeBcg729EncoderChannel(state->dst.g729_enc); + break; +#endif + default: + break; + } + return 0; +} + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (state) { + talloc_free(state); + dst_end->rtp_process_data = NULL; + } + + if (!src_end) + return 0; + + src_fmt = get_audio_format(src_end); + dst_fmt = get_audio_format(dst_end); + + LOGP(DMGCP, LOGL_ERROR, + "Checking transcoding: %s (%d) -> %s (%d)\n", + src_end->subtype_name, src_end->payload_type, + dst_end->subtype_name, dst_end->payload_type); + + if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { + if (!src_end->subtype_name || !dst_end->subtype_name) + /* Not enough info, do nothing */ + return 0; + + if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) + /* Nothing to do */ + return 0; + + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: %s codec not supported (%s -> %s).\n", + src_fmt != AF_INVALID ? "destination" : "source", + src_end->audio_name, dst_end->audio_name); + return -EINVAL; + } + + if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: rate conversion (%d -> %d) not supported.\n", + src_end->rate, dst_end->rate); + return -EINVAL; + } + + state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); + talloc_set_destructor(state, processing_state_destructor); + dst_end->rtp_process_data = state; + + state->src_fmt = src_fmt; + + switch (state->src_fmt) { + case AF_L16: + case AF_S16: + state->src_frame_size = 80 * sizeof(short); + state->src_samples_per_frame = 80; + break; + case AF_GSM: + state->src_frame_size = sizeof(gsm_frame); + state->src_samples_per_frame = 160; + state->src.gsm_handle = gsm_create(); + if (!state->src.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM decoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->src_frame_size = 10; + state->src_samples_per_frame = 80; + state->src.g729_dec = initBcg729DecoderChannel(); + if (!state->src.g729_dec) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->src_frame_size = 80; + state->src_samples_per_frame = 80; + break; + default: + break; + } + + state->dst_fmt = dst_fmt; + + switch (state->dst_fmt) { + case AF_L16: + case AF_S16: + state->dst_frame_size = 80*sizeof(short); + state->dst_samples_per_frame = 80; + break; + case AF_GSM: + state->dst_frame_size = sizeof(gsm_frame); + state->dst_samples_per_frame = 160; + state->dst.gsm_handle = gsm_create(); + if (!state->dst.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM encoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->dst_frame_size = 10; + state->dst_samples_per_frame = 80; + state->dst.g729_enc = initBcg729EncoderChannel(); + if (!state->dst.g729_enc) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->dst_frame_size = 80; + state->dst_samples_per_frame = 80; + break; + default: + break; + } + + LOGP(DMGCP, LOGL_INFO, + "Initialized RTP processing on: 0x%x " + "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", + ENDPOINT_NUMBER(endp), + src_fmt, src_end->payload_type, src_end->rate, src_end->fmtp_extra, + dst_fmt, dst_end->payload_type, dst_end->rate, dst_end->fmtp_extra); + + return 0; +} + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; + if (!state || endp->net_end.payload_type < 0) { + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; + return; + } + + *payload_type = endp->net_end.payload_type; + *fmtp_extra = endp->net_end.fmtp_extra; + *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) +{ + 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)) { + LOGP(DMGCP, LOGL_ERROR, + "Sample buffer too small: %d > %d.\n", + sample_cnt + state->src_samples_per_frame, + ARRAY_SIZE(samples)); + return -ENOSPC; + } + switch (state->src_fmt) { + case AF_GSM: + if (gsm_decode(state->src.gsm_handle, + (gsm_byte *)src, samples + sample_cnt) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to decode GSM.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt); + break; +#endif + case AF_PCMA: + alaw_decode(src, samples + sample_cnt, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(samples + sample_cnt, src, + state->src_frame_size); + break; + case AF_L16: + l16_decode(src, samples + 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; + } + } + + /* Encode samples into dst */ + sample_idx = 0; + nbytes = 0; + while (sample_idx + state->dst_samples_per_frame <= sample_cnt) { + if (nbytes + state->dst_frame_size > buf_size) { + LOGP(DMGCP, LOGL_ERROR, + "Encoding (RTP) buffer too small: %d > %d.\n", + nbytes + state->dst_frame_size, buf_size); + return -ENOSPC; + } + switch (state->dst_fmt) { + case AF_GSM: + gsm_encode(state->dst.gsm_handle, + samples + sample_idx, dst); + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Encoder(state->dst.g729_enc, + samples + sample_idx, dst); + break; +#endif + case AF_PCMA: + alaw_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(dst, samples + sample_idx, state->dst_frame_size); + break; + case AF_L16: + l16_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + default: + break; + } + dst += state->dst_frame_size; + nbytes += state->dst_frame_size; + sample_idx += state->dst_samples_per_frame; + } + + *len = rtp_hdr_size + nbytes; + /* Patch payload type */ + data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f); + + /* TODO: remove me + fprintf(stderr, "sample_cnt = %d, sample_idx = %d, plen = %d -> %d, " + "hdr_size = %d, len = %d, pt = %d\n", + sample_cnt, sample_idx, payload_len, nbytes, rtp_hdr_size, *len, + data[1]); + */ + + return 0; +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h new file mode 100644 index 0000000..2dfb06a --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h @@ -0,0 +1,34 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ +#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra); + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size); +#endif /* OPENBSC_MGCP_TRANSCODE_H */
On Mon, May 12, 2014 at 12:39:01PM +0200, Jacob Erlbeck wrote:
+#include "g711common.h" +#include <gsm.h> +#include <bcg729/decoder.h> +#include <bcg729/encoder.h>
these must be guarded with the approriate defines. Specially the g729 ones. I think you are lucky as /usr/include/bcg729/encoder.h just exists. :)
+++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -0,0 +1,459 @@ +/*
- (C) 2014 by On-Waves
shared copyright here. :)
+#include "../../bscconfig.h"
Does this work with make distcheck? srcdir != builddir?
- /* cleanup first */
- if (state) {
talloc_free(state);dst_end->rtp_process_data = NULL;
state = NULL;
- }
Or just avoid assigning state that early?
LOGP(DMGCP, LOGL_ERROR,"Cannot transcode: %s codec not supported (%s -> %s).\n",src_fmt != AF_INVALID ? "destination" : "source",src_end->audio_name, dst_end->audio_name);return -EINVAL;
Will the CRCX/MDCX fail in this case? I am a bit too lazy to check this right now.
- /* TODO: remove me
- fprintf(stderr, "sample_cnt = %d, sample_idx = %d, plen = %d -> %d, "
"hdr_size = %d, len = %d, pt = %d\n",sample_cnt, sample_idx, payload_len, nbytes, rtp_hdr_size, *len,data[1]);*/
You want to keep this for now?
+#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H
I started to use "#pragma once". It is supported by GCC for a long time and even the Microsoft Compiler handles it correctly.
Hello Holger,
I'm already modifying the patches and planning to send them tomorrow.
On 14.05.2014 07:46, Holger Hans Peter Freyther wrote:
On Mon, May 12, 2014 at 12:39:01PM +0200, Jacob Erlbeck wrote:
+#include "g711common.h" +#include <gsm.h> +#include <bcg729/decoder.h> +#include <bcg729/encoder.h>
these must be guarded with the approriate defines. Specially the g729 ones. I think you are lucky as /usr/include/bcg729/encoder.h just exists. :)
Thanks for spotting those, they're just left overs and can be removed completely (the real ones are in mpgc_transcode.c are guarded).
+++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -0,0 +1,459 @@ +/*
- (C) 2014 by On-Waves
shared copyright here. :)
By whom?
+#include "../../bscconfig.h"
Does this work with make distcheck? srcdir != builddir?
Apparently yes. This line dates back to 2010 (5a29c7fa895a14112a1ac65e541f4b87ae9dba04) and can be found like that at several other places too. Removing the '../../' works too, but I don't want to change that within this commit.
- /* cleanup first */
- if (state) {
talloc_free(state);dst_end->rtp_process_data = NULL;state = NULL;
- }
Or just avoid assigning state that early?
This might called multiply during a single call and this way does a full transcoding reset every time including cleaning up and disabling it when called with src_end == NULL.
LOGP(DMGCP, LOGL_ERROR,"Cannot transcode: %s codec not supported (%s -> %s).\n",src_fmt != AF_INVALID ? "destination" : "source",src_end->audio_name, dst_end->audio_name);return -EINVAL;Will the CRCX/MDCX fail in this case? I am a bit too lazy to check this right now.
No. It just falls back to passing all packets unmodified. I'd rather like to address this when real negiation is added, but OTOH it wouldn't be much effort to just terminate the connection in this case.
- /* TODO: remove me
- fprintf(stderr, "sample_cnt = %d, sample_idx = %d, plen = %d -> %d, "
"hdr_size = %d, len = %d, pt = %d\n",sample_cnt, sample_idx, payload_len, nbytes, rtp_hdr_size, *len,data[1]);*/You want to keep this for now?
No.
+#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H
I started to use "#pragma once". It is supported by GCC for a long time and even the Microsoft Compiler handles it correctly.
Hmm, I just found that in only single file yet. I'm just traditionally reluctant with #pragma'a in general, but if we want to change the style I can change this accordingly.
On Wed, May 14, 2014 at 02:44:26PM +0200, Jacob Erlbeck wrote:
Hi!
I started to use "#pragma once". It is supported by GCC for a long time and even the Microsoft Compiler handles it correctly.
Hmm, I just found that in only single file yet. I'm just traditionally reluctant with #pragma'a in general, but if we want to change the style I can change this accordingly.
I started to use "#pragma once" in the osmo-pcu. I highly encourage to use it for new files.
On Wed, May 14, 2014 at 02:44:26PM +0200, Jacob Erlbeck wrote:
- /* cleanup first */
- if (state) {
talloc_free(state);dst_end->rtp_process_data = NULL;state = NULL;
- }
Or just avoid assigning state that early?
This might called multiply during a single call and this way does a full transcoding reset every time including cleaning up and disabling it when called with src_end == NULL.
Yes, my question was maybe use if (dst_end->rtp_process_data) and assign state only after it. What we should avoid is having state be a dangling pointer.
This tool uses mgcp_transcode.c to convert audio data from stdin to stdout.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/Makefile | 17 ++++++ openbsc/contrib/testconv/testconv_main.c | 91 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 13 ++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 2 + 4 files changed, 123 insertions(+) create mode 100644 openbsc/contrib/testconv/Makefile create mode 100644 openbsc/contrib/testconv/testconv_main.c
diff --git a/openbsc/contrib/testconv/Makefile b/openbsc/contrib/testconv/Makefile new file mode 100644 index 0000000..90adecc --- /dev/null +++ b/openbsc/contrib/testconv/Makefile @@ -0,0 +1,17 @@ + +OBJS = testconv_main.o mgcp_transcode.o + +CC = gcc +CFLAGS = -O0 -ggdb -Wall +LDFLAGS = +CPPFLAGS = -I../.. -I../../include $(shell pkg-config --cflags libosmocore) $(shell pkg-config --cflags libbcg729) +LIBS = ../../src/libmgcp/libmgcp.a ../../src/libcommon/libcommon.a $(shell pkg-config --libs libosmocore) $(shell pkg-config --libs libbcg729) -lgsm -lrt + +testconv: $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +testconv_main.o: testconv_main.c +mgcp_transcode.o: ../../src/osmo-bsc_mgcp/mgcp_transcode.c + +$(OBJS): + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c new file mode 100644 index 0000000..c2785f2 --- /dev/null +++ b/openbsc/contrib/testconv/testconv_main.c @@ -0,0 +1,91 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/application.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include "bscconfig.h" +#ifndef BUILD_MGCP_TRANSCODING +#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" +#endif + +#include "src/osmo-bsc_mgcp/mgcp_transcode.h" + +static int audio_name_to_type(const char *name) +{ + if (!strcasecmp(name, "gsm")) + return 3; +#ifdef HAVE_BCG729 + else if (!strcasecmp(name, "g729")) + return 18; +#endif + else if (!strcasecmp(name, "pcma")) + return 8; + else if (!strcasecmp(name, "l16")) + return 11; + return -1; +} + +int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); + +int main(int argc, char **argv) +{ + char buf[4096] = {0}; + int cc, rc; + struct mgcp_rtp_end dst_end = {0}; + struct mgcp_rtp_end src_end = {0}; + struct mgcp_trunk_config tcfg = {{0}}; + struct mgcp_endpoint endp = {0}; + struct mgcp_process_rtp_state *state; + int in_size; + + osmo_init_logging(&log_info); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + endp.tcfg = &tcfg; + + 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) + errx(1, "invalid input format '%s'", argv[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); + if (rc < 0) + errx(1, "setup failed: %s", strerror(-rc)); + + 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); + + while ((cc = read(0, buf + 12, in_size))) { + 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)); + + cc -= 12; /* ignore RTP header */ + if (write(1, buf + 12, cc) != cc) + err(1, "write"); + } + return 0; +} + diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c index c6b2508..ea4bd74 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -72,6 +72,19 @@ struct mgcp_process_rtp_state { size_t dst_samples_per_frame; };
+int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) +{ + struct mgcp_process_rtp_state *state = state_; + if (dst) + return (nsamples >= 0 ? + nsamples / state->dst_samples_per_frame : + 1) * state->dst_frame_size; + else + return (nsamples >= 0 ? + nsamples / state->src_samples_per_frame : + 1) * state->src_frame_size; +} + static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) { if (rtp_end->subtype_name) { diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h index 2dfb06a..0961634 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h @@ -31,4 +31,6 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); + +int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); #endif /* OPENBSC_MGCP_TRANSCODE_H */
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 --- openbsc/contrib/testconv/testconv_main.c | 52 +++++-- openbsc/include/openbsc/mgcp.h | 5 +- openbsc/include/openbsc/mgcp_internal.h | 3 +- openbsc/src/libmgcp/mgcp_network.c | 30 +++- openbsc/src/libmgcp/mgcp_protocol.c | 15 +- openbsc/src/libmgcp/mgcp_vty.c | 22 +++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 233 +++++++++++++++++++--------- 7 files changed, 259 insertions(+), 101 deletions(-)
diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index c2785f2..aee7304 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -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; } diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index b595aba..eb64e32 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -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; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index e74b9fa..c7bc2a8 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -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, diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index dcbb97a..42f0381 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -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, diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 62bf481..f26587e 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -604,6 +604,12 @@ static int set_audio_info(struct mgcp_rtp_end *rtp, rtp->channels = channels; rtp->subtype_name = talloc_strdup(NULL, audio_codec); rtp->audio_name = talloc_strdup(NULL, 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(&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; diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 953d34b..f1afa95 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -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); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c index ea4bd74..edd3178 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -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,19 +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;
- /* TODO: remove me - fprintf(stderr, "sample_cnt = %d, sample_idx = %d, plen = %d -> %d, " - "hdr_size = %d, len = %d, pt = %d\n", - sample_cnt, sample_idx, payload_len, nbytes, rtp_hdr_size, *len, - data[1]); - */ + if (!state) + return 0;
- 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; }
This modification allows it to set the number of samples per packet that is written to the output.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/testconv_main.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index aee7304..e74c686 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -46,6 +46,10 @@ int main(int argc, char **argv) struct mgcp_endpoint endp = {0}; struct mgcp_process_rtp_state *state; int in_size; + int in_samples = 160; + int out_samples = 0; + uint32_t ts = 0; + uint16_t seq = 0;
osmo_init_logging(&log_info);
@@ -58,12 +62,20 @@ int main(int argc, char **argv) src_end = &endp.net_end;
if (argc <= 2) - errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16}"); + errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]");
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) errx(1, "invalid output format '%s'", argv[2]); + if (argc > 3) + out_samples = atoi(argv[3]); + + if (out_samples) { + dst_end->frame_duration_den = dst_end->rate; + dst_end->frame_duration_num = out_samples; + dst_end->frames_per_packet = 1; + }
rc = mgcp_transcoding_setup(&endp, dst_end, src_end); if (rc < 0) @@ -72,7 +84,7 @@ int main(int argc, char **argv) state = dst_end->rtp_process_data; OSMO_ASSERT(state != NULL);
- in_size = mgcp_transcoding_get_frame_size(state, 160, 0); + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12);
buf[1] = src_end->payload_type; @@ -87,13 +99,19 @@ int main(int argc, char **argv) if (cc != in_size) err(1, "read");
+ *(uint16_t*)(buf+2) = htonl(seq); + *(uint32_t*)(buf+4) = htonl(ts); + + seq += 1; + ts += in_samples; + cc += 12; /* include RTP header */
len = cc;
do { cont = mgcp_transcoding_process_rtp(&endp, dst_end, - buf, &len, sizeof(buf)); + buf, &len, sizeof(buf)); if (cont == -EAGAIN) { fprintf(stderr, "Got EAGAIN\n"); break;
This patch adds test cases for transcoding and repacking.
Sponsored-by: On-Waves ehf --- openbsc/tests/atlocal.in | 1 + openbsc/tests/mgcp/Makefile.am | 19 +- openbsc/tests/mgcp/mgcp_transcoding_test.c | 377 +++++++++++++++++++ openbsc/tests/mgcp/mgcp_transcoding_test.ok | 534 +++++++++++++++++++++++++++ openbsc/tests/testsuite.at | 7 + 5 files changed, 935 insertions(+), 3 deletions(-) create mode 100644 openbsc/tests/mgcp/mgcp_transcoding_test.c create mode 100644 openbsc/tests/mgcp/mgcp_transcoding_test.ok
diff --git a/openbsc/tests/atlocal.in b/openbsc/tests/atlocal.in index 4635113..542a78e 100644 --- a/openbsc/tests/atlocal.in +++ b/openbsc/tests/atlocal.in @@ -1,3 +1,4 @@ enable_nat_test='@osmo_ac_build_nat@' enable_smpp_test='@osmo_ac_build_smpp@' enable_bsc_test='@osmo_ac_build_bsc@' +enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@' diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 71bf8c0..81f28ae 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -1,11 +1,15 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir) +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS)
-EXTRA_DIST = mgcp_test.ok +EXTRA_DIST = mgcp_test.ok mgcp_transcoding_test.ok
noinst_PROGRAMS = mgcp_test
+if BUILD_MGCP_TRANSCODING +noinst_PROGRAMS += mgcp_transcoding_test +endif + mgcp_test_SOURCES = mgcp_test.c
mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ @@ -13,3 +17,12 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL) + +mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c $(top_builddir)/src/osmo-bsc_mgcp/mgcp_transcode.c + +mgcp_transcoding_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBRARY_DL) diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c new file mode 100644 index 0000000..a1af157 --- /dev/null +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -0,0 +1,377 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/application.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include "bscconfig.h" +#ifndef BUILD_MGCP_TRANSCODING +#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" +#endif + +#include "src/osmo-bsc_mgcp/mgcp_transcode.h" + +uint8_t *audio_frame_l16[] = { +}; + +struct rtp_packets { + float t; + int len; + char *data; +}; + +struct rtp_packets audio_packets_l16[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 332, + "\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + }, +}; + +struct rtp_packets audio_packets_gsm[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_size[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 41, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_data[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" + "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" + "\xEE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_ptype[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_g729[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 32, + "\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5" + "\xB2\x95\xC4\xAD" + }, +}; + +struct rtp_packets audio_packets_pcma[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 172, + "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + }, +}; + + + +static int audio_name_to_type(const char *name) +{ + if (!strcasecmp(name, "gsm")) + return 3; +#ifdef HAVE_BCG729 + else if (!strcasecmp(name, "g729")) + return 18; +#endif + else if (!strcasecmp(name, "pcma")) + return 8; + else if (!strcasecmp(name, "l16")) + return 11; + return -1; +} + +int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); + +static int transcode_test(const char *srcfmt, const char *dstfmt, + uint8_t *src_pkts, size_t src_pkt_size) +{ + char buf[4096] = {0x80, 0}; + int rc; + 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; + int in_size; + int in_samples = 160; + int len, cont; + + printf("== Transcoding test ==\n"); + printf("converting %s -> %s\n", srcfmt, dstfmt); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + endp.tcfg = &tcfg; + mgcp_free_endp(&endp); + + dst_end = &endp.bts_end; + src_end = &endp.net_end; + + src_end->payload_type = audio_name_to_type(srcfmt); + dst_end->payload_type = audio_name_to_type(dstfmt); + + 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; + OSMO_ASSERT(state != NULL); + + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); + OSMO_ASSERT(sizeof(buf) >= in_size + 12); + + memcpy(buf, src_pkts, src_pkt_size); + + len = src_pkt_size; + + cont = mgcp_transcoding_process_rtp(&endp, dst_end, + buf, &len, sizeof(buf)); + if (cont < 0) + errx(1, "processing failed: %s", strerror(-cont)); + + if (len < 24) { + printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len)); + } else { + const char *str = osmo_hexdump((unsigned char *)buf, len); + int i = 0; + const int prefix = 4; + const int cutlen = 48; + int nchars = 0; + + printf("encoded:\n"); + do { + nchars = printf("% *s%-.*s", prefix, "", cutlen, str + i); + i += nchars - prefix; + printf("\n"); + } while (nchars - prefix >= cutlen); + } + return 0; +} + +static int test_repacking(int in_samples, int out_samples, int no_transcode) +{ + char buf[4096] = {0x80, 0}; + int cc, rc; + struct mgcp_rtp_end *dst_end; + struct mgcp_rtp_end *src_end; + struct mgcp_config *cfg; + struct mgcp_trunk_config tcfg = {{0}}; + struct mgcp_endpoint endp = {0}; + struct mgcp_process_rtp_state *state; + int in_cnt; + int out_size; + int in_size; + uint32_t ts = 0; + uint16_t seq = 0; + const char *srcfmt = "pcma"; + const char *dstfmt = no_transcode ? "pcma" : "l16"; + + cfg = mgcp_config_alloc(); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + tcfg.cfg = cfg; + endp.tcfg = &tcfg; + endp.cfg = cfg; + mgcp_free_endp(&endp); + + dst_end = &endp.bts_end; + src_end = &endp.net_end; + + printf("== Transcoding test ==\n"); + printf("converting %s -> %s\n", srcfmt, dstfmt); + + src_end->payload_type = audio_name_to_type(srcfmt); + dst_end->payload_type = audio_name_to_type(dstfmt); + + if (out_samples) { + dst_end->frame_duration_den = dst_end->rate; + dst_end->frame_duration_num = out_samples; + dst_end->frames_per_packet = 1; + dst_end->force_output_ptime = 1; + } + + 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; + OSMO_ASSERT(state != NULL); + + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); + OSMO_ASSERT(sizeof(buf) >= in_size + 12); + + out_size = mgcp_transcoding_get_frame_size(state, -1, 1); + OSMO_ASSERT(sizeof(buf) >= out_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); + + for (in_cnt = 0; in_cnt < 16; in_cnt++) { + int cont; + int len; + + /* fake PCMA data */ + printf("generating %d %s input samples\n", in_samples, srcfmt); + for (cc = 0; cc < in_samples; cc++) + buf[12+cc] = cc; + + *(uint16_t*)(buf+2) = htonl(seq); + *(uint32_t*)(buf+4) = htonl(ts); + + seq += 1; + ts += in_samples; + + cc += 12; /* include RTP header */ + + 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 */ + + printf("got %d %s output frames (%d octets)\n", + len / out_size, dstfmt, len); + + len = cont; + } while (len > 0); + } + return 0; +} + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + + printf("=== Transcoding Good Cases ===\n"); + + transcode_test("l16", "l16", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("l16", "gsm", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("l16", "pcma", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("gsm", "l16", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("gsm", "gsm", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("pcma", "l16", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + transcode_test("pcma", "gsm", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + transcode_test("pcma", "pcma", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + + printf("=== Transcoding Bad Cases ===\n"); + + printf("Invalid size:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_size[0].data, + audio_packets_gsm_invalid_size[0].len); + + printf("Invalid data:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_data[0].data, + audio_packets_gsm_invalid_data[0].len); + + printf("Invalid payload type:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_ptype[0].data, + audio_packets_gsm_invalid_ptype[0].len); + + printf("=== Repacking ===\n"); + + test_repacking(160, 160, 0); + test_repacking(160, 160, 1); + test_repacking(160, 80, 0); + test_repacking(160, 80, 1); + test_repacking(160, 320, 0); + test_repacking(160, 320, 1); + test_repacking(160, 240, 0); + test_repacking(160, 240, 1); + test_repacking(160, 100, 0); + test_repacking(160, 100, 1); + + return 0; +} + diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.ok b/openbsc/tests/mgcp/mgcp_transcoding_test.ok new file mode 100644 index 0000000..189d079 --- /dev/null +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.ok @@ -0,0 +1,534 @@ +=== Transcoding Good Cases === +== Transcoding test == +converting l16 -> l16 +encoded: + 80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed +== Transcoding test == +converting l16 -> gsm +encoded: + 80 0b 00 00 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de +== Transcoding test == +converting l16 -> pcma +encoded: + 80 0b 00 00 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 +== Transcoding test == +converting gsm -> l16 +encoded: + 80 03 00 00 00 00 00 a0 11 22 33 44 00 00 54 00 + 59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0 + 46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0 + 24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8 + 66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40 + 37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0 + 4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90 + 4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48 + 50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8 + 54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30 + 4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8 + 55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58 + 51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98 + 51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8 + 54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0 + 51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18 + 50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40 + 51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8 + 4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08 + 4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18 + 4d a0 3b 98 07 90 cf 18 b4 68 c4 88 +== Transcoding test == +converting gsm -> gsm +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf + 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 + 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 + 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 + 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd + 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf + 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf + 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 + db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 + f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 + ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 +== Transcoding test == +converting pcma -> l16 +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 +== Transcoding test == +converting pcma -> gsm +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 d4 b9 f4 5d + d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2 + 0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39 +== Transcoding test == +converting pcma -> pcma +encoded: + 80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 +=== Transcoding Bad Cases === +Invalid size: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a +Invalid data: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 ee ee ee ee + ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee + ee ee ee ee ee ee ee ee ee ee ee ee ee +Invalid payload type: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf + 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 + 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 + 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 + 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd + 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf + 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf + 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 + db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 + f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 + ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 +=== Repacking === +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 4465b25..57310d6 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -27,6 +27,13 @@ cat $abs_srcdir/mgcp/mgcp_test.ok > expout AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([mgcp-trans]) +AT_KEYWORDS([mgcp-trans]) +AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77]) +cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore]) +AT_CLEANUP + AT_SETUP([gprs]) AT_KEYWORDS([gprs]) cat $abs_srcdir/gprs/gprs_test.ok > expout
This patch moves the files relevant to transcoding from src/osmo-bsc_mgcp to src/libmgcp and src/include/openbsc. Makefiles and include directives are being updated accordingly.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/Makefile | 3 +- openbsc/contrib/testconv/testconv_main.c | 2 +- openbsc/include/openbsc/Makefile.am | 3 +- openbsc/include/openbsc/mgcp_transcode.h | 36 ++ openbsc/src/libmgcp/Makefile.am | 12 +- openbsc/src/libmgcp/g711common.h | 187 ++++++++++ openbsc/src/libmgcp/mgcp_transcode.c | 553 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/Makefile.am | 9 +- openbsc/src/osmo-bsc_mgcp/g711common.h | 187 ---------- openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 7 +- openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 553 ---------------------------- openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 36 -- openbsc/tests/mgcp/Makefile.am | 2 +- openbsc/tests/mgcp/mgcp_transcoding_test.c | 2 +- 14 files changed, 795 insertions(+), 797 deletions(-) create mode 100644 openbsc/include/openbsc/mgcp_transcode.h create mode 100644 openbsc/src/libmgcp/g711common.h create mode 100644 openbsc/src/libmgcp/mgcp_transcode.c delete mode 100644 openbsc/src/osmo-bsc_mgcp/g711common.h delete mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c delete mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h
diff --git a/openbsc/contrib/testconv/Makefile b/openbsc/contrib/testconv/Makefile index 90adecc..bb856f7 100644 --- a/openbsc/contrib/testconv/Makefile +++ b/openbsc/contrib/testconv/Makefile @@ -1,5 +1,5 @@
-OBJS = testconv_main.o mgcp_transcode.o +OBJS = testconv_main.o
CC = gcc CFLAGS = -O0 -ggdb -Wall @@ -11,7 +11,6 @@ testconv: $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
testconv_main.o: testconv_main.c -mgcp_transcode.o: ../../src/osmo-bsc_mgcp/mgcp_transcode.c
$(OBJS): $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index e74c686..89dce1a 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -17,7 +17,7 @@ #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif
-#include "src/osmo-bsc_mgcp/mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h"
static int audio_name_to_type(const char *name) { diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 8f7c1c4..6b08d07 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -13,7 +13,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ - arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h + arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ + mgcp_transcode.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/mgcp_transcode.h b/openbsc/include/openbsc/mgcp_transcode.h new file mode 100644 index 0000000..0961634 --- /dev/null +++ b/openbsc/include/openbsc/mgcp_transcode.h @@ -0,0 +1,36 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ +#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra); + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size); + +int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); +#endif /* OPENBSC_MGCP_TRANSCODE_H */ diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am index 72f625d..bd02e61 100644 --- a/openbsc/src/libmgcp/Makefile.am +++ b/openbsc/src/libmgcp/Makefile.am @@ -1,7 +1,15 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBBCG729_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) \ + $(LIBBCG729_LIBS)
noinst_LIBRARIES = libmgcp.a
+noinst_HEADERS = g711common.h + libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c + +if BUILD_MGCP_TRANSCODING + libmgcp_a_SOURCES += mgcp_transcode.c +endif diff --git a/openbsc/src/libmgcp/g711common.h b/openbsc/src/libmgcp/g711common.h new file mode 100644 index 0000000..cb35fc6 --- /dev/null +++ b/openbsc/src/libmgcp/g711common.h @@ -0,0 +1,187 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara abramo@alsa-project.org + * + * Wrapper for linphone Codec class by Simon Morlat simon.morlat@linphone.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 + */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; /*7 = 4 + 3*/ + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. + */ + +static inline unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) /*256 = 32 << 3*/ + aval = pcm_val >> 4; /*4 = 1 + 3*/ + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static inline int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = 0x84 - pcm_val; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static inline int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c new file mode 100644 index 0000000..edd3178 --- /dev/null +++ b/openbsc/src/libmgcp/mgcp_transcode.c @@ -0,0 +1,553 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + + +#include "../../bscconfig.h" + +#include "g711common.h" +#include <gsm.h> +#ifdef HAVE_BCG729 +#include <bcg729/decoder.h> +#include <bcg729/encoder.h> +#endif + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include <osmocom/core/talloc.h> + +enum audio_format { + AF_INVALID, + AF_S16, + AF_L16, + AF_GSM, + AF_G729, + AF_PCMA +}; + +struct mgcp_process_rtp_state { + /* decoding */ + enum audio_format src_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729DecoderChannelContextStruct *g729_dec; +#endif + } src; + size_t src_frame_size; + size_t src_samples_per_frame; + + /* processing */ + + /* encoding */ + enum audio_format dst_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729EncoderChannelContextStruct *g729_enc; +#endif + } 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) +{ + struct mgcp_process_rtp_state *state = state_; + if (dst) + return (nsamples >= 0 ? + nsamples / state->dst_samples_per_frame : + 1) * state->dst_frame_size; + else + return (nsamples >= 0 ? + nsamples / state->src_samples_per_frame : + 1) * state->src_frame_size; +} + +static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) +{ + if (rtp_end->subtype_name) { + if (!strcmp("GSM", rtp_end->subtype_name)) + return AF_GSM; + if (!strcmp("PCMA", rtp_end->subtype_name)) + return AF_PCMA; +#ifdef HAVE_BCG729 + if (!strcmp("G729", rtp_end->subtype_name)) + return AF_G729; +#endif + if (!strcmp("L16", rtp_end->subtype_name)) + return AF_L16; + } + + switch (rtp_end->payload_type) { + case 3 /* GSM */: + return AF_GSM; + case 8 /* PCMA */: + return AF_PCMA; +#ifdef HAVE_BCG729 + case 18 /* G.729 */: + return AF_G729; +#endif + case 11 /* L16 */: + return AF_L16; + default: + return AF_INVALID; + } +} + +static void l16_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) { + buf[0] = sample[0] >> 8; + buf[1] = sample[0] & 0xff; + } +} + +static void l16_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) + sample[0] = ((short)buf[0] << 8) | buf[1]; +} + +static void alaw_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n) + *(buf++) = s16_to_alaw(*(sample++)); +} + +static void alaw_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n) + *(sample++) = alaw_to_s16(*(buf++)); +} + +static int processing_state_destructor(struct mgcp_process_rtp_state *state) +{ + switch (state->src_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->src.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->src.g729_dec) + closeBcg729DecoderChannel(state->src.g729_dec); + break; +#endif + default: + break; + } + switch (state->dst_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->dst.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->dst.g729_enc) + closeBcg729EncoderChannel(state->dst.g729_enc); + break; +#endif + default: + break; + } + return 0; +} + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (state) { + talloc_free(state); + dst_end->rtp_process_data = NULL; + } + + if (!src_end) + return 0; + + src_fmt = get_audio_format(src_end); + dst_fmt = get_audio_format(dst_end); + + LOGP(DMGCP, LOGL_ERROR, + "Checking transcoding: %s (%d) -> %s (%d)\n", + src_end->subtype_name, src_end->payload_type, + dst_end->subtype_name, dst_end->payload_type); + + if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { + if (!src_end->subtype_name || !dst_end->subtype_name) + /* Not enough info, do nothing */ + return 0; + + if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) + /* Nothing to do */ + return 0; + + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: %s codec not supported (%s -> %s).\n", + src_fmt != AF_INVALID ? "destination" : "source", + src_end->audio_name, dst_end->audio_name); + return -EINVAL; + } + + if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: rate conversion (%d -> %d) not supported.\n", + src_end->rate, dst_end->rate); + return -EINVAL; + } + + state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); + talloc_set_destructor(state, processing_state_destructor); + dst_end->rtp_process_data = state; + + state->src_fmt = src_fmt; + + switch (state->src_fmt) { + case AF_L16: + case AF_S16: + state->src_frame_size = 80 * sizeof(short); + state->src_samples_per_frame = 80; + break; + case AF_GSM: + state->src_frame_size = sizeof(gsm_frame); + state->src_samples_per_frame = 160; + state->src.gsm_handle = gsm_create(); + if (!state->src.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM decoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->src_frame_size = 10; + state->src_samples_per_frame = 80; + state->src.g729_dec = initBcg729DecoderChannel(); + if (!state->src.g729_dec) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->src_frame_size = 80; + state->src_samples_per_frame = 80; + break; + default: + break; + } + + state->dst_fmt = dst_fmt; + + switch (state->dst_fmt) { + case AF_L16: + case AF_S16: + state->dst_frame_size = 80*sizeof(short); + state->dst_samples_per_frame = 80; + break; + case AF_GSM: + state->dst_frame_size = sizeof(gsm_frame); + state->dst_samples_per_frame = 160; + state->dst.gsm_handle = gsm_create(); + if (!state->dst.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM encoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->dst_frame_size = 10; + state->dst_samples_per_frame = 80; + state->dst.g729_enc = initBcg729EncoderChannel(); + if (!state->dst.g729_enc) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->dst_frame_size = 80; + state->dst_samples_per_frame = 80; + break; + default: + 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", + ENDPOINT_NUMBER(endp), + src_fmt, src_end->payload_type, src_end->rate, src_end->fmtp_extra, + dst_fmt, dst_end->payload_type, dst_end->rate, dst_end->fmtp_extra); + + return 0; +} + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; + if (!state || endp->net_end.payload_type < 0) { + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; + return; + } + + *payload_type = endp->net_end.payload_type; + *fmtp_extra = endp->net_end.fmtp_extra; + *audio_name = endp->net_end.audio_name; +} + +static int decode_audio(struct mgcp_process_rtp_state *state, + uint8_t **src, size_t *nbytes) +{ + 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", + 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, state->samples + state->sample_cnt) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to decode GSM.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); + break; +#endif + case AF_PCMA: + alaw_decode(*src, state->samples + state->sample_cnt, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(state->samples + state->sample_cnt, *src, + state->src_frame_size); + break; + case AF_L16: + 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; + 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 */ + while (nsamples + state->dst_samples_per_frame <= max_samples) { + if (nbytes + state->dst_frame_size > buf_size) { + 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; + } + switch (state->dst_fmt) { + case AF_GSM: + gsm_encode(state->dst.gsm_handle, + state->samples + state->sample_offs, dst); + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Encoder(state->dst.g729_enc, + state->samples + state->sample_offs, dst); + break; +#endif + case AF_PCMA: + alaw_encode(state->samples + state->sample_offs, dst, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(dst, state->samples + state->sample_offs, + state->dst_frame_size); + break; + case AF_L16: + l16_encode(state->samples + state->sample_offs, dst, + state->src_samples_per_frame); + break; + default: + break; + } + dst += state->dst_frame_size; + nbytes += state->dst_frame_size; + state->sample_offs += state->dst_samples_per_frame; + nsamples += state->dst_samples_per_frame; + } + state->sample_cnt -= nsamples; + return nbytes; +} + +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; + + 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; +} diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index a620e7a..da02380 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,16 +1,11 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ - $(LIBBCG729_CFLAGS) + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
bin_PROGRAMS = osmo-bsc_mgcp
osmo_bsc_mgcp_SOURCES = mgcp_main.c -if BUILD_MGCP_TRANSCODING - osmo_bsc_mgcp_SOURCES += mgcp_transcode.c -endif + osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) - -noinst_HEADERS = g711common.h mgcp_transcode.h diff --git a/openbsc/src/osmo-bsc_mgcp/g711common.h b/openbsc/src/osmo-bsc_mgcp/g711common.h deleted file mode 100644 index cb35fc6..0000000 --- a/openbsc/src/osmo-bsc_mgcp/g711common.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * PCM - A-Law conversion - * Copyright (c) 2000 by Abramo Bagnara abramo@alsa-project.org - * - * Wrapper for linphone Codec class by Simon Morlat simon.morlat@linphone.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 - */ - -static inline int val_seg(int val) -{ - int r = 0; - val >>= 7; /*7 = 4 + 3*/ - if (val & 0xf0) { - val >>= 4; - r += 4; - } - if (val & 0x0c) { - val >>= 2; - r += 2; - } - if (val & 0x02) - r += 1; - return r; -} - -/* - * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law - * - * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. - * - * Linear Input Code Compressed Code - * ------------------------ --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. - */ - -static inline unsigned char s16_to_alaw(int pcm_val) -{ - int mask; - int seg; - unsigned char aval; - - if (pcm_val >= 0) { - mask = 0xD5; - } else { - mask = 0x55; - pcm_val = -pcm_val; - if (pcm_val > 0x7fff) - pcm_val = 0x7fff; - } - - if (pcm_val < 256) /*256 = 32 << 3*/ - aval = pcm_val >> 4; /*4 = 1 + 3*/ - else { - /* Convert the scaled magnitude to segment number. */ - seg = val_seg(pcm_val); - aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); - } - return aval ^ mask; -} - -/* - * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM - * - */ -static inline int alaw_to_s16(unsigned char a_val) -{ - int t; - int seg; - - a_val ^= 0x55; - t = a_val & 0x7f; - if (t < 16) - t = (t << 4) + 8; - else { - seg = (t >> 4) & 0x07; - t = ((t & 0x0f) << 4) + 0x108; - t <<= seg -1; - } - return ((a_val & 0x80) ? t : -t); -} -/* - * s16_to_ulaw() - Convert a linear PCM value to u-law - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ - -static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char uval; - - if (pcm_val < 0) { - pcm_val = 0x84 - pcm_val; - mask = 0x7f; - } else { - pcm_val += 0x84; - mask = 0xff; - } - if (pcm_val > 0x7fff) - pcm_val = 0x7fff; - - /* Convert the scaled magnitude to segment number. */ - seg = val_seg(pcm_val); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); - return uval ^ mask; -} - -/* - * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -static inline int ulaw_to_s16(unsigned char u_val) -{ - int t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & 0x0f) << 3) + 0x84; - t <<= (u_val & 0x70) >> 4; - - return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); -} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 5ac4c26..8c3808a 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -31,11 +31,6 @@
#include <sys/socket.h>
-#include "g711common.h" -#include <gsm.h> -#include <bcg729/decoder.h> -#include <bcg729/encoder.h> - #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/mgcp.h> @@ -55,7 +50,7 @@ #include "../../bscconfig.h"
#ifdef BUILD_MGCP_TRANSCODING -#include "mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h" #endif
/* this is here for the vty... it will never be called */ diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c deleted file mode 100644 index edd3178..0000000 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -#include <stdlib.h> -#include <string.h> -#include <errno.h> - - -#include "../../bscconfig.h" - -#include "g711common.h" -#include <gsm.h> -#ifdef HAVE_BCG729 -#include <bcg729/decoder.h> -#include <bcg729/encoder.h> -#endif - -#include <openbsc/debug.h> -#include <openbsc/mgcp.h> -#include <openbsc/mgcp_internal.h> - -#include <osmocom/core/talloc.h> - -enum audio_format { - AF_INVALID, - AF_S16, - AF_L16, - AF_GSM, - AF_G729, - AF_PCMA -}; - -struct mgcp_process_rtp_state { - /* decoding */ - enum audio_format src_fmt; - union { - gsm gsm_handle; -#ifdef HAVE_BCG729 - bcg729DecoderChannelContextStruct *g729_dec; -#endif - } src; - size_t src_frame_size; - size_t src_samples_per_frame; - - /* processing */ - - /* encoding */ - enum audio_format dst_fmt; - union { - gsm gsm_handle; -#ifdef HAVE_BCG729 - bcg729EncoderChannelContextStruct *g729_enc; -#endif - } 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) -{ - struct mgcp_process_rtp_state *state = state_; - if (dst) - return (nsamples >= 0 ? - nsamples / state->dst_samples_per_frame : - 1) * state->dst_frame_size; - else - return (nsamples >= 0 ? - nsamples / state->src_samples_per_frame : - 1) * state->src_frame_size; -} - -static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) -{ - if (rtp_end->subtype_name) { - if (!strcmp("GSM", rtp_end->subtype_name)) - return AF_GSM; - if (!strcmp("PCMA", rtp_end->subtype_name)) - return AF_PCMA; -#ifdef HAVE_BCG729 - if (!strcmp("G729", rtp_end->subtype_name)) - return AF_G729; -#endif - if (!strcmp("L16", rtp_end->subtype_name)) - return AF_L16; - } - - switch (rtp_end->payload_type) { - case 3 /* GSM */: - return AF_GSM; - case 8 /* PCMA */: - return AF_PCMA; -#ifdef HAVE_BCG729 - case 18 /* G.729 */: - return AF_G729; -#endif - case 11 /* L16 */: - return AF_L16; - default: - return AF_INVALID; - } -} - -static void l16_encode(short *sample, unsigned char *buf, size_t n) -{ - for (; n > 0; --n, ++sample, buf += 2) { - buf[0] = sample[0] >> 8; - buf[1] = sample[0] & 0xff; - } -} - -static void l16_decode(unsigned char *buf, short *sample, size_t n) -{ - for (; n > 0; --n, ++sample, buf += 2) - sample[0] = ((short)buf[0] << 8) | buf[1]; -} - -static void alaw_encode(short *sample, unsigned char *buf, size_t n) -{ - for (; n > 0; --n) - *(buf++) = s16_to_alaw(*(sample++)); -} - -static void alaw_decode(unsigned char *buf, short *sample, size_t n) -{ - for (; n > 0; --n) - *(sample++) = alaw_to_s16(*(buf++)); -} - -static int processing_state_destructor(struct mgcp_process_rtp_state *state) -{ - switch (state->src_fmt) { - case AF_GSM: - if (state->dst.gsm_handle) - gsm_destroy(state->src.gsm_handle); - break; -#ifdef HAVE_BCG729 - case AF_G729: - if (state->src.g729_dec) - closeBcg729DecoderChannel(state->src.g729_dec); - break; -#endif - default: - break; - } - switch (state->dst_fmt) { - case AF_GSM: - if (state->dst.gsm_handle) - gsm_destroy(state->dst.gsm_handle); - break; -#ifdef HAVE_BCG729 - case AF_G729: - if (state->dst.g729_enc) - closeBcg729EncoderChannel(state->dst.g729_enc); - break; -#endif - default: - break; - } - return 0; -} - -int mgcp_transcoding_setup(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end) -{ - struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; - enum audio_format src_fmt, dst_fmt; - - /* cleanup first */ - if (state) { - talloc_free(state); - dst_end->rtp_process_data = NULL; - } - - if (!src_end) - return 0; - - src_fmt = get_audio_format(src_end); - dst_fmt = get_audio_format(dst_end); - - LOGP(DMGCP, LOGL_ERROR, - "Checking transcoding: %s (%d) -> %s (%d)\n", - src_end->subtype_name, src_end->payload_type, - dst_end->subtype_name, dst_end->payload_type); - - if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { - if (!src_end->subtype_name || !dst_end->subtype_name) - /* Not enough info, do nothing */ - return 0; - - if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) - /* Nothing to do */ - return 0; - - LOGP(DMGCP, LOGL_ERROR, - "Cannot transcode: %s codec not supported (%s -> %s).\n", - src_fmt != AF_INVALID ? "destination" : "source", - src_end->audio_name, dst_end->audio_name); - return -EINVAL; - } - - if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot transcode: rate conversion (%d -> %d) not supported.\n", - src_end->rate, dst_end->rate); - return -EINVAL; - } - - state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); - talloc_set_destructor(state, processing_state_destructor); - dst_end->rtp_process_data = state; - - state->src_fmt = src_fmt; - - switch (state->src_fmt) { - case AF_L16: - case AF_S16: - state->src_frame_size = 80 * sizeof(short); - state->src_samples_per_frame = 80; - break; - case AF_GSM: - state->src_frame_size = sizeof(gsm_frame); - state->src_samples_per_frame = 160; - state->src.gsm_handle = gsm_create(); - if (!state->src.gsm_handle) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize GSM decoder.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - state->src_frame_size = 10; - state->src_samples_per_frame = 80; - state->src.g729_dec = initBcg729DecoderChannel(); - if (!state->src.g729_dec) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize G.729 decoder.\n"); - return -EINVAL; - } - break; -#endif - case AF_PCMA: - state->src_frame_size = 80; - state->src_samples_per_frame = 80; - break; - default: - break; - } - - state->dst_fmt = dst_fmt; - - switch (state->dst_fmt) { - case AF_L16: - case AF_S16: - state->dst_frame_size = 80*sizeof(short); - state->dst_samples_per_frame = 80; - break; - case AF_GSM: - state->dst_frame_size = sizeof(gsm_frame); - state->dst_samples_per_frame = 160; - state->dst.gsm_handle = gsm_create(); - if (!state->dst.gsm_handle) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize GSM encoder.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - state->dst_frame_size = 10; - state->dst_samples_per_frame = 80; - state->dst.g729_enc = initBcg729EncoderChannel(); - if (!state->dst.g729_enc) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize G.729 decoder.\n"); - return -EINVAL; - } - break; -#endif - case AF_PCMA: - state->dst_frame_size = 80; - state->dst_samples_per_frame = 80; - break; - default: - 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", - ENDPOINT_NUMBER(endp), - src_fmt, src_end->payload_type, src_end->rate, src_end->fmtp_extra, - dst_fmt, dst_end->payload_type, dst_end->rate, dst_end->fmtp_extra); - - return 0; -} - -void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, - int *payload_type, - const char**audio_name, - const char**fmtp_extra) -{ - struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; - if (!state || endp->net_end.payload_type < 0) { - *payload_type = endp->bts_end.payload_type; - *audio_name = endp->bts_end.audio_name; - *fmtp_extra = endp->bts_end.fmtp_extra; - return; - } - - *payload_type = endp->net_end.payload_type; - *fmtp_extra = endp->net_end.fmtp_extra; - *audio_name = endp->net_end.audio_name; -} - -static int decode_audio(struct mgcp_process_rtp_state *state, - uint8_t **src, size_t *nbytes) -{ - 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", - 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, state->samples + state->sample_cnt) < 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to decode GSM.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); - break; -#endif - case AF_PCMA: - alaw_decode(*src, state->samples + state->sample_cnt, - state->src_samples_per_frame); - break; - case AF_S16: - memmove(state->samples + state->sample_cnt, *src, - state->src_frame_size); - break; - case AF_L16: - 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; - 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 */ - while (nsamples + state->dst_samples_per_frame <= max_samples) { - if (nbytes + state->dst_frame_size > buf_size) { - 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; - } - switch (state->dst_fmt) { - case AF_GSM: - gsm_encode(state->dst.gsm_handle, - state->samples + state->sample_offs, dst); - break; -#ifdef HAVE_BCG729 - case AF_G729: - bcg729Encoder(state->dst.g729_enc, - state->samples + state->sample_offs, dst); - break; -#endif - case AF_PCMA: - alaw_encode(state->samples + state->sample_offs, dst, - state->src_samples_per_frame); - break; - case AF_S16: - memmove(dst, state->samples + state->sample_offs, - state->dst_frame_size); - break; - case AF_L16: - l16_encode(state->samples + state->sample_offs, dst, - state->src_samples_per_frame); - break; - default: - break; - } - dst += state->dst_frame_size; - nbytes += state->dst_frame_size; - state->sample_offs += state->dst_samples_per_frame; - nsamples += state->dst_samples_per_frame; - } - state->sample_cnt -= nsamples; - return nbytes; -} - -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; - - 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; -} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h deleted file mode 100644 index 0961634..0000000 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ -#ifndef OPENBSC_MGCP_TRANSCODE_H -#define OPENBSC_MGCP_TRANSCODE_H - -int mgcp_transcoding_setup(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end); - -void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, - int *payload_type, - const char**audio_name, - const char**fmtp_extra); - -int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size); - -int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); -#endif /* OPENBSC_MGCP_TRANSCODE_H */ diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 81f28ae..2bc2da6 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -18,7 +18,7 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL)
-mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c $(top_builddir)/src/osmo-bsc_mgcp/mgcp_transcode.c +mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c
mgcp_transcoding_test_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c index a1af157..264dbe6 100644 --- a/openbsc/tests/mgcp/mgcp_transcoding_test.c +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -17,7 +17,7 @@ #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif
-#include "src/osmo-bsc_mgcp/mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h"
uint8_t *audio_frame_l16[] = { };
On Mon, May 12, 2014 at 12:39:06PM +0200, Jacob Erlbeck wrote:
This patch moves the files relevant to transcoding from src/osmo-bsc_mgcp to src/libmgcp and src/include/openbsc. Makefiles and include directives are being updated accordingly.
Can you try to create this patch with the -M option. It will re-direct renames. I assume the actual change is limited to Makefiles.
On 14.05.2014 07:49, Holger Hans Peter Freyther wrote:
On Mon, May 12, 2014 at 12:39:06PM +0200, Jacob Erlbeck wrote:
This patch moves the files relevant to transcoding from src/osmo-bsc_mgcp to src/libmgcp and src/include/openbsc. Makefiles and include directives are being updated accordingly.
Can you try to create this patch with the -M option. It will re-direct renames. I assume the actual change is limited to Makefiles.
No, since the header file has moved also, some include directives have to be adjusted, too.
Currently, if there is no SDP data in the MGCP message received from the net, the fields containing audio encoding information are not set in net_end. So in recvonly mode transcoding would not be set up correctly.
This patch changes the implementation of the code handling CRCX and MDCX to use the codec signalled in the MGCP local connection options (field 'a:') if there isn't any SDP data. This is only halfway negotiation, because the codec is used blindly and not matched against the supported ones.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 1 + openbsc/src/libmgcp/mgcp_protocol.c | 35 ++++++++++++++++++++++++++++--- openbsc/tests/mgcp/mgcp_test.c | 7 +++++++ openbsc/tests/mgcp/mgcp_test.ok | 5 +++++ 4 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index c7bc2a8..e877026 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -124,6 +124,7 @@ struct mgcp_rtp_tap {
struct mgcp_lco { char *string; + char *codec; int pkt_period_min; /* time in ms */ int pkt_period_max; /* time in ms */ }; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index f26587e..104ad00 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -582,7 +582,8 @@ static int set_audio_info(struct mgcp_rtp_end *rtp, talloc_free(rtp->audio_name); rtp->audio_name = NULL;
- rtp->payload_type = payload_type; + if (payload_type >= 0) + rtp->payload_type = payload_type;
if (!audio_name) { switch (payload_type) { @@ -597,7 +598,7 @@ static int set_audio_info(struct mgcp_rtp_end *rtp, }
if (sscanf(audio_name, "%63[^/]/%d/%d", - audio_codec, &rate, &channels) < 2) + audio_codec, &rate, &channels) < 1) return -EINVAL;
rtp->rate = rate; @@ -610,6 +611,20 @@ static int set_audio_info(struct mgcp_rtp_end *rtp, rtp->frame_duration_den = 1000; }
+ if (payload_type < 0) { + payload_type = 96; + if (rate == 8000 && channels == 1) { + if (!strcmp(audio_codec, "GSM")) + payload_type = 3; + else if (!strcmp(audio_codec, "PCMA")) + payload_type = 8; + else if (!strcmp(audio_codec, "G729")) + payload_type = 18; + } + + rtp->payload_type = payload_type; + } + if (channels != 1) LOGP(DMGCP, LOGL_NOTICE, "Channels != 1 in SDP: '%s'\n", audio_name); @@ -781,9 +796,12 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, const char *options) { - char *p_opt; + char *p_opt, *a_opt; + char codec[9];
talloc_free(lco->string); + talloc_free(lco->codec); + lco->codec = NULL; lco->pkt_period_min = lco->pkt_period_max = 0; lco->string = talloc_strdup(ctx, options ? options : "");
@@ -791,6 +809,10 @@ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, if (p_opt && sscanf(p_opt, "p:%d-%d", &lco->pkt_period_min, &lco->pkt_period_max) == 1) lco->pkt_period_max = lco->pkt_period_min; + + a_opt = strstr(lco->string, "a:"); + if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) + lco->codec = talloc_strdup(ctx, codec); }
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, @@ -924,6 +946,8 @@ mgcp_header_done: tcfg->audio_fmtp_extra); if (have_sdp) parse_sdp_data(&endp->net_end, p); + else if (endp->local_options.codec) + set_audio_info(&endp->net_end, -1, endp->local_options.codec);
if (p->cfg->bts_force_ptime) { endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; @@ -977,6 +1001,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) struct mgcp_endpoint *endp = p->endp; int error_code = 500; int silent = 0; + int have_sdp = 0; char *line; const char *local_options = NULL;
@@ -1016,6 +1041,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; case '\0': /* SDP file begins */ + have_sdp = 1; parse_sdp_data(&endp->net_end, p); /* This will exhaust p->save, so the loop will * terminate next time. @@ -1031,6 +1057,9 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options);
+ if (!have_sdp && endp->local_options.codec) + set_audio_info(&endp->net_end, -1, endp->local_options.codec); + setup_rtp_processing(endp);
/* policy CB */ diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 498a1d8..0552c72 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -168,6 +168,12 @@ static void test_strline(void) "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n"
+#define MDCX4_RO "MDCX 18983221 1@mgw MGCP 1.0\r\n" \ + "M: recvonly\r" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20, a:AMR, nt:IN\r\n" + #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1@mgw" @@ -257,6 +263,7 @@ static const struct mgcp_test tests[] = { { "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 }, { "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 }, { "MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99, 126 }, + { "MDCX4_RO", MDCX4_RO, MDCX4_RET("18983221"), PTYPE_IGNORE, 126 }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 }, { "EMPTY", EMPTY, EMPTY_RET }, diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3901cfb..7301a81 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -49,6 +49,11 @@ Testing MDCX4_SO Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 2: SEND +Testing MDCX4_RO +Dummy packets: 1 +Packet duration not set +Requested packetetization period: 20-20 +Connection mode: 1: RECV Testing DLCX Detected packet duration: 20 Requested packetization period not set
This patch adds the callbacks rtp_processing_cb and setup_rtp_processing_cb to mgcp_config to support arbitrary RTP payload processing.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 10 +++++++++ openbsc/include/openbsc/mgcp_internal.h | 8 ++++++++ openbsc/src/libmgcp/mgcp_network.c | 20 +++++++++++++++--- openbsc/src/libmgcp/mgcp_protocol.c | 34 ++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 1d74078..6ba0769 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -65,6 +65,7 @@ static inline int rtp_calculate_port(int multiplex, int base) struct mgcp_endpoint; struct mgcp_config; struct mgcp_trunk_config; +struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1 #define MGCP_ENDP_DLCX 2 @@ -86,6 +87,11 @@ 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, + char *data, int *len, int buf_size); +typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); #define PORT_ALLOC_STATIC 0 #define PORT_ALLOC_DYNAMIC 1
@@ -156,6 +162,10 @@ struct mgcp_config { struct in_addr transcoder_in; int transcoder_remote_base;
+ /* RTP processing */ + mgcp_processing rtp_processing_cb; + mgcp_processing_setup setup_rtp_processing_cb; + struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 9b97165..56c280d 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -91,6 +91,7 @@ struct mgcp_rtp_end { /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ int force_aligned_timing; + void *rtp_process_data;
/* * Each end has a socket... @@ -197,5 +198,12 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); 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, + char *data, int *len, int buf_size); + +int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end);
#endif diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8a5656a..4d1ad35 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -77,6 +77,7 @@ struct rtp_hdr { #define RTP_SEQ_MOD (1 << 16) #define RTP_MAX_DROPOUT 3000 #define RTP_MAX_MISORDER 100 +#define RTP_BUF_SIZE 4096
enum { @@ -347,6 +348,18 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, return timestamp_error; }
+int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size) +{ + return 0; +} + +int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + return 0; +}
/** * The RFC 3550 Appendix A assumes there are multiple sources but @@ -597,6 +610,7 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, 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, @@ -635,7 +649,7 @@ static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *
static int rtp_data_net(struct osmo_fd *fd, unsigned int what) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; @@ -719,7 +733,7 @@ static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_
static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; @@ -781,7 +795,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, int dest, struct osmo_fd *fd) { - char buf[4096]; + char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_config *cfg; int rc, proto; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 5c88c9d..0f8614c 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -107,6 +107,8 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *data); static void create_transcoder(struct mgcp_endpoint *endp); static void delete_transcoder(struct mgcp_endpoint *endp);
+static void setup_rtp_processing(struct mgcp_endpoint *endp); + static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
static uint32_t generate_call_id(struct mgcp_config *cfg) @@ -827,8 +829,10 @@ mgcp_header_done: endp->bts_end.payload_type = tcfg->audio_payload; 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); + }
/* policy CB */ if (p->cfg->policy_cb) { @@ -929,6 +933,8 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options);
+ setup_rtp_processing(endp); + /* policy CB */ if (p->cfg->policy_cb) { int rc; @@ -1169,6 +1175,9 @@ struct mgcp_config *mgcp_config_alloc(void) cfg->bts_ports.base_port = RTP_PORT_DEFAULT; cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
+ cfg->rtp_processing_cb = &mgcp_rtp_processing_default; + cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default; + /* default trunk handling */ cfg->trunk.cfg = cfg; cfg->trunk.trunk_nr = 0; @@ -1234,6 +1243,8 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) end->local_alloc = -1; talloc_free(end->fmtp_extra); end->fmtp_extra = NULL; + talloc_free(end->rtp_process_data); + end->rtp_process_data = NULL;
/* Set default values */ end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; @@ -1388,6 +1399,27 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) return send_agent(endp->cfg, buf, len); }
+static void setup_rtp_processing(struct mgcp_endpoint *endp) +{ + struct mgcp_config *cfg = endp->cfg; + + if (endp->type != MGCP_RTP_DEFAULT) + return; + + if (endp->conn_mode == MGCP_CONN_LOOPBACK) + return; + + if (endp->conn_mode & MGCP_CONN_SEND_ONLY) + cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); + else + cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL); + + if (endp->conn_mode & MGCP_CONN_RECV_ONLY) + cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); + else + cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL); +} + static void create_transcoder(struct mgcp_endpoint *endp) { int port;
This patch adds the fields channels, subtype_name, and audio_name to the struct. The field audio_name contains the full string that has been used for the last part of a SDP a=rtpmap line. The others contain decoded parts of that string. If no a=rtpmap line has been given (e.g. because dynamic payload types are not used), values are assigned when the payload type matches one of the predefined ones (GSM, G729, PCMA).
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 3 ++ openbsc/src/libmgcp/mgcp_protocol.c | 82 +++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 21 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 56c280d..72ac8e9 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -81,11 +81,14 @@ struct mgcp_rtp_end { /* per endpoint data */ int payload_type; uint32_t rate; + int channels; uint32_t frame_duration_num; uint32_t frame_duration_den; int frames_per_packet; uint32_t packet_duration_ms; char *fmtp_extra; + char *audio_name; + char *subtype_name; int output_enabled;
/* RTP patching */ diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 0f8614c..3960c88 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -28,6 +28,7 @@ #include <time.h> #include <limits.h> #include <unistd.h> +#include <errno.h>
#include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> @@ -77,6 +78,7 @@ char *strline_r(char *str, char **saveptr) #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 +#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
@@ -231,6 +233,8 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, { const char *addr = endp->cfg->local_ip; const char *fmtp_extra = endp->bts_end.fmtp_extra; + const char *audio_name = endp->bts_end.audio_name; + int payload_type = endp->bts_end.payload_type; char sdp_record[4096]; int len;
@@ -244,11 +248,12 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n" + "a=rtpmap:%d%s%s\r\n" "%s%s", endp->ci, endp->ci, addr, addr, - endp->net_end.local_port, endp->bts_end.payload_type, - endp->bts_end.payload_type, endp->tcfg->audio_name, + endp->net_end.local_port, payload_type, + payload_type, + audio_name ? " " : "", audio_name ? audio_name : "", fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : "");
if (len < 0 || len >= sizeof(sdp_record)) @@ -514,6 +519,47 @@ static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp) return ret; }
+static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, + int payload_type, const char *audio_name) +{ + int rate = 8000; + int channels = 1; + char audio_codec[64]; + + talloc_free(rtp->subtype_name); + rtp->subtype_name = NULL; + talloc_free(rtp->audio_name); + rtp->audio_name = NULL; + + rtp->payload_type = payload_type; + + if (!audio_name) { + switch (payload_type) { + case 3: audio_name = "GSM/8000/1"; break; + case 8: audio_name = "PCMA/8000/1"; break; + case 18: audio_name = "G729/8000/1"; break; + default: + rtp->rate = 8000; + rtp->channels = 1; + return 0; + } + } + + if (sscanf(audio_name, "%63[^/]/%d/%d", + audio_codec, &rate, &channels) < 2) + return -EINVAL; + + rtp->rate = rate; + rtp->channels = channels; + rtp->subtype_name = talloc_strdup(ctx, audio_codec); + rtp->audio_name = talloc_strdup(ctx, audio_name); + if (channels != 1) + LOGP(DMGCP, LOGL_NOTICE, + "Channels != 1 in SDP: '%s'\n", audio_name); + + return 0; +} + static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, struct mgcp_port_range *range, int (*alloc)(struct mgcp_endpoint *endp, int port)) @@ -600,29 +646,18 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) break; case 'a': { int payload; - int rate; - int channels = 1; int ptime, ptime2 = 0; char audio_name[64]; - char audio_codec[64];
if (audio_payload == -1) break;
- if (sscanf(line, "a=rtpmap:%d %64s", + if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { if (payload != audio_payload) break;
- if (sscanf(audio_name, "%[^/]/%d/%d", - audio_codec, &rate, &channels) < 2) - break; - - rtp->rate = rate; - if (channels != 1) - LOGP(DMGCP, LOGL_NOTICE, - "Channels != 1 in SDP: '%s' on 0x%x\n", - line, ENDPOINT_NUMBER(p->endp)); + set_audio_info(p->cfg, rtp, payload, audio_name); } else if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) { if (ptime2 > 0 && ptime2 != ptime) @@ -645,8 +680,8 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) &port, &audio_payload) == 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); - rtp->payload_type = audio_payload; found_media = 1; + set_audio_info(p->cfg, rtp, audio_payload, NULL); } break; } @@ -826,7 +861,7 @@ mgcp_header_done: endp->allocated = 1;
/* set up RTP media parameters */ - endp->bts_end.payload_type = tcfg->audio_payload; + 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) { @@ -1252,6 +1287,7 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) end->frames_per_packet = 0; /* unknown */ end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; + end->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; end->output_enabled = 0; }
@@ -1329,6 +1365,9 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; + const char *fmtp_extra = endp->bts_end.fmtp_extra; + const char *audio_name = endp->bts_end.audio_name; + int payload_type = endp->bts_end.payload_type;
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf), @@ -1338,10 +1377,11 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, "\r\n" "c=IN IP4 %s\r\n" "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", + "a=rtpmap:%d%s%s\r\n", msg, endpoint, mode, endp->cfg->source_addr, - port, endp->tcfg->audio_payload, - endp->tcfg->audio_payload, endp->tcfg->audio_name); + port, payload_type, + payload_type, + audio_name ? " " : "", audio_name ? audio_name : "");
if (len < 0) return;
On Thu, May 15, 2014 at 10:29:10AM +0200, Jacob Erlbeck wrote:
if (sscanf(line, "a=rtpmap:%d %64s",
if (sscanf(line, "a=rtpmap:%d %63s",
cppcheck is warning about the usage of 64 here. It warns about some of the other scanf as well. Please have a look at it by running cppcheck.
On Thu, May 15, 2014 at 10:29:10AM +0200, Jacob Erlbeck wrote:
Hi,
+static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp,
int payload_type, const char *audio_name)+{
...
- talloc_free(rtp->subtype_name);
- rtp->subtype_name = NULL;
- talloc_free(rtp->audio_name);
- rtp->audio_name = NULL;
...
@@ -600,29 +646,18 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
...
set_audio_info(p->cfg, rtp, payload, audio_name); } else if (sscanf(line, "a=ptime:%d-%d",
...
set_audio_info(p->cfg, rtp, audio_payload, NULL);
- endp->bts_end.payload_type = tcfg->audio_payload;
- set_audio_info(p->cfg, &endp->bts_end, tcfg->audio_payload, tcfg->audio_name);
I raised this before as well. So for a
CRCX MDCX DLCX
procedure. We might have endp->bts_end->subtype_name still with an allocated string. Please make reset endpoint give up all the data.
On 15.05.2014 21:34, Holger Hans Peter Freyther wrote:
I raised this before as well.
Yes you did, and I implemented it accordingly. But I apparently lost it somewhere in rebase hell :-( Thanks for spotting again.
So for a
CRCX MDCX DLCX
procedure. We might have endp->bts_end->subtype_name still with an allocated string. Please make reset endpoint give up all the data.
This patch adds the get_net_downlink_format_cb() callback to provide payload_type, subtype_name, and fmtp_extra suitable for use in a MGCP response sent to the network. Per default, the BTS side values are returned since these must be honoured by the net peer when sending audio to the media gateway (unless transcoding is done).
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 8 ++++++++ openbsc/include/openbsc/mgcp_internal.h | 5 +++++ openbsc/src/libmgcp/mgcp_network.c | 13 +++++++++++++ openbsc/src/libmgcp/mgcp_protocol.c | 20 ++++++++++++++------ 4 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 6ba0769..b595aba 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -92,6 +92,12 @@ typedef int (*mgcp_processing)(struct mgcp_rtp_end *dst_end, typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end); + +typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, + int *payload_type, + const char**subtype_name, + const char**fmtp_extra); + #define PORT_ALLOC_STATIC 0 #define PORT_ALLOC_DYNAMIC 1
@@ -166,6 +172,8 @@ struct mgcp_config { mgcp_processing rtp_processing_cb; mgcp_processing_setup setup_rtp_processing_cb;
+ mgcp_get_format get_net_downlink_format_cb; + struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 72ac8e9..e74b9fa 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -209,4 +209,9 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end);
+void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, + int *payload_type, + const char**subtype_name, + const char**fmtp_extra); + #endif diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 4d1ad35..dcbb97a 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -361,6 +361,19 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, return 0; }
+void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + /* Use the BTS side parameters when passing the SDP data (for + * downlink) to the net peer. + */ + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; +} + /** * The RFC 3550 Appendix A assumes there are multiple sources but * some of the supported endpoints (e.g. the nanoBTS) can only handle diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 3960c88..215c2ee 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -232,12 +232,15 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id) { const char *addr = endp->cfg->local_ip; - const char *fmtp_extra = endp->bts_end.fmtp_extra; - const char *audio_name = endp->bts_end.audio_name; - int payload_type = endp->bts_end.payload_type; + const char *fmtp_extra; + const char *audio_name; + int payload_type; char sdp_record[4096]; int len;
+ endp->cfg->get_net_downlink_format_cb(endp, &payload_type, + &audio_name, &fmtp_extra); + if (!addr) addr = endp->cfg->source_addr;
@@ -1213,6 +1216,8 @@ struct mgcp_config *mgcp_config_alloc(void) cfg->rtp_processing_cb = &mgcp_rtp_processing_default; cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
+ cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default; + /* default trunk handling */ cfg->trunk.cfg = cfg; cfg->trunk.trunk_nr = 0; @@ -1365,9 +1370,12 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; - const char *fmtp_extra = endp->bts_end.fmtp_extra; - const char *audio_name = endp->bts_end.audio_name; - int payload_type = endp->bts_end.payload_type; + const char *fmtp_extra; + const char *audio_name; + int payload_type; + + endp->cfg->get_net_downlink_format_cb(endp, &payload_type, + &audio_name, &fmtp_extra);
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf),
Don't show media related lines if the payload type has not been set. Don't show a 'a=rtpmap' line if the audio_name has not been set.
This patch unifies the SDP generation of create_response_with_sdp() and send_msg().
Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_protocol.c | 129 +++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 42 deletions(-)
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 215c2ee..f0d94c8 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -228,55 +228,103 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp, return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); }
-static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, - const char *msg, const char *trans_id) +static int write_response_sdp(struct mgcp_endpoint *endp, + char *sdp_record, size_t size, const char *addr) { - const char *addr = endp->cfg->local_ip; const char *fmtp_extra; const char *audio_name; int payload_type; - char sdp_record[4096]; int len; + int nchars;
endp->cfg->get_net_downlink_format_cb(endp, &payload_type, &audio_name, &fmtp_extra);
- if (!addr) - addr = endp->cfg->source_addr; - - len = snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %u\n\n" + len = snprintf(sdp_record, size, "v=0\r\n" "o=- %u 23 IN IP4 %s\r\n" "c=IN IP4 %s\r\n" - "t=0 0\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d%s%s\r\n" - "%s%s", - endp->ci, endp->ci, addr, addr, - endp->net_end.local_port, payload_type, - payload_type, - audio_name ? " " : "", audio_name ? audio_name : "", - fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); - - if (len < 0 || len >= sizeof(sdp_record)) + "t=0 0\r\n", + endp->ci, addr, addr); + + if (len < 0 || len >= size) goto buffer_too_small;
+ if (payload_type >= 0) { + nchars = snprintf(sdp_record + len, size - len, + "m=audio %d RTP/AVP %d\r\n", + endp->net_end.local_port, payload_type); + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + + if (audio_name) { + nchars = snprintf(sdp_record + len, size - len, + "a=rtpmap:%d %s\r\n", + payload_type, audio_name); + + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + } + + if (fmtp_extra) { + nchars = snprintf(sdp_record + len, size - len, + "a=rtpmap:%d %s\r\n", + payload_type, audio_name); + + if (nchars < 0 || nchars >= size - len) + goto buffer_too_small; + + len += nchars; + } + } if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { - int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len, - "a=ptime:%d\r\n", - endp->bts_end.packet_duration_ms); - if (nchars < 0 || nchars >= sizeof(sdp_record) - len) + nchars = snprintf(sdp_record + len, size - len, + "a=ptime:%d\r\n", + endp->bts_end.packet_duration_ms); + if (nchars < 0 || nchars >= size - len) goto buffer_too_small;
len += nchars; } - return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); + + return len;
buffer_too_small: LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n", - sizeof(sdp_record), len); - return NULL; + size, len); + return -1; +} + +static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, + const char *msg, const char *trans_id) +{ + const char *addr = endp->cfg->local_ip; + char sdp_record[4096]; + int len; + int nchars; + + if (!addr) + addr = endp->cfg->source_addr; + + len = snprintf(sdp_record, sizeof(sdp_record), "I: %u\n\n", endp->ci); + + if (len < 0) + return NULL; + + nchars = write_response_sdp(endp, sdp_record + len, + sizeof(sdp_record) - len - 1, addr); + if (nchars < 0) + return NULL; + + len += nchars; + + sdp_record[sizeof(sdp_record) - 1] = '\0'; + + return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); }
/* @@ -711,9 +759,10 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
if (found_media) LOGP(DMGCP, LOGL_NOTICE, - "Got media info via SDP: port %d, payload %d, " + "Got media info via SDP: port %d, payload %d (%s), " "duration %d, addr %s\n", ntohs(rtp->rtp_port), rtp->payload_type, + rtp->subtype_name ? rtp->subtype_name : "unknown", rtp->packet_duration_ms, inet_ntoa(rtp->addr));
return found_media; @@ -1370,30 +1419,26 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; - const char *fmtp_extra; - const char *audio_name; - int payload_type; - - endp->cfg->get_net_downlink_format_cb(endp, &payload_type, - &audio_name, &fmtp_extra); + int nchars;
/* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf), "%s 42 %x@mgw MGCP 1.0\r\n" "C: 4256\r\n" "M: %s\r\n" - "\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d%s%s\r\n", - msg, endpoint, mode, endp->cfg->source_addr, - port, payload_type, - payload_type, - audio_name ? " " : "", audio_name ? audio_name : ""); + "\r\n", + msg, endpoint, mode);
if (len < 0) return;
+ nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, + endp->cfg->source_addr); + if (nchars < 0) + return; + + len += nchars; + buf[sizeof(buf) - 1] = '\0';
send_trans(endp->cfg, buf, len);
This patch implements audio transcoding between the formats GSM, PCMA, L16, and optionally G.729.
The feature needs to be enabled by using the autoconf option '--enable-mgcp-transcoding'. In this case mgcp_transcode.c will be compiled and linked to osmo-bsc_mgcp, and the transcoding functions provided will be registered as processing callbacks.
If G.729 support is required, libcg729 needs to be installed and '--with-g729' must be passed to ./configure.
Ticket: OW#1111 Sponsored-by: On-Waves ehf --- openbsc/configure.ac | 15 + openbsc/src/osmo-bsc_mgcp/Makefile.am | 10 +- openbsc/src/osmo-bsc_mgcp/g711common.h | 187 ++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 10 + openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 452 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 34 +++ 6 files changed, 706 insertions(+), 2 deletions(-) create mode 100644 openbsc/src/osmo-bsc_mgcp/g711common.h create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h
diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 978f526..ead05af 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -56,6 +56,21 @@ fi AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") AC_SUBST(osmo_ac_build_smpp)
+# Enable/disable transcoding within osmo-bsc_mgcp? +AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], + [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) +AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) + +if test "$osmo_ac_mgcp_transcoding" = "yes" ; then + AC_SEARCH_LIBS(gsm_create, gsm) + if test "$osmo_ac_with_g729" = "yes" ; then + PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) + fi + AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) +fi +AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") +AC_SUBST(osmo_ac_mgcp_transcoding) +
found_libgtp=yes PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no) diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index 0456cf1..a620e7a 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,10 +1,16 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBBCG729_CFLAGS)
bin_PROGRAMS = osmo-bsc_mgcp
osmo_bsc_mgcp_SOURCES = mgcp_main.c +if BUILD_MGCP_TRANSCODING + osmo_bsc_mgcp_SOURCES += mgcp_transcode.c +endif osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ - $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) + $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) + +noinst_HEADERS = g711common.h mgcp_transcode.h diff --git a/openbsc/src/osmo-bsc_mgcp/g711common.h b/openbsc/src/osmo-bsc_mgcp/g711common.h new file mode 100644 index 0000000..cb35fc6 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/g711common.h @@ -0,0 +1,187 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara abramo@alsa-project.org + * + * Wrapper for linphone Codec class by Simon Morlat simon.morlat@linphone.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 + */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; /*7 = 4 + 3*/ + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. + */ + +static inline unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) /*256 = 32 << 3*/ + aval = pcm_val >> 4; /*4 = 1 + 3*/ + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static inline int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = 0x84 - pcm_val; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static inline int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 14ec221..6b72965 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -49,6 +49,10 @@
#include "../../bscconfig.h"
+#ifdef BUILD_MGCP_TRANSCODING +#include "mgcp_transcode.h" +#endif + /* this is here for the vty... it will never be called */ void subscr_put() { abort(); }
@@ -207,6 +211,12 @@ int main(int argc, char **argv) if (!cfg) return -1;
+#ifdef BUILD_MGCP_TRANSCODING + cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; + cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; + cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; +#endif + vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(&log_info); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c new file mode 100644 index 0000000..7247c88 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -0,0 +1,452 @@ +/* + * (C) 2014 by Sysmocom s.f.m.c. GmbH + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "bscconfig.h" + +#include "g711common.h" +#include <gsm.h> +#ifdef HAVE_BCG729 +#include <bcg729/decoder.h> +#include <bcg729/encoder.h> +#endif + +#include <openbsc/debug.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include <osmocom/core/talloc.h> + +enum audio_format { + AF_INVALID, + AF_S16, + AF_L16, + AF_GSM, + AF_G729, + AF_PCMA +}; + +struct mgcp_process_rtp_state { + /* decoding */ + enum audio_format src_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729DecoderChannelContextStruct *g729_dec; +#endif + } src; + size_t src_frame_size; + size_t src_samples_per_frame; + + /* processing */ + + /* encoding */ + enum audio_format dst_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729EncoderChannelContextStruct *g729_enc; +#endif + } dst; + size_t dst_frame_size; + size_t dst_samples_per_frame; +}; + +static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) +{ + if (rtp_end->subtype_name) { + if (!strcmp("GSM", rtp_end->subtype_name)) + return AF_GSM; + if (!strcmp("PCMA", rtp_end->subtype_name)) + return AF_PCMA; +#ifdef HAVE_BCG729 + if (!strcmp("G729", rtp_end->subtype_name)) + return AF_G729; +#endif + if (!strcmp("L16", rtp_end->subtype_name)) + return AF_L16; + } + + switch (rtp_end->payload_type) { + case 3 /* GSM */: + return AF_GSM; + case 8 /* PCMA */: + return AF_PCMA; +#ifdef HAVE_BCG729 + case 18 /* G.729 */: + return AF_G729; +#endif + case 11 /* L16 */: + return AF_L16; + default: + return AF_INVALID; + } +} + +static void l16_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) { + buf[0] = sample[0] >> 8; + buf[1] = sample[0] & 0xff; + } +} + +static void l16_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) + sample[0] = ((short)buf[0] << 8) | buf[1]; +} + +static void alaw_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n) + *(buf++) = s16_to_alaw(*(sample++)); +} + +static void alaw_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n) + *(sample++) = alaw_to_s16(*(buf++)); +} + +static int processing_state_destructor(struct mgcp_process_rtp_state *state) +{ + switch (state->src_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->src.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->src.g729_dec) + closeBcg729DecoderChannel(state->src.g729_dec); + break; +#endif + default: + break; + } + switch (state->dst_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->dst.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->dst.g729_enc) + closeBcg729EncoderChannel(state->dst.g729_enc); + break; +#endif + default: + break; + } + return 0; +} + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (state) { + talloc_free(state); + dst_end->rtp_process_data = NULL; + } + + if (!src_end) + return 0; + + src_fmt = get_audio_format(src_end); + dst_fmt = get_audio_format(dst_end); + + LOGP(DMGCP, LOGL_ERROR, + "Checking transcoding: %s (%d) -> %s (%d)\n", + src_end->subtype_name, src_end->payload_type, + dst_end->subtype_name, dst_end->payload_type); + + if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { + if (!src_end->subtype_name || !dst_end->subtype_name) + /* Not enough info, do nothing */ + return 0; + + if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) + /* Nothing to do */ + return 0; + + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: %s codec not supported (%s -> %s).\n", + src_fmt != AF_INVALID ? "destination" : "source", + src_end->audio_name, dst_end->audio_name); + return -EINVAL; + } + + if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: rate conversion (%d -> %d) not supported.\n", + src_end->rate, dst_end->rate); + return -EINVAL; + } + + state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); + talloc_set_destructor(state, processing_state_destructor); + dst_end->rtp_process_data = state; + + state->src_fmt = src_fmt; + + switch (state->src_fmt) { + case AF_L16: + case AF_S16: + state->src_frame_size = 80 * sizeof(short); + state->src_samples_per_frame = 80; + break; + case AF_GSM: + state->src_frame_size = sizeof(gsm_frame); + state->src_samples_per_frame = 160; + state->src.gsm_handle = gsm_create(); + if (!state->src.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM decoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->src_frame_size = 10; + state->src_samples_per_frame = 80; + state->src.g729_dec = initBcg729DecoderChannel(); + if (!state->src.g729_dec) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->src_frame_size = 80; + state->src_samples_per_frame = 80; + break; + default: + break; + } + + state->dst_fmt = dst_fmt; + + switch (state->dst_fmt) { + case AF_L16: + case AF_S16: + state->dst_frame_size = 80*sizeof(short); + state->dst_samples_per_frame = 80; + break; + case AF_GSM: + state->dst_frame_size = sizeof(gsm_frame); + state->dst_samples_per_frame = 160; + state->dst.gsm_handle = gsm_create(); + if (!state->dst.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM encoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->dst_frame_size = 10; + state->dst_samples_per_frame = 80; + state->dst.g729_enc = initBcg729EncoderChannel(); + if (!state->dst.g729_enc) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->dst_frame_size = 80; + state->dst_samples_per_frame = 80; + break; + default: + break; + } + + LOGP(DMGCP, LOGL_INFO, + "Initialized RTP processing on: 0x%x " + "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", + ENDPOINT_NUMBER(endp), + src_fmt, src_end->payload_type, src_end->rate, src_end->fmtp_extra, + dst_fmt, dst_end->payload_type, dst_end->rate, dst_end->fmtp_extra); + + return 0; +} + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; + if (!state || endp->net_end.payload_type < 0) { + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; + return; + } + + *payload_type = endp->net_end.payload_type; + *fmtp_extra = endp->net_end.fmtp_extra; + *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) +{ + 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)) { + LOGP(DMGCP, LOGL_ERROR, + "Sample buffer too small: %d > %d.\n", + sample_cnt + state->src_samples_per_frame, + ARRAY_SIZE(samples)); + return -ENOSPC; + } + switch (state->src_fmt) { + case AF_GSM: + if (gsm_decode(state->src.gsm_handle, + (gsm_byte *)src, samples + sample_cnt) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to decode GSM.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt); + break; +#endif + case AF_PCMA: + alaw_decode(src, samples + sample_cnt, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(samples + sample_cnt, src, + state->src_frame_size); + break; + case AF_L16: + l16_decode(src, samples + 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; + } + } + + /* Encode samples into dst */ + sample_idx = 0; + nbytes = 0; + while (sample_idx + state->dst_samples_per_frame <= sample_cnt) { + if (nbytes + state->dst_frame_size > buf_size) { + LOGP(DMGCP, LOGL_ERROR, + "Encoding (RTP) buffer too small: %d > %d.\n", + nbytes + state->dst_frame_size, buf_size); + return -ENOSPC; + } + switch (state->dst_fmt) { + case AF_GSM: + gsm_encode(state->dst.gsm_handle, + samples + sample_idx, dst); + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Encoder(state->dst.g729_enc, + samples + sample_idx, dst); + break; +#endif + case AF_PCMA: + alaw_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(dst, samples + sample_idx, state->dst_frame_size); + break; + case AF_L16: + l16_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + default: + break; + } + dst += state->dst_frame_size; + nbytes += state->dst_frame_size; + sample_idx += state->dst_samples_per_frame; + } + + *len = rtp_hdr_size + nbytes; + /* Patch payload type */ + data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f); + + return 0; +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h new file mode 100644 index 0000000..2dfb06a --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h @@ -0,0 +1,34 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ +#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra); + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size); +#endif /* OPENBSC_MGCP_TRANSCODE_H */
This tool uses mgcp_transcode.c to convert audio data from stdin to stdout.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/Makefile | 17 ++++++ openbsc/contrib/testconv/testconv_main.c | 91 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 13 ++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 2 + 4 files changed, 123 insertions(+) create mode 100644 openbsc/contrib/testconv/Makefile create mode 100644 openbsc/contrib/testconv/testconv_main.c
diff --git a/openbsc/contrib/testconv/Makefile b/openbsc/contrib/testconv/Makefile new file mode 100644 index 0000000..90adecc --- /dev/null +++ b/openbsc/contrib/testconv/Makefile @@ -0,0 +1,17 @@ + +OBJS = testconv_main.o mgcp_transcode.o + +CC = gcc +CFLAGS = -O0 -ggdb -Wall +LDFLAGS = +CPPFLAGS = -I../.. -I../../include $(shell pkg-config --cflags libosmocore) $(shell pkg-config --cflags libbcg729) +LIBS = ../../src/libmgcp/libmgcp.a ../../src/libcommon/libcommon.a $(shell pkg-config --libs libosmocore) $(shell pkg-config --libs libbcg729) -lgsm -lrt + +testconv: $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +testconv_main.o: testconv_main.c +mgcp_transcode.o: ../../src/osmo-bsc_mgcp/mgcp_transcode.c + +$(OBJS): + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c new file mode 100644 index 0000000..c2785f2 --- /dev/null +++ b/openbsc/contrib/testconv/testconv_main.c @@ -0,0 +1,91 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/application.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include "bscconfig.h" +#ifndef BUILD_MGCP_TRANSCODING +#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" +#endif + +#include "src/osmo-bsc_mgcp/mgcp_transcode.h" + +static int audio_name_to_type(const char *name) +{ + if (!strcasecmp(name, "gsm")) + return 3; +#ifdef HAVE_BCG729 + else if (!strcasecmp(name, "g729")) + return 18; +#endif + else if (!strcasecmp(name, "pcma")) + return 8; + else if (!strcasecmp(name, "l16")) + return 11; + return -1; +} + +int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); + +int main(int argc, char **argv) +{ + char buf[4096] = {0}; + int cc, rc; + struct mgcp_rtp_end dst_end = {0}; + struct mgcp_rtp_end src_end = {0}; + struct mgcp_trunk_config tcfg = {{0}}; + struct mgcp_endpoint endp = {0}; + struct mgcp_process_rtp_state *state; + int in_size; + + osmo_init_logging(&log_info); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + endp.tcfg = &tcfg; + + 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) + errx(1, "invalid input format '%s'", argv[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); + if (rc < 0) + errx(1, "setup failed: %s", strerror(-rc)); + + 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); + + while ((cc = read(0, buf + 12, in_size))) { + 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)); + + cc -= 12; /* ignore RTP header */ + if (write(1, buf + 12, cc) != cc) + err(1, "write"); + } + return 0; +} + diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c index 7247c88..67e7e52 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -72,6 +72,19 @@ struct mgcp_process_rtp_state { size_t dst_samples_per_frame; };
+int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) +{ + struct mgcp_process_rtp_state *state = state_; + if (dst) + return (nsamples >= 0 ? + nsamples / state->dst_samples_per_frame : + 1) * state->dst_frame_size; + else + return (nsamples >= 0 ? + nsamples / state->src_samples_per_frame : + 1) * state->src_frame_size; +} + static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) { if (rtp_end->subtype_name) { diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h index 2dfb06a..0961634 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h @@ -31,4 +31,6 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); + +int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); #endif /* OPENBSC_MGCP_TRANSCODE_H */
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 --- openbsc/contrib/testconv/testconv_main.c | 52 +++++-- openbsc/include/openbsc/mgcp.h | 5 +- openbsc/include/openbsc/mgcp_internal.h | 3 +- openbsc/src/libmgcp/mgcp_network.c | 30 +++- openbsc/src/libmgcp/mgcp_protocol.c | 15 +- openbsc/src/libmgcp/mgcp_vty.c | 22 +++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 232 +++++++++++++++++++--------- 7 files changed, 262 insertions(+), 97 deletions(-)
diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index c2785f2..aee7304 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -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; } diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index b595aba..eb64e32 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -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; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index e74b9fa..c7bc2a8 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -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, diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index dcbb97a..42f0381 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -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, diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index f0d94c8..a903e3c 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -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; diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 953d34b..f1afa95 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -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); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c index 67e7e52..edd3178 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,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; }
This modification allows it to set the number of samples per packet that is written to the output.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/testconv_main.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index aee7304..e74c686 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -46,6 +46,10 @@ int main(int argc, char **argv) struct mgcp_endpoint endp = {0}; struct mgcp_process_rtp_state *state; int in_size; + int in_samples = 160; + int out_samples = 0; + uint32_t ts = 0; + uint16_t seq = 0;
osmo_init_logging(&log_info);
@@ -58,12 +62,20 @@ int main(int argc, char **argv) src_end = &endp.net_end;
if (argc <= 2) - errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16}"); + errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]");
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) errx(1, "invalid output format '%s'", argv[2]); + if (argc > 3) + out_samples = atoi(argv[3]); + + if (out_samples) { + dst_end->frame_duration_den = dst_end->rate; + dst_end->frame_duration_num = out_samples; + dst_end->frames_per_packet = 1; + }
rc = mgcp_transcoding_setup(&endp, dst_end, src_end); if (rc < 0) @@ -72,7 +84,7 @@ int main(int argc, char **argv) state = dst_end->rtp_process_data; OSMO_ASSERT(state != NULL);
- in_size = mgcp_transcoding_get_frame_size(state, 160, 0); + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12);
buf[1] = src_end->payload_type; @@ -87,13 +99,19 @@ int main(int argc, char **argv) if (cc != in_size) err(1, "read");
+ *(uint16_t*)(buf+2) = htonl(seq); + *(uint32_t*)(buf+4) = htonl(ts); + + seq += 1; + ts += in_samples; + cc += 12; /* include RTP header */
len = cc;
do { cont = mgcp_transcoding_process_rtp(&endp, dst_end, - buf, &len, sizeof(buf)); + buf, &len, sizeof(buf)); if (cont == -EAGAIN) { fprintf(stderr, "Got EAGAIN\n"); break;
This patch adds test cases for transcoding and repacking.
Sponsored-by: On-Waves ehf --- openbsc/tests/atlocal.in | 1 + openbsc/tests/mgcp/Makefile.am | 19 +- openbsc/tests/mgcp/mgcp_transcoding_test.c | 377 +++++++++++++++++++ openbsc/tests/mgcp/mgcp_transcoding_test.ok | 534 +++++++++++++++++++++++++++ openbsc/tests/testsuite.at | 7 + 5 files changed, 935 insertions(+), 3 deletions(-) create mode 100644 openbsc/tests/mgcp/mgcp_transcoding_test.c create mode 100644 openbsc/tests/mgcp/mgcp_transcoding_test.ok
diff --git a/openbsc/tests/atlocal.in b/openbsc/tests/atlocal.in index 4635113..542a78e 100644 --- a/openbsc/tests/atlocal.in +++ b/openbsc/tests/atlocal.in @@ -1,3 +1,4 @@ enable_nat_test='@osmo_ac_build_nat@' enable_smpp_test='@osmo_ac_build_smpp@' enable_bsc_test='@osmo_ac_build_bsc@' +enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@' diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 71bf8c0..81f28ae 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -1,11 +1,15 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir) +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS)
-EXTRA_DIST = mgcp_test.ok +EXTRA_DIST = mgcp_test.ok mgcp_transcoding_test.ok
noinst_PROGRAMS = mgcp_test
+if BUILD_MGCP_TRANSCODING +noinst_PROGRAMS += mgcp_transcoding_test +endif + mgcp_test_SOURCES = mgcp_test.c
mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ @@ -13,3 +17,12 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL) + +mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c $(top_builddir)/src/osmo-bsc_mgcp/mgcp_transcode.c + +mgcp_transcoding_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBRARY_DL) diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c new file mode 100644 index 0000000..e5da138 --- /dev/null +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -0,0 +1,377 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/application.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> + +#include "bscconfig.h" +#ifndef BUILD_MGCP_TRANSCODING +#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" +#endif + +#include "src/osmo-bsc_mgcp/mgcp_transcode.h" + +uint8_t *audio_frame_l16[] = { +}; + +struct rtp_packets { + float t; + int len; + char *data; +}; + +struct rtp_packets audio_packets_l16[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 332, + "\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" + }, +}; + +struct rtp_packets audio_packets_gsm[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_size[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 41, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_data[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" + "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" + "\xEE" + }, +}; + +struct rtp_packets audio_packets_gsm_invalid_ptype[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 45, + "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" + "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" + "\xDE" + }, +}; + +struct rtp_packets audio_packets_g729[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 32, + "\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5" + "\xB2\x95\xC4\xAD" + }, +}; + +struct rtp_packets audio_packets_pcma[] = { + /* RTP: SeqNo=1, TS=160 */ + {0.020000, 172, + "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" + }, +}; + + + +static int audio_name_to_type(const char *name) +{ + if (!strcasecmp(name, "gsm")) + return 3; +#ifdef HAVE_BCG729 + else if (!strcasecmp(name, "g729")) + return 18; +#endif + else if (!strcasecmp(name, "pcma")) + return 8; + else if (!strcasecmp(name, "l16")) + return 11; + return -1; +} + +int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); + +static int transcode_test(const char *srcfmt, const char *dstfmt, + uint8_t *src_pkts, size_t src_pkt_size) +{ + char buf[4096] = {0x80, 0}; + int rc; + 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; + int in_size; + int in_samples = 160; + int len, cont; + + printf("== Transcoding test ==\n"); + printf("converting %s -> %s\n", srcfmt, dstfmt); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + endp.tcfg = &tcfg; + mgcp_free_endp(&endp); + + dst_end = &endp.bts_end; + src_end = &endp.net_end; + + src_end->payload_type = audio_name_to_type(srcfmt); + dst_end->payload_type = audio_name_to_type(dstfmt); + + 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; + OSMO_ASSERT(state != NULL); + + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); + OSMO_ASSERT(sizeof(buf) >= in_size + 12); + + memcpy(buf, src_pkts, src_pkt_size); + + len = src_pkt_size; + + cont = mgcp_transcoding_process_rtp(&endp, dst_end, + buf, &len, sizeof(buf)); + if (cont < 0) + errx(1, "processing failed: %s", strerror(-cont)); + + if (len < 24) { + printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len)); + } else { + const char *str = osmo_hexdump((unsigned char *)buf, len); + int i = 0; + const int prefix = 4; + const int cutlen = 48; + int nchars = 0; + + printf("encoded:\n"); + do { + nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i); + i += nchars - prefix; + printf("\n"); + } while (nchars - prefix >= cutlen); + } + return 0; +} + +static int test_repacking(int in_samples, int out_samples, int no_transcode) +{ + char buf[4096] = {0x80, 0}; + int cc, rc; + struct mgcp_rtp_end *dst_end; + struct mgcp_rtp_end *src_end; + struct mgcp_config *cfg; + struct mgcp_trunk_config tcfg = {{0}}; + struct mgcp_endpoint endp = {0}; + struct mgcp_process_rtp_state *state; + int in_cnt; + int out_size; + int in_size; + uint32_t ts = 0; + uint16_t seq = 0; + const char *srcfmt = "pcma"; + const char *dstfmt = no_transcode ? "pcma" : "l16"; + + cfg = mgcp_config_alloc(); + + tcfg.endpoints = &endp; + tcfg.number_endpoints = 1; + tcfg.cfg = cfg; + endp.tcfg = &tcfg; + endp.cfg = cfg; + mgcp_free_endp(&endp); + + dst_end = &endp.bts_end; + src_end = &endp.net_end; + + printf("== Transcoding test ==\n"); + printf("converting %s -> %s\n", srcfmt, dstfmt); + + src_end->payload_type = audio_name_to_type(srcfmt); + dst_end->payload_type = audio_name_to_type(dstfmt); + + if (out_samples) { + dst_end->frame_duration_den = dst_end->rate; + dst_end->frame_duration_num = out_samples; + dst_end->frames_per_packet = 1; + dst_end->force_output_ptime = 1; + } + + 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; + OSMO_ASSERT(state != NULL); + + in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); + OSMO_ASSERT(sizeof(buf) >= in_size + 12); + + out_size = mgcp_transcoding_get_frame_size(state, -1, 1); + OSMO_ASSERT(sizeof(buf) >= out_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); + + for (in_cnt = 0; in_cnt < 16; in_cnt++) { + int cont; + int len; + + /* fake PCMA data */ + printf("generating %d %s input samples\n", in_samples, srcfmt); + for (cc = 0; cc < in_samples; cc++) + buf[12+cc] = cc; + + *(uint16_t*)(buf+2) = htonl(seq); + *(uint32_t*)(buf+4) = htonl(ts); + + seq += 1; + ts += in_samples; + + cc += 12; /* include RTP header */ + + 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 */ + + printf("got %d %s output frames (%d octets)\n", + len / out_size, dstfmt, len); + + len = cont; + } while (len > 0); + } + return 0; +} + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + + printf("=== Transcoding Good Cases ===\n"); + + transcode_test("l16", "l16", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("l16", "gsm", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("l16", "pcma", + (uint8_t *)audio_packets_l16[0].data, + audio_packets_l16[0].len); + transcode_test("gsm", "l16", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("gsm", "gsm", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm[0].data, + audio_packets_gsm[0].len); + transcode_test("pcma", "l16", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + transcode_test("pcma", "gsm", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + transcode_test("pcma", "pcma", + (uint8_t *)audio_packets_pcma[0].data, + audio_packets_pcma[0].len); + + printf("=== Transcoding Bad Cases ===\n"); + + printf("Invalid size:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_size[0].data, + audio_packets_gsm_invalid_size[0].len); + + printf("Invalid data:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_data[0].data, + audio_packets_gsm_invalid_data[0].len); + + printf("Invalid payload type:\n"); + transcode_test("gsm", "pcma", + (uint8_t *)audio_packets_gsm_invalid_ptype[0].data, + audio_packets_gsm_invalid_ptype[0].len); + + printf("=== Repacking ===\n"); + + test_repacking(160, 160, 0); + test_repacking(160, 160, 1); + test_repacking(160, 80, 0); + test_repacking(160, 80, 1); + test_repacking(160, 320, 0); + test_repacking(160, 320, 1); + test_repacking(160, 240, 0); + test_repacking(160, 240, 1); + test_repacking(160, 100, 0); + test_repacking(160, 100, 1); + + return 0; +} + diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.ok b/openbsc/tests/mgcp/mgcp_transcoding_test.ok new file mode 100644 index 0000000..189d079 --- /dev/null +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.ok @@ -0,0 +1,534 @@ +=== Transcoding Good Cases === +== Transcoding test == +converting l16 -> l16 +encoded: + 80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 + 5a 9e 40 13 00 00 bf ed a5 62 bf ed +== Transcoding test == +converting l16 -> gsm +encoded: + 80 0b 00 00 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de +== Transcoding test == +converting l16 -> pcma +encoded: + 80 0b 00 00 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 +== Transcoding test == +converting gsm -> l16 +encoded: + 80 03 00 00 00 00 00 a0 11 22 33 44 00 00 54 00 + 59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0 + 46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0 + 24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8 + 66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40 + 37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0 + 4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90 + 4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48 + 50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8 + 54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30 + 4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8 + 55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58 + 51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98 + 51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8 + 54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0 + 51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18 + 50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40 + 51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8 + 4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08 + 4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18 + 4d a0 3b 98 07 90 cf 18 b4 68 c4 88 +== Transcoding test == +converting gsm -> gsm +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf + 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 + 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 + 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 + 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd + 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf + 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf + 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 + db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 + f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 + ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 +== Transcoding test == +converting pcma -> l16 +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 + 5a 00 42 00 00 08 be 00 a6 00 be 00 +== Transcoding test == +converting pcma -> gsm +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 d4 b9 f4 5d + d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2 + 0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39 +== Transcoding test == +converting pcma -> pcma +encoded: + 80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 + d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 +=== Transcoding Bad Cases === +Invalid size: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 + 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc + 69 9c d1 f0 66 7a ec 49 7a +Invalid data: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 03 00 01 00 00 00 a0 11 22 33 44 ee ee ee ee + ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee + ee ee ee ee ee ee ee ee ee ee ee ee ee +Invalid payload type: +== Transcoding test == +converting gsm -> pcma +encoded: + 80 08 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf + 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 + 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 + 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 + 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd + 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf + 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf + 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 + db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 + f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 + ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 +=== Repacking === +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +generating 160 pcma input samples +got 2 l16 output frames (320 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +generating 160 pcma input samples +got 2 pcma output frames (160 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 l16 output frames (640 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 4 pcma output frames (320 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 l16 output frames (480 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +generating 160 pcma input samples +generating 160 pcma input samples +got 3 pcma output frames (240 octets) +== Transcoding test == +converting pcma -> l16 +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +generating 160 pcma input samples +got 1 l16 output frames (160 octets) +got 1 l16 output frames (160 octets) +== Transcoding test == +converting pcma -> pcma +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) +generating 160 pcma input samples +got 1 pcma output frames (80 octets) +got 1 pcma output frames (80 octets) diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 4465b25..57310d6 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -27,6 +27,13 @@ cat $abs_srcdir/mgcp/mgcp_test.ok > expout AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([mgcp-trans]) +AT_KEYWORDS([mgcp-trans]) +AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77]) +cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore]) +AT_CLEANUP + AT_SETUP([gprs]) AT_KEYWORDS([gprs]) cat $abs_srcdir/gprs/gprs_test.ok > expout
This patch moves the files relevant to transcoding from src/osmo-bsc_mgcp to src/libmgcp and src/include/openbsc. Makefiles and include directives are being updated accordingly.
Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/Makefile | 3 +-- openbsc/contrib/testconv/testconv_main.c | 2 +- openbsc/include/openbsc/Makefile.am | 3 ++- .../openbsc}/mgcp_transcode.h | 0 openbsc/src/libmgcp/Makefile.am | 12 ++++++++++-- .../src/{osmo-bsc_mgcp => libmgcp}/g711common.h | 0 .../{osmo-bsc_mgcp => libmgcp}/mgcp_transcode.c | 0 openbsc/src/osmo-bsc_mgcp/Makefile.am | 9 ++------- openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 2 +- openbsc/tests/mgcp/Makefile.am | 2 +- openbsc/tests/mgcp/mgcp_transcoding_test.c | 2 +- 11 files changed, 19 insertions(+), 16 deletions(-) rename openbsc/{src/osmo-bsc_mgcp => include/openbsc}/mgcp_transcode.h (100%) rename openbsc/src/{osmo-bsc_mgcp => libmgcp}/g711common.h (100%) rename openbsc/src/{osmo-bsc_mgcp => libmgcp}/mgcp_transcode.c (100%)
diff --git a/openbsc/contrib/testconv/Makefile b/openbsc/contrib/testconv/Makefile index 90adecc..bb856f7 100644 --- a/openbsc/contrib/testconv/Makefile +++ b/openbsc/contrib/testconv/Makefile @@ -1,5 +1,5 @@
-OBJS = testconv_main.o mgcp_transcode.o +OBJS = testconv_main.o
CC = gcc CFLAGS = -O0 -ggdb -Wall @@ -11,7 +11,6 @@ testconv: $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
testconv_main.o: testconv_main.c -mgcp_transcode.o: ../../src/osmo-bsc_mgcp/mgcp_transcode.c
$(OBJS): $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index e74c686..89dce1a 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -17,7 +17,7 @@ #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif
-#include "src/osmo-bsc_mgcp/mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h"
static int audio_name_to_type(const char *name) { diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 8f7c1c4..6b08d07 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -13,7 +13,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ - arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h + arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ + mgcp_transcode.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/include/openbsc/mgcp_transcode.h similarity index 100% rename from openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h rename to openbsc/include/openbsc/mgcp_transcode.h diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am index 72f625d..bd02e61 100644 --- a/openbsc/src/libmgcp/Makefile.am +++ b/openbsc/src/libmgcp/Makefile.am @@ -1,7 +1,15 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBBCG729_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) \ + $(LIBBCG729_LIBS)
noinst_LIBRARIES = libmgcp.a
+noinst_HEADERS = g711common.h + libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c + +if BUILD_MGCP_TRANSCODING + libmgcp_a_SOURCES += mgcp_transcode.c +endif diff --git a/openbsc/src/osmo-bsc_mgcp/g711common.h b/openbsc/src/libmgcp/g711common.h similarity index 100% rename from openbsc/src/osmo-bsc_mgcp/g711common.h rename to openbsc/src/libmgcp/g711common.h diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c similarity index 100% rename from openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c rename to openbsc/src/libmgcp/mgcp_transcode.c diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index a620e7a..da02380 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,16 +1,11 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ - $(LIBBCG729_CFLAGS) + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
bin_PROGRAMS = osmo-bsc_mgcp
osmo_bsc_mgcp_SOURCES = mgcp_main.c -if BUILD_MGCP_TRANSCODING - osmo_bsc_mgcp_SOURCES += mgcp_transcode.c -endif + osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) - -noinst_HEADERS = g711common.h mgcp_transcode.h diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 6b72965..8c3808a 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -50,7 +50,7 @@ #include "../../bscconfig.h"
#ifdef BUILD_MGCP_TRANSCODING -#include "mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h" #endif
/* this is here for the vty... it will never be called */ diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 81f28ae..2bc2da6 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -18,7 +18,7 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL)
-mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c $(top_builddir)/src/osmo-bsc_mgcp/mgcp_transcode.c +mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c
mgcp_transcoding_test_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c index e5da138..9ba2c4b 100644 --- a/openbsc/tests/mgcp/mgcp_transcoding_test.c +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -17,7 +17,7 @@ #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif
-#include "src/osmo-bsc_mgcp/mgcp_transcode.h" +#include "openbsc/mgcp_transcode.h"
uint8_t *audio_frame_l16[] = { };
Currently, if there is no SDP data in the MGCP message received from the net, the fields containing audio encoding information are not set in net_end. So in recvonly mode transcoding would not be set up correctly.
This patch changes the implementation of the code handling CRCX and MDCX to use the codec signalled in the MGCP local connection options (field 'a:') if there isn't any SDP data. This is only halfway negotiation, because the codec is used blindly and not matched against the supported ones.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 1 + openbsc/src/libmgcp/mgcp_protocol.c | 37 ++++++++++++++++++++++++++++--- openbsc/tests/mgcp/mgcp_test.c | 7 ++++++ openbsc/tests/mgcp/mgcp_test.ok | 5 +++++ 4 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index c7bc2a8..e877026 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -124,6 +124,7 @@ struct mgcp_rtp_tap {
struct mgcp_lco { char *string; + char *codec; int pkt_period_min; /* time in ms */ int pkt_period_max; /* time in ms */ }; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index a903e3c..04f7750 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -582,7 +582,8 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, talloc_free(rtp->audio_name); rtp->audio_name = NULL;
- rtp->payload_type = payload_type; + if (payload_type >= 0) + rtp->payload_type = payload_type;
if (!audio_name) { switch (payload_type) { @@ -597,7 +598,7 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, }
if (sscanf(audio_name, "%63[^/]/%d/%d", - audio_codec, &rate, &channels) < 2) + audio_codec, &rate, &channels) < 1) return -EINVAL;
rtp->rate = rate; @@ -610,6 +611,20 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, rtp->frame_duration_den = 1000; }
+ if (payload_type < 0) { + payload_type = 96; + if (rate == 8000 && channels == 1) { + if (!strcmp(audio_codec, "GSM")) + payload_type = 3; + else if (!strcmp(audio_codec, "PCMA")) + payload_type = 8; + else if (!strcmp(audio_codec, "G729")) + payload_type = 18; + } + + rtp->payload_type = payload_type; + } + if (channels != 1) LOGP(DMGCP, LOGL_NOTICE, "Channels != 1 in SDP: '%s'\n", audio_name); @@ -781,9 +796,12 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, const char *options) { - char *p_opt; + char *p_opt, *a_opt; + char codec[9];
talloc_free(lco->string); + talloc_free(lco->codec); + lco->codec = NULL; lco->pkt_period_min = lco->pkt_period_max = 0; lco->string = talloc_strdup(ctx, options ? options : "");
@@ -791,6 +809,10 @@ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, if (p_opt && sscanf(p_opt, "p:%d-%d", &lco->pkt_period_min, &lco->pkt_period_max) == 1) lco->pkt_period_max = lco->pkt_period_min; + + a_opt = strstr(lco->string, "a:"); + if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) + lco->codec = talloc_strdup(ctx, codec); }
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, @@ -924,6 +946,9 @@ mgcp_header_done: tcfg->audio_fmtp_extra); if (have_sdp) parse_sdp_data(&endp->net_end, p); + else if (endp->local_options.codec) + set_audio_info(p->cfg, &endp->net_end, + -1, endp->local_options.codec);
if (p->cfg->bts_force_ptime) { endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; @@ -977,6 +1002,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) struct mgcp_endpoint *endp = p->endp; int error_code = 500; int silent = 0; + int have_sdp = 0; char *line; const char *local_options = NULL;
@@ -1016,6 +1042,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; case '\0': /* SDP file begins */ + have_sdp = 1; parse_sdp_data(&endp->net_end, p); /* This will exhaust p->save, so the loop will * terminate next time. @@ -1031,6 +1058,10 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options);
+ if (!have_sdp && endp->local_options.codec) + set_audio_info(p->cfg, &endp->net_end, + -1, endp->local_options.codec); + setup_rtp_processing(endp);
/* policy CB */ diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 498a1d8..0552c72 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -168,6 +168,12 @@ static void test_strline(void) "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n"
+#define MDCX4_RO "MDCX 18983221 1@mgw MGCP 1.0\r\n" \ + "M: recvonly\r" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20, a:AMR, nt:IN\r\n" + #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1@mgw" @@ -257,6 +263,7 @@ static const struct mgcp_test tests[] = { { "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 }, { "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 }, { "MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99, 126 }, + { "MDCX4_RO", MDCX4_RO, MDCX4_RET("18983221"), PTYPE_IGNORE, 126 }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 }, { "EMPTY", EMPTY, EMPTY_RET }, diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3901cfb..7301a81 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -49,6 +49,11 @@ Testing MDCX4_SO Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 2: SEND +Testing MDCX4_RO +Dummy packets: 1 +Packet duration not set +Requested packetetization period: 20-20 +Connection mode: 1: RECV Testing DLCX Detected packet duration: 20 Requested packetization period not set
On Thu, May 15, 2014 at 10:29:09AM +0200, Jacob Erlbeck wrote:
Dear Jacob,
This patch adds the callbacks rtp_processing_cb and setup_rtp_processing_cb to mgcp_config to support arbitrary RTP payload processing.
could you please make a smoke test with a bidirectional call (e.g. by calling our office number and picking up) and then rebase your code against master and merge with --no-ff
thanks