From a0d64ce063fbf60733c6f2949015091aa33a2c9d Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Thu, 13 Mar 2014 14:24:52 +0100 Subject: [PATCH 01/11] mgcp: Add callbacks for payload processing 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 | 9 +++++++ openbsc/src/libmgcp/mgcp_network.c | 20 ++++++++++++--- openbsc/src/libmgcp/mgcp_protocol.c | 34 ++++++++++++++++++++++++- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 939e7eed1..34eb42955 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 52bf55843..d2818139e 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -90,6 +90,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... @@ -205,6 +206,14 @@ 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); + enum { MGCP_DEST_NET = 0, MGCP_DEST_BTS, diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 5e8284da2..393a9e52a 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -79,6 +79,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 { MGCP_PROTO_RTP, @@ -339,6 +340,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 @@ -589,6 +602,7 @@ 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, @@ -627,7 +641,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; @@ -723,7 +737,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; @@ -790,7 +804,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 1a88a84b3..964971b00 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 int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) @@ -852,8 +854,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) { @@ -980,6 +984,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; @@ -1223,6 +1229,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; @@ -1288,6 +1297,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; @@ -1442,6 +1453,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; From 845d0054b45dff5f5606b38dcacd9bf06a605f07 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 17 Mar 2014 12:30:56 +0100 Subject: [PATCH 02/11] mgcp: Add audio info fields to struct mgcp_rtp_end 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). The patch also moves the audio_name parsing code to a dedicated set_audio_info() function. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 3 + openbsc/src/libmgcp/mgcp_protocol.c | 84 +++++++++++++++++++------ 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index d2818139e..dbdc1d9d9 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -80,11 +80,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 964971b00..862bf175d 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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); @@ -244,6 +246,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; @@ -257,12 +261,13 @@ 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->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "", 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)) @@ -528,6 +533,48 @@ 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 = rtp->rate; + int channels = rtp->channels; + 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: + /* Payload type is unknown, don't change rate and + * channels. */ + /* TODO: return value? */ + 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)) @@ -614,11 +661,8 @@ 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; @@ -628,15 +672,7 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) 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) @@ -659,8 +695,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; } @@ -851,7 +887,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) { @@ -1297,6 +1333,10 @@ 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->subtype_name); + end->subtype_name = NULL; + talloc_free(end->audio_name); + end->audio_name = NULL; talloc_free(end->rtp_process_data); end->rtp_process_data = NULL; @@ -1306,6 +1346,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; } @@ -1383,6 +1424,8 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; + 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), @@ -1392,10 +1435,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; From 168ca00b02685edea650aa2a4c62481a75926125 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 17 Mar 2014 12:40:07 +0100 Subject: [PATCH 03/11] mgcp: Add a function to get media info for MGCP responses 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 | 19 ++++++++++++++----- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 34eb42955..002dd7c6e 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 dbdc1d9d9..ac136a377 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -217,6 +217,11 @@ 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); + enum { MGCP_DEST_NET = 0, MGCP_DEST_BTS, diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 393a9e52a..05c3e7749 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -353,6 +353,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 862bf175d..7837378f3 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -245,12 +245,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; @@ -1268,6 +1271,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; @@ -1424,8 +1429,12 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, { char buf[2096]; int len; - 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), From 997e1e8e9da0b82cc3755e50bb071c12975ff6ba Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Fri, 14 Mar 2014 18:01:20 +0100 Subject: [PATCH 04/11] mgcp: Only include SDP lines with valid content 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 | 131 +++++++++++++++++++--------- 1 file changed, 89 insertions(+), 42 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 7837378f3..6e974f147 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -241,56 +241,106 @@ 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%s\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->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "", - 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" : ""); + "t=0 0\r\n", + endp->ci, addr, addr); - if (len < 0 || len >= sizeof(sdp_record)) + 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%s\n\n", + endp->ci, + endp->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : ""); + + 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); } /* @@ -726,9 +776,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; @@ -1429,30 +1480,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); From 239a853f40879a68487057fbcd0a09a8d3625185 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Thu, 13 Mar 2014 14:25:51 +0100 Subject: [PATCH 05/11] mgcp: Add RTP audio transcoding 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 26a7b6209..72f6e6ade 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -57,6 +57,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 7b6262142..be399779f 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,11 +1,17 @@ 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) \ - $(LIBOSMONETIF_LIBS) + $(LIBOSMONETIF_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 000000000..cb35fc651 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/g711common.h @@ -0,0 +1,187 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara + * + * Wrapper for linphone Codec class by Simon Morlat + * + * + * 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 14ec22143..6b7296591 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 000000000..cadc8769e --- /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 . + * + */ + +#include +#include +#include + +#include "bscconfig.h" + +#include "g711common.h" +#include +#ifdef HAVE_BCG729 +#include +#include +#endif + +#include +#include +#include + +#include + +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; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (dst_end->rtp_process_data) { + talloc_free(dst_end->rtp_process_data); + 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 000000000..2dfb06abf --- /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 . + * + */ +#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 */ From 136a319e910eec81ea9ff8f8a34c324557109d03 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Thu, 13 Mar 2014 14:33:37 +0100 Subject: [PATCH 06/11] mgcp: Add CLI tool to test audio conversion 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 000000000..90adeccca --- /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 000000000..c2785f23e --- /dev/null +++ b/openbsc/contrib/testconv/testconv_main.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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 cadc8769e..91c0c383c 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 2dfb06abf..0961634da 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 */ From 42a833e89f443116fb165c35654c9f21ceed6876 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 14 Apr 2014 10:31:47 +0200 Subject: [PATCH 07/11] mgcp: Add packet size (ptime) conversion The current transcoder implemenation always does a 1:1 recoding concerning the duration of a packet. So RTP timestamps and sequence numbers are not modified. This is not sufficient in some cases, e.g. when the BTS does only allow for a single fixed ptime. This patch decouples encoding from decoding and moves the decoded samples to the state structure so that samples can be combined or drain according to the packaging of incoming and outgoing packets. This patch incorporates parts of Holger's experimental fixes in 0e669e05^..9eba68f9. Ticket: OW#1111 Sponsored-by: On-Waves ehf --- 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 | 18 +- openbsc/src/libmgcp/mgcp_vty.c | 22 ++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 229 ++++++++++++++------- 7 files changed, 262 insertions(+), 97 deletions(-) diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index c2785f23e..aee73048c 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; - cc -= 12; /* ignore RTP header */ - if (write(1, buf + 12, cc) != cc) - err(1, "write"); + 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"); + + len = cont; + } while (len > 0); } return 0; } diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 002dd7c6e..d4d614099 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 ac136a377..9f0c0f9e2 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -89,6 +89,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 */ @@ -210,7 +211,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 05c3e7749..219d3f990 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -340,7 +340,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; @@ -614,12 +614,28 @@ 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 6e974f147..21b9ff0f8 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -621,6 +621,15 @@ 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; + } else { + rtp->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; + rtp->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; + } + if (channels != 1) LOGP(DMGCP, LOGL_NOTICE, "Channels != 1 in SDP: '%s'\n", audio_name); @@ -944,11 +953,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 1f8a63ab7..26b570627 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -366,6 +366,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", @@ -1123,6 +1143,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 91c0c383c..581cd3293 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 #include -#include "bscconfig.h" + +#include "../../bscconfig.h" #include "g711common.h" #include @@ -70,6 +70,14 @@ struct mgcp_process_rtp_state { } dst; size_t dst_frame_size; size_t dst_samples_per_frame; + int dst_packet_duration; + + int is_running; + uint16_t next_seq; + uint32_t next_time; + int16_t samples[10*160]; + size_t sample_cnt; + size_t sample_offs; }; int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) @@ -302,6 +310,9 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp, break; } + if (dst_end->force_output_ptime) + state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); + LOGP(DMGCP, LOGL_INFO, "Initialized RTP processing on: 0x%x " "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", @@ -330,44 +341,21 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, *audio_name = endp->net_end.audio_name; } - -int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size) +static int decode_audio(struct mgcp_process_rtp_state *state, + uint8_t **src, size_t *nbytes) { - struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; - size_t rtp_hdr_size = 12; - char *payload_data = data + rtp_hdr_size; - int payload_len = *len - rtp_hdr_size; - size_t sample_cnt = 0; - size_t sample_idx; - int16_t samples[10*160]; - uint8_t *src = (uint8_t *)payload_data; - uint8_t *dst = (uint8_t *)payload_data; - size_t nbytes = payload_len; - size_t frame_remainder; - - if (!state) - return 0; - - if (state->src_fmt == state->dst_fmt) - return 0; - - /* TODO: check payload type (-> G.711 comfort noise) */ - - /* Decode src into samples */ - while (nbytes >= state->src_frame_size) { - if (sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(samples)) { + while (*nbytes >= state->src_frame_size) { + if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { LOGP(DMGCP, LOGL_ERROR, "Sample buffer too small: %d > %d.\n", - sample_cnt + state->src_samples_per_frame, - ARRAY_SIZE(samples)); + state->sample_cnt + state->src_samples_per_frame, + ARRAY_SIZE(state->samples)); return -ENOSPC; } switch (state->src_fmt) { case AF_GSM: if (gsm_decode(state->src.gsm_handle, - (gsm_byte *)src, samples + sample_cnt) < 0) { + (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to decode GSM.\n"); return -EINVAL; @@ -375,54 +363,44 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, break; #ifdef HAVE_BCG729 case AF_G729: - bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt); + bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); break; #endif case AF_PCMA: - alaw_decode(src, samples + sample_cnt, + alaw_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; case AF_S16: - memmove(samples + sample_cnt, src, + memmove(state->samples + state->sample_cnt, *src, state->src_frame_size); break; case AF_L16: - l16_decode(src, samples + sample_cnt, + l16_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; default: break; } - src += state->src_frame_size; - nbytes -= state->src_frame_size; - sample_cnt += state->src_samples_per_frame; - } - - /* Add silence if necessary */ - frame_remainder = sample_cnt % state->dst_samples_per_frame; - if (frame_remainder) { - size_t silence = state->dst_samples_per_frame - frame_remainder; - if (sample_cnt + silence > ARRAY_SIZE(samples)) { - LOGP(DMGCP, LOGL_ERROR, - "Sample buffer too small for silence: %d > %d.\n", - sample_cnt + silence, - ARRAY_SIZE(samples)); - return -ENOSPC; - } - - while (silence > 0) { - samples[sample_cnt] = 0; - sample_cnt += 1; - silence -= 1; - } + *src += state->src_frame_size; + *nbytes -= state->src_frame_size; + state->sample_cnt += state->src_samples_per_frame; } + return 0; +} +static int encode_audio(struct mgcp_process_rtp_state *state, + uint8_t *dst, size_t buf_size, size_t max_samples) +{ + int nbytes = 0; + size_t nsamples = 0; /* Encode samples into dst */ - sample_idx = 0; - nbytes = 0; - while (sample_idx + state->dst_samples_per_frame <= sample_cnt) { + while (nsamples + state->dst_samples_per_frame <= max_samples) { if (nbytes + state->dst_frame_size > buf_size) { - LOGP(DMGCP, LOGL_ERROR, + if (nbytes > 0) + break; + + /* Not even one frame fits into the buffer */ + LOGP(DMGCP, LOGL_INFO, "Encoding (RTP) buffer too small: %d > %d.\n", nbytes + state->dst_frame_size, buf_size); return -ENOSPC; @@ -430,23 +408,24 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, switch (state->dst_fmt) { case AF_GSM: gsm_encode(state->dst.gsm_handle, - samples + sample_idx, dst); + state->samples + state->sample_offs, dst); break; #ifdef HAVE_BCG729 case AF_G729: bcg729Encoder(state->dst.g729_enc, - samples + sample_idx, dst); + state->samples + state->sample_offs, dst); break; #endif case AF_PCMA: - alaw_encode(samples + sample_idx, dst, + alaw_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; case AF_S16: - memmove(dst, samples + sample_idx, state->dst_frame_size); + memmove(dst, state->samples + state->sample_offs, + state->dst_frame_size); break; case AF_L16: - l16_encode(samples + sample_idx, dst, + l16_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; default: @@ -454,12 +433,118 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, } dst += state->dst_frame_size; nbytes += state->dst_frame_size; - sample_idx += state->dst_samples_per_frame; + state->sample_offs += state->dst_samples_per_frame; + nsamples += state->dst_samples_per_frame; + } + state->sample_cnt -= nsamples; + return nbytes; +} + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + size_t rtp_hdr_size = 12; + char *payload_data = data + rtp_hdr_size; + int payload_len = *len - rtp_hdr_size; + uint8_t *src = (uint8_t *)payload_data; + uint8_t *dst = (uint8_t *)payload_data; + size_t nbytes = payload_len; + size_t nsamples; + size_t max_samples; + uint32_t ts_no; + int rc; + + if (!state) + return 0; + + if (state->src_fmt == state->dst_fmt) { + if (!state->dst_packet_duration) + return 0; + + /* TODO: repackage without transcoding */ } - *len = rtp_hdr_size + nbytes; - /* Patch payload type */ - data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f); + /* 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 + */ - return 0; + /* TODO: check payload type (-> G.711 comfort noise) */ + + if (payload_len > 0) { + ts_no = ntohl(*(uint32_t*)(data+4)); + if (!state->is_running) + state->next_seq = ntohs(*(uint32_t*)(data+4)); + + state->is_running = 1; + + if (state->sample_cnt > 0) { + int32_t delta = ts_no - state->next_time; + /* TODO: check sequence? reordering? packet loss? */ + + if (delta > state->sample_cnt) + /* There is a time gap between the last packet + * and the current one. Just discard the + * partial data that is left in the buffer. + * TODO: This can be improved by adding silence + * instead if the delta is small enough. + */ + state->sample_cnt = 0; + else if (delta < 0) { + LOGP(DMGCP, LOGL_NOTICE, + "RTP time jumps backwards, delta = %d, " + "discarding buffered samples\n", + delta); + state->sample_cnt = 0; + state->sample_offs = 0; + return -EAGAIN; + } + + /* Make sure the samples start without offset */ + if (state->sample_offs && state->sample_cnt) + memmove(&state->samples[0], + &state->samples[state->sample_offs], + state->sample_cnt * + sizeof(state->samples[0])); + } + + state->sample_offs = 0; + + /* Append decoded audio to samples */ + decode_audio(state, &src, &nbytes); + + if (nbytes > 0) + LOGP(DMGCP, LOGL_NOTICE, + "Skipped audio frame in RTP packet: %d octets\n", + nbytes); + } else + ts_no = state->next_time; + + if (state->sample_cnt < state->dst_packet_duration) + return -EAGAIN; + + max_samples = + state->dst_packet_duration ? + state->dst_packet_duration : state->sample_cnt; + + nsamples = state->sample_cnt; + + rc = encode_audio(state, dst, buf_size, max_samples); + if (rc <= 0) + return rc; + + nsamples -= state->sample_cnt; + + *len = rtp_hdr_size + rc; + *(uint16_t*)(data+2) = htonl(state->next_seq); + *(uint32_t*)(data+4) = htonl(ts_no); + + state->next_seq += 1; + state->next_time = ts_no + nsamples; + + return nsamples ? rtp_hdr_size : 0; } From 07886d9b0abe81259433ab39a8a8fc86e1c197a3 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 14 Apr 2014 11:30:45 +0200 Subject: [PATCH 08/11] mgcp: Extend the CLI transcoding tool by ptime conversion 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 aee73048c..e74c686e4 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; From 84a45cbf8384be753e2b83414dddc95ad63f4f2b Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Tue, 8 Apr 2014 16:10:04 +0200 Subject: [PATCH 09/11] mgcp/test: Add test cases for transcoding and repacking 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 46351137b..542a78e92 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 79e0bf49d..3982b0782 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) $(LIBOSMONETIF_LIBS) + +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) $(LIBOSMONETIF_LIBS) diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c new file mode 100644 index 000000000..e5da13856 --- /dev/null +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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 000000000..189d07952 --- /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 4465b2528..57310d629 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 From 909fac6689df570ef0c5983fe51da14eb3bf2783 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Thu, 8 May 2014 14:08:37 +0200 Subject: [PATCH 10/11] mgcp: Move transcoding to libmgcp 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 | 2 +- .../openbsc}/mgcp_transcode.h | 0 openbsc/src/libmgcp/Makefile.am | 12 +++++++++--- openbsc/src/{osmo-bsc_mgcp => libmgcp}/g711common.h | 0 .../src/{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, 17 insertions(+), 17 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 90adeccca..bb856f750 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 e74c686e4..89dce1ac2 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 d902315b5..b739d0f46 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -14,7 +14,7 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.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 \ - osmux.h + osmux.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 262ad34a2..e5dab1ad7 100644 --- a/openbsc/src/libmgcp/Makefile.am +++ b/openbsc/src/libmgcp/Makefile.am @@ -1,9 +1,15 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ - $(LIBOSMONETIF_LIBS) $(COVERAGE_LDFLAGS) + $(LIBOSMONETIF_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 osmux.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 be399779f..fba76b41e 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,17 +1,12 @@ 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) \ $(LIBOSMONETIF_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 6b7296591..8c3808a28 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 3982b0782..ce9e59647 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) $(LIBOSMONETIF_LIBS) -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 e5da13856..9ba2c4b46 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[] = { }; From 452c183c5ee9cc2c1b0225d9706d0425b8c655e3 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Fri, 9 May 2014 19:42:17 +0200 Subject: [PATCH 11/11] mgcp: Set net_end audio params in recvonly mode 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 | 41 +++++++++++++++++++++++-- openbsc/tests/mgcp/mgcp_test.c | 7 +++++ openbsc/tests/mgcp/mgcp_test.ok | 5 +++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 9f0c0f9e2..2b44e40be 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -123,6 +123,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 21b9ff0f8..f0457d1aa 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -80,6 +80,8 @@ char *strline_r(char *str, char **saveptr) #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 +#define PTYPE_UNDEFINED (-1) + static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); struct mgcp_parse_data { @@ -598,7 +600,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 != PTYPE_UNDEFINED) + rtp->payload_type = payload_type; if (!audio_name) { switch (payload_type) { @@ -614,7 +617,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; @@ -630,6 +633,20 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, rtp->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; } + 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); @@ -801,9 +818,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 : ""); @@ -811,6 +831,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, @@ -955,6 +979,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, + PTYPE_UNDEFINED, endp->local_options.codec); if (p->cfg->bts_force_ptime) { endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; @@ -1012,6 +1039,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, osmux = 0; + int have_sdp = 0; char *line; const char *local_options = NULL; @@ -1058,6 +1086,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. @@ -1088,6 +1117,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, + PTYPE_UNDEFINED, endp->local_options.codec); + setup_rtp_processing(endp); /* policy CB */ @@ -1462,6 +1495,8 @@ void mgcp_free_endp(struct mgcp_endpoint *endp) talloc_free(endp->local_options.string); endp->local_options.string = NULL; + talloc_free(endp->local_options.codec); + endp->local_options.codec = NULL; mgcp_rtp_end_reset(&endp->bts_end); mgcp_rtp_end_reset(&endp->net_end); diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 53884833f..19615d9da 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" @@ -258,6 +264,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 3901cfbed..7301a8152 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