diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index 773be26a3..6c95c5542 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -64,16 +64,16 @@ int main(int argc, char **argv) if (argc <= 2) errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]"); - if ((src_end->payload_type = audio_name_to_type(argv[1])) == -1) + if ((src_end->codec.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->codec.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->codec.frame_duration_den = dst_end->codec.rate; + dst_end->codec.frame_duration_num = out_samples; dst_end->frames_per_packet = 1; } @@ -87,7 +87,7 @@ int main(int argc, char **argv) in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12); - buf[1] = src_end->payload_type; + buf[1] = src_end->codec.payload_type; *(uint16_t*)(buf+2) = htons(1); *(uint32_t*)(buf+4) = htonl(0); *(uint32_t*)(buf+8) = htonl(0xaabbccdd); diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 3d308835e..34c3d973a 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -67,6 +67,17 @@ struct mgcp_rtp_state { struct mgcp_rtp_stream_state out_stream; }; +struct mgcp_rtp_codec { + uint32_t rate; + int channels; + uint32_t frame_duration_num; + uint32_t frame_duration_den; + + int payload_type; + char *audio_name; + char *subtype_name; +}; + struct mgcp_rtp_end { /* statistics */ unsigned int packets; @@ -77,17 +88,14 @@ struct mgcp_rtp_end { /* in network byte order */ int rtp_port, rtcp_port; + /* audio codec information */ + struct mgcp_rtp_codec codec; + struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ + /* 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; int force_output_ptime; diff --git a/openbsc/include/openbsc/rtp.h b/openbsc/include/openbsc/rtp.h index 451d0defa..718fa84e6 100644 --- a/openbsc/include/openbsc/rtp.h +++ b/openbsc/include/openbsc/rtp.h @@ -35,4 +35,5 @@ struct rtp_hdr { uint16_t sequence; uint32_t timestamp; uint32_t ssrc; + uint8_t data[0]; } __attribute__((packed)); diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 7dcf3f3ae..587d4e88c 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -239,7 +239,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } else { - tsdelta = rtp_end->rate * 20 / 1000; + tsdelta = rtp_end->codec.rate * 20 / 1000; LOGP(DMGCP, LOGL_NOTICE, "Fixed packet duration and last timestamp delta " "are not available on 0x%x, " @@ -326,8 +326,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, /* 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; + *payload_type = endp->bts_end.codec.payload_type; + *audio_name = endp->bts_end.codec.audio_name; *fmtp_extra = endp->bts_end.fmtp_extra; } @@ -348,7 +348,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta uint16_t seq, udelta; uint32_t timestamp, ssrc; struct rtp_hdr *rtp_hdr; - int payload = rtp_end->payload_type; + int payload = rtp_end->codec.payload_type; if (len < sizeof(*rtp_hdr)) return; @@ -356,7 +356,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta rtp_hdr = (struct rtp_hdr *) data; seq = ntohs(rtp_hdr->sequence); timestamp = ntohl(rtp_hdr->timestamp); - arrival_time = get_current_ts(rtp_end->rate); + arrival_time = get_current_ts(rtp_end->codec.rate); ssrc = ntohl(rtp_hdr->ssrc); transit = arrival_time - timestamp; @@ -380,7 +380,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); if (state->packet_duration == 0) { - state->packet_duration = rtp_end->rate * 20 / 1000; + state->packet_duration = rtp_end->codec.rate * 20 / 1000; LOGP(DMGCP, LOGL_NOTICE, "Fixed packet duration is not available on 0x%x, " "using fixed 20ms instead: %d from %s:%d in %d\n", diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index ae275a890..a728b67c4 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -598,20 +598,20 @@ 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, +static int set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name) { - int rate = rtp->rate; - int channels = rtp->channels; + int rate = codec->rate; + int channels = codec->channels; char audio_codec[64]; - talloc_free(rtp->subtype_name); - rtp->subtype_name = NULL; - talloc_free(rtp->audio_name); - rtp->audio_name = NULL; + talloc_free(codec->subtype_name); + codec->subtype_name = NULL; + talloc_free(codec->audio_name); + codec->audio_name = NULL; if (payload_type != PTYPE_UNDEFINED) - rtp->payload_type = payload_type; + codec->payload_type = payload_type; if (!audio_name) { switch (payload_type) { @@ -630,17 +630,17 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, audio_codec, &rate, &channels) < 1) return -EINVAL; - rtp->rate = rate; - rtp->channels = channels; - rtp->subtype_name = talloc_strdup(ctx, audio_codec); - rtp->audio_name = talloc_strdup(ctx, audio_name); + codec->rate = rate; + codec->channels = channels; + codec->subtype_name = talloc_strdup(ctx, audio_codec); + codec->audio_name = talloc_strdup(ctx, audio_name); if (!strcmp(audio_codec, "G729")) { - rtp->frame_duration_num = 10; - rtp->frame_duration_den = 1000; + codec->frame_duration_num = 10; + codec->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; + codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; + codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; } if (payload_type < 0) { @@ -654,7 +654,7 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, payload_type = 18; } - rtp->payload_type = payload_type; + codec->payload_type = payload_type; } if (channels != 1) @@ -738,7 +738,9 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) { char *line; int found_media = 0; + /* TODO/XXX make it more generic */ int audio_payload = -1; + int audio_payload_alt = -1; for_each_line(line, p->save) { switch (line[0]) { @@ -758,10 +760,12 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { - if (payload != audio_payload) - break; - - set_audio_info(p->cfg, rtp, payload, audio_name); + if (payload == audio_payload) + set_audio_info(p->cfg, &rtp->codec, + payload, audio_name); + else if (payload == audio_payload_alt) + set_audio_info(p->cfg, &rtp->alt_codec, + payload, audio_name); } else if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) { if (ptime2 > 0 && ptime2 != ptime) @@ -769,23 +773,29 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) else rtp->packet_duration_ms = ptime; } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { - if (ptime2 * rtp->frame_duration_den > - rtp->frame_duration_num * 1500) + /* TODO/XXX: Store this per codec and derive it on use */ + if (ptime2 * rtp->codec.frame_duration_den > + rtp->codec.frame_duration_num * 1500) /* more than 1 frame */ rtp->packet_duration_ms = 0; } break; } case 'm': { - int port; + int port, rc; audio_payload = -1; + audio_payload_alt = -1; - if (sscanf(line, "m=audio %d RTP/AVP %d", - &port, &audio_payload) == 2) { + rc = sscanf(line, "m=audio %d RTP/AVP %d %d", + &port, &audio_payload, &audio_payload_alt); + if (rc >= 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); found_media = 1; - set_audio_info(p->cfg, rtp, audio_payload, NULL); + set_audio_info(p->cfg, &rtp->codec, audio_payload, NULL); + if (rc == 3) + set_audio_info(p->cfg, &rtp->alt_codec, + audio_payload_alt, NULL); } break; } @@ -814,8 +824,8 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) LOGP(DMGCP, LOGL_NOTICE, "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", + ntohs(rtp->rtp_port), rtp->codec.payload_type, + rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", rtp->packet_duration_ms, inet_ntoa(rtp->addr)); return found_media; @@ -872,13 +882,13 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, /* Get the number of frames per channel and packet */ if (rtp->frames_per_packet) f = rtp->frames_per_packet; - else if (rtp->packet_duration_ms && rtp->frame_duration_num) { - int den = 1000 * rtp->frame_duration_num; - f = (rtp->packet_duration_ms * rtp->frame_duration_den + den/2) + else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) { + int den = 1000 * rtp->codec.frame_duration_num; + f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2) / den; } - return rtp->rate * f * rtp->frame_duration_num / rtp->frame_duration_den; + return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den; } static int mgcp_parse_osmux_cid(const char *line) @@ -1025,13 +1035,13 @@ mgcp_header_done: endp->allocated = 1; /* set up RTP media parameters */ - set_audio_info(p->cfg, &endp->bts_end, tcfg->audio_payload, tcfg->audio_name); + set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name); endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, 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, + set_audio_info(p->cfg, &endp->net_end.codec, PTYPE_UNDEFINED, endp->local_options.codec); if (p->cfg->bts_force_ptime) { @@ -1147,7 +1157,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) local_options); if (!have_sdp && endp->local_options.codec) - set_audio_info(p->cfg, &endp->net_end, + set_audio_info(p->cfg, &endp->net_end.codec, PTYPE_UNDEFINED, endp->local_options.codec); setup_rtp_processing(endp); @@ -1449,6 +1459,19 @@ struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) return NULL; } +static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) +{ + codec->payload_type = -1; + talloc_free(codec->subtype_name); + codec->subtype_name = NULL; + talloc_free(codec->audio_name); + codec->audio_name = NULL; + codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; + codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; + codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; + codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; +} + static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) { if (end->local_alloc == PORT_ALLOC_DYNAMIC) { @@ -1461,25 +1484,19 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) end->dropped_packets = 0; memset(&end->addr, 0, sizeof(end->addr)); end->rtp_port = end->rtcp_port = 0; - end->payload_type = -1; 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; /* Set default values */ - end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; - end->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; 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; + + mgcp_rtp_codec_reset(&end->codec); + mgcp_rtp_codec_reset(&end->alt_codec); } static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c index 4d4cec8a2..38daeb8ae 100644 --- a/openbsc/src/libmgcp/mgcp_transcode.c +++ b/openbsc/src/libmgcp/mgcp_transcode.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -44,22 +45,22 @@ int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) 1) * state->src_frame_size; } -static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) +static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec) { - if (rtp_end->subtype_name) { - if (!strcmp("GSM", rtp_end->subtype_name)) + if (codec->subtype_name) { + if (!strcmp("GSM", codec->subtype_name)) return AF_GSM; - if (!strcmp("PCMA", rtp_end->subtype_name)) + if (!strcmp("PCMA", codec->subtype_name)) return AF_PCMA; #ifdef HAVE_BCG729 - if (!strcmp("G729", rtp_end->subtype_name)) + if (!strcmp("G729", codec->subtype_name)) return AF_G729; #endif - if (!strcmp("L16", rtp_end->subtype_name)) + if (!strcmp("L16", codec->subtype_name)) return AF_L16; } - switch (rtp_end->payload_type) { + switch (codec->payload_type) { case 3 /* GSM */: return AF_GSM; case 8 /* PCMA */: @@ -140,6 +141,8 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp, { struct mgcp_process_rtp_state *state; enum audio_format src_fmt, dst_fmt; + const struct mgcp_rtp_codec *src_codec = &src_end->codec; + const struct mgcp_rtp_codec *dst_codec = &dst_end->codec; /* cleanup first */ if (dst_end->rtp_process_data) { @@ -150,34 +153,34 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp, if (!src_end) return 0; - src_fmt = get_audio_format(src_end); - dst_fmt = get_audio_format(dst_end); + src_fmt = get_audio_format(src_codec); + dst_fmt = get_audio_format(dst_codec); 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); + src_codec->subtype_name, src_codec->payload_type, + dst_codec->subtype_name, dst_codec->payload_type); if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { - if (!src_end->subtype_name || !dst_end->subtype_name) + if (!src_codec->subtype_name || !dst_codec->subtype_name) /* Not enough info, do nothing */ return 0; - if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) + if (strcmp(src_codec->subtype_name, dst_codec->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); + src_codec->audio_name, dst_codec->audio_name); return -EINVAL; } - if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { + if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) { LOGP(DMGCP, LOGL_ERROR, "Cannot transcode: rate conversion (%d -> %d) not supported.\n", - src_end->rate, dst_end->rate); + src_codec->rate, dst_codec->rate); return -EINVAL; } @@ -268,8 +271,8 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp, "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); + src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra, + dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra); return 0; } @@ -280,16 +283,19 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, 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; + struct mgcp_rtp_codec *net_codec = &endp->net_end.codec; + struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec; + + if (!state || net_codec->payload_type < 0) { + *payload_type = bts_codec->payload_type; + *audio_name = bts_codec->audio_name; *fmtp_extra = endp->bts_end.fmtp_extra; return; } - *payload_type = endp->net_end.payload_type; + *payload_type = net_codec->payload_type; + *audio_name = net_codec->audio_name; *fmtp_extra = endp->net_end.fmtp_extra; - *audio_name = endp->net_end.audio_name; } static int decode_audio(struct mgcp_process_rtp_state *state, @@ -391,13 +397,65 @@ static int encode_audio(struct mgcp_process_rtp_state *state, return nbytes; } +static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end) +{ + if (&endp->bts_end == dst_end) + return &endp->net_end; + else if (&endp->net_end == dst_end) + return &endp->bts_end; + OSMO_ASSERT(0); +} + +/* + * With some modems we get offered multiple codecs + * and we have selected one of them. It might not + * be the right one and we need to detect this with + * the first audio packets. One difficulty is that + * we patch the rtp payload type in place, so we + * need to discuss this. + */ +struct mgcp_process_rtp_state *check_transcode_state( + struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct rtp_hdr *rtp_hdr) +{ + struct mgcp_rtp_end *src_end; + + /* Only deal with messages from net to bts */ + if (&endp->bts_end != dst_end) + goto done; + + src_end = source_for_dest(endp, dst_end); + + /* Already patched */ + if (rtp_hdr->payload_type == dst_end->codec.payload_type) + goto done; + /* The payload we expect */ + if (rtp_hdr->payload_type == src_end->codec.payload_type) + goto done; + /* The matching alternate payload type? Then switch */ + if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) { + struct mgcp_config *cfg = endp->cfg; + struct mgcp_rtp_codec tmp_codec = src_end->alt_codec; + src_end->alt_codec = src_end->codec; + src_end->codec = tmp_codec; + cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); + cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); + } + +done: + return dst_end->rtp_process_data; +} + 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; + struct mgcp_process_rtp_state *state; + const size_t rtp_hdr_size = sizeof(struct rtp_hdr); + struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data; + char *payload_data = (char *) &rtp_hdr->data[0]; int payload_len = *len - rtp_hdr_size; uint8_t *src = (uint8_t *)payload_data; uint8_t *dst = (uint8_t *)payload_data; @@ -407,6 +465,7 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, uint32_t ts_no; int rc; + state = check_transcode_state(endp, dst_end, rtp_hdr); if (!state) return 0; @@ -427,9 +486,9 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, /* TODO: check payload type (-> G.711 comfort noise) */ if (payload_len > 0) { - ts_no = ntohl(*(uint32_t*)(data+4)); + ts_no = ntohl(rtp_hdr->timestamp); if (!state->is_running) { - state->next_seq = ntohs(*(uint16_t*)(data+2)); + state->next_seq = ntohs(rtp_hdr->sequence); state->next_time = ts_no; state->is_running = 1; } @@ -504,8 +563,8 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, nsamples -= state->sample_cnt; *len = rtp_hdr_size + rc; - *(uint16_t*)(data+2) = htons(state->next_seq); - *(uint32_t*)(data+4) = htonl(ts_no); + rtp_hdr->sequence = htons(state->next_seq); + rtp_hdr->timestamp = htonl(ts_no); state->next_seq += 1; state->next_time = ts_no + nsamples; diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 9ae0bc9fa..b29eb6b4d 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -147,6 +147,8 @@ static int config_write_mgcp(struct vty *vty) static void dump_rtp_end(const char *end_name, struct vty *vty, struct mgcp_rtp_state *state, struct mgcp_rtp_end *end) { + struct mgcp_rtp_codec *codec = &end->codec; + vty_out(vty, " %s%s" " Timestamp Errs: %d->%d%s" @@ -160,10 +162,10 @@ static void dump_rtp_end(const char *end_name, struct vty *vty, state->in_stream.err_ts_counter, state->out_stream.err_ts_counter, VTY_NEWLINE, end->dropped_packets, VTY_NEWLINE, - end->payload_type, end->rate, end->channels, VTY_NEWLINE, - end->frame_duration_num, end->frame_duration_den, VTY_NEWLINE, + codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, + codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms, VTY_NEWLINE, - end->fmtp_extra, end->audio_name, end->subtype_name, VTY_NEWLINE, + end->fmtp_extra, codec->audio_name, codec->subtype_name, VTY_NEWLINE, end->output_enabled, end->force_output_ptime, VTY_NEWLINE); } diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c index d58039788..97593031c 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c +++ b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c @@ -552,7 +552,7 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, sccp->bsc_endp, nat->mgcp_cfg->source_addr, mgcp_endp->bts_end.local_port, osmux_cid, - &mgcp_endp->net_end.payload_type); + &mgcp_endp->net_end.codec.payload_type); if (!bsc_msg) { LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n"); return MGCP_POLICY_CONT; @@ -746,7 +746,7 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg) output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1, bsc->nat->mgcp_cfg->source_addr, endp->net_end.local_port, -1, - &endp->bts_end.payload_type); + &endp->bts_end.codec.payload_type); if (!output) { LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n"); return; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 50c8b2d0a..a0575032c 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -1,6 +1,6 @@ /* - * (C) 2011-2012 by Holger Hans Peter Freyther - * (C) 2011-2012 by On-Waves + * (C) 2011-2012,2014 by Holger Hans Peter Freyther + * (C) 2011-2012,2014 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -268,6 +268,61 @@ static void test_strline(void) #define PTYPE_NONE 128 #define PTYPE_NYI PTYPE_NONE +#define CRCX_MULT_1 "CRCX 2 1@mgw MGCP 1.0\r\n" \ + "M: recvonly\r\n" \ + "C: 2\r\n" \ + "X\r\n" \ + "L: p:20\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP 18 97\r\n"\ + "a=rtpmap:18 G729/8000\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=ptime:40\r\n" + +#define CRCX_MULT_2 "CRCX 2 2@mgw MGCP 1.0\r\n" \ + "M: recvonly\r\n" \ + "C: 2\r\n" \ + "X\r\n" \ + "L: p:20\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP 18 97 101\r\n"\ + "a=rtpmap:18 G729/8000\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=rtpmap:101 FOO/8000\r\n" \ + "a=ptime:40\r\n" + +#define CRCX_MULT_3 "CRCX 2 3@mgw MGCP 1.0\r\n" \ + "M: recvonly\r\n" \ + "C: 2\r\n" \ + "X\r\n" \ + "L: p:20\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP\r\n" \ + "a=rtpmap:18 G729/8000\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=rtpmap:101 FOO/8000\r\n" \ + "a=ptime:40\r\n" + +#define CRCX_MULT_4 "CRCX 2 4@mgw MGCP 1.0\r\n" \ + "M: recvonly\r\n" \ + "C: 2\r\n" \ + "X\r\n" \ + "L: p:20\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP 18\r\n" \ + "a=rtpmap:18 G729/8000\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=rtpmap:101 FOO/8000\r\n" \ + "a=ptime:40\r\n" + struct mgcp_test { const char *name; const char *req; @@ -410,7 +465,7 @@ static void test_messages(void) /* reset endpoints */ for (i = 0; i < cfg->trunk.number_endpoints; i++) { endp = &cfg->trunk.endpoints[i]; - endp->net_end.payload_type = PTYPE_NONE; + endp->net_end.codec.payload_type = PTYPE_NONE; endp->net_end.packet_duration_ms = -1; OSMO_ASSERT(endp->conn_mode == MGCP_CONN_NONE); @@ -498,18 +553,18 @@ static void test_messages(void) fprintf(stderr, "endpoint %d: " "payload type BTS %d (exp %d), NET %d (exp %d)\n", last_endpoint, - endp->bts_end.payload_type, t->exp_bts_ptype, - endp->net_end.payload_type, t->exp_net_ptype); + endp->bts_end.codec.payload_type, t->exp_bts_ptype, + endp->net_end.codec.payload_type, t->exp_net_ptype); if (t->exp_bts_ptype != PTYPE_IGNORE) - OSMO_ASSERT(endp->bts_end.payload_type == + OSMO_ASSERT(endp->bts_end.codec.payload_type == t->exp_bts_ptype); if (t->exp_net_ptype != PTYPE_IGNORE) - OSMO_ASSERT(endp->net_end.payload_type == + OSMO_ASSERT(endp->net_end.codec.payload_type == t->exp_net_ptype); /* Reset them again for next test */ - endp->net_end.payload_type = PTYPE_NONE; + endp->net_end.codec.payload_type = PTYPE_NONE; } } @@ -830,7 +885,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) mgcp_initialize_endp(&endp); - rtp->payload_type = 98; + rtp->codec.payload_type = 98; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; @@ -877,6 +932,72 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) force_monotonic_time_us = -1; } +static void test_multilple_codec(void) +{ + struct mgcp_config *cfg; + struct mgcp_endpoint *endp; + struct msgb *inp, *resp; + + printf("Testing multiple payload types\n"); + + cfg = mgcp_config_alloc(); + cfg->trunk.number_endpoints = 64; + mgcp_endpoints_allocate(&cfg->trunk); + cfg->policy_cb = mgcp_test_policy_cb; + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); + + /* Allocate endpoint 1@mgw with two codecs */ + last_endpoint = -1; + inp = create_msg(CRCX_MULT_1); + resp = mgcp_handle_message(cfg, inp); + msgb_free(inp); + msgb_free(resp); + + OSMO_ASSERT(last_endpoint == 1); + endp = &cfg->trunk.endpoints[last_endpoint]; + OSMO_ASSERT(endp->net_end.codec.payload_type == 18); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 97); + + /* Allocate 2@mgw with three codecs, last one ignored */ + last_endpoint = -1; + inp = create_msg(CRCX_MULT_2); + resp = mgcp_handle_message(cfg, inp); + msgb_free(inp); + msgb_free(resp); + + OSMO_ASSERT(last_endpoint == 2); + endp = &cfg->trunk.endpoints[last_endpoint]; + OSMO_ASSERT(endp->net_end.codec.payload_type == 18); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 97); + + /* Allocate 3@mgw with no codecs, check for PT == -1 */ + last_endpoint = -1; + inp = create_msg(CRCX_MULT_3); + resp = mgcp_handle_message(cfg, inp); + msgb_free(inp); + msgb_free(resp); + + OSMO_ASSERT(last_endpoint == 3); + endp = &cfg->trunk.endpoints[last_endpoint]; + OSMO_ASSERT(endp->net_end.codec.payload_type == -1); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); + + /* Allocate 4@mgw with a single codec */ + last_endpoint = -1; + inp = create_msg(CRCX_MULT_4); + resp = mgcp_handle_message(cfg, inp); + msgb_free(inp); + msgb_free(resp); + + OSMO_ASSERT(last_endpoint == 4); + endp = &cfg->trunk.endpoints[last_endpoint]; + OSMO_ASSERT(endp->net_end.codec.payload_type == 18); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); + + + talloc_free(cfg); +} + int main(int argc, char **argv) { osmo_init_logging(&log_info); @@ -892,6 +1013,7 @@ int main(int argc, char **argv) test_packet_error_detection(0, 0); test_packet_error_detection(0, 1); test_packet_error_detection(1, 1); + test_multilple_codec(); printf("Done\n"); return EXIT_SUCCESS; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 033f783f3..a56a3fd1b 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -474,4 +474,5 @@ Stats: Jitter = 0, Transit = -144000 In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 +Testing multiple payload types Done diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c index 44f307251..cf679b356 100644 --- a/openbsc/tests/mgcp/mgcp_transcoding_test.c +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c @@ -174,6 +174,9 @@ static int given_configured_endpoint(int in_samples, int out_samples, tcfg = talloc_zero(cfg, struct mgcp_trunk_config); endp = talloc_zero(tcfg, struct mgcp_endpoint); + 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; tcfg->endpoints = endp; tcfg->number_endpoints = 1; @@ -183,14 +186,14 @@ static int given_configured_endpoint(int in_samples, int out_samples, mgcp_initialize_endp(endp); dst_end = &endp->bts_end; - dst_end->payload_type = audio_name_to_type(dstfmt); + dst_end->codec.payload_type = audio_name_to_type(dstfmt); src_end = &endp->net_end; - src_end->payload_type = audio_name_to_type(srcfmt); + src_end->codec.payload_type = audio_name_to_type(srcfmt); if (out_samples) { - dst_end->frame_duration_den = dst_end->rate; - dst_end->frame_duration_num = out_samples; + dst_end->codec.frame_duration_den = dst_end->codec.rate; + dst_end->codec.frame_duration_num = out_samples; dst_end->frames_per_packet = 1; dst_end->force_output_ptime = 1; } @@ -433,6 +436,63 @@ static void test_transcode_result(void) } } +static void test_transcode_change(void) +{ + char buf[4096] = {0x80, 0}; + void *ctx; + + struct mgcp_endpoint *endp; + struct mgcp_process_rtp_state *state; + struct rtp_hdr *hdr; + + int len, res; + + { + /* from GSM to PCMA and same ptime */ + printf("Testing Initial G729->GSM, PCMA->GSM\n"); + given_configured_endpoint(160, 0, "g729", "gsm", &ctx, &endp); + endp->net_end.alt_codec = endp->net_end.codec; + endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma"); + state = endp->bts_end.rtp_process_data; + + /* initial transcoding work */ + OSMO_ASSERT(state->src_fmt == AF_G729); + OSMO_ASSERT(state->dst_fmt == AF_GSM); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8); + OSMO_ASSERT(endp->net_end.codec.payload_type == 18); + + /* result */ + len = audio_packets_pcma[0].len; + memcpy(buf, audio_packets_pcma[0].data, len); + res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); + OSMO_ASSERT(res == sizeof(struct rtp_hdr)); + OSMO_ASSERT(state->sample_cnt == 0); + OSMO_ASSERT(state->src_fmt == AF_PCMA); + OSMO_ASSERT(state->dst_fmt == AF_GSM); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 18); + OSMO_ASSERT(endp->net_end.codec.payload_type == 8); + + len = res; + res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); + OSMO_ASSERT(res == -ENOMSG); + + + /* now check that comfort noise doesn't change anything */ + len = audio_packets_pcma[1].len; + memcpy(buf, audio_packets_pcma[1].data, len); + hdr = (struct rtp_hdr *) buf; + hdr->payload_type = 11; + res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); + OSMO_ASSERT(state->sample_cnt == 80); + OSMO_ASSERT(state->src_fmt == AF_PCMA); + OSMO_ASSERT(state->dst_fmt == AF_GSM); + OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 18); + OSMO_ASSERT(endp->net_end.codec.payload_type == 8); + + talloc_free(ctx); + } +} + static int test_repacking(int in_samples, int out_samples, int no_transcode) { char buf[4096] = {0x80, 0}; @@ -463,7 +523,7 @@ static int test_repacking(int in_samples, int out_samples, int no_transcode) out_size = mgcp_transcoding_get_frame_size(state, -1, 1); OSMO_ASSERT(sizeof(buf) >= out_size + 12); - buf[1] = endp->net_end.payload_type; + buf[1] = endp->net_end.codec.payload_type; *(uint16_t*)(buf+2) = htons(1); *(uint32_t*)(buf+4) = htonl(0); *(uint32_t*)(buf+8) = htonl(0xaabbccdd); @@ -582,6 +642,7 @@ int main(int argc, char **argv) test_repacking(160, 100, 1); test_rtp_seq_state(); test_transcode_result(); + test_transcode_change(); return 0; } diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.ok b/openbsc/tests/mgcp/mgcp_transcoding_test.ok index 7c1c8cebd..5cfc2897e 100644 --- a/openbsc/tests/mgcp/mgcp_transcoding_test.ok +++ b/openbsc/tests/mgcp/mgcp_transcoding_test.ok @@ -536,3 +536,4 @@ got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 +Testing Initial G729->GSM, PCMA->GSM