From 844876f8d5b3be161b6694e274692679608bad50 Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Fri, 13 Jul 2018 09:17:07 +0200 Subject: codec_pref: move match_codec_pref() to separate c-file and add unit-test At the moment there are three sources that may advertise a list of supported audio codec/rate settings. There is the MS that advertises advertises a speech codec list and the MSC that sends a channel type information element over A and there are also settings in the bsc configuration file that may restrict the codec/rate types that are allowed to use. The function match_codec_pref() looks at all of the three buckets and selects a codec that satisfies all three. This is already a somewhat complicated process, overit is very isolated, so lets give it its own c-file. Due to the lack of unit-tests it is very hard to make changes here so lets add also unit-test to make sure that regressions are catched early. - Put match_codec_pref() and all its helper functions into a separate c-file. - Add a unit test. Change-Id: Iabedfdcec8b99a319f2d57cbea45c5e36c7b6e29 Related: OS#3361 --- src/osmo-bsc/codec_pref.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/osmo-bsc/codec_pref.c (limited to 'src/osmo-bsc/codec_pref.c') diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c new file mode 100644 index 000000000..ee0760a59 --- /dev/null +++ b/src/osmo-bsc/codec_pref.c @@ -0,0 +1,175 @@ +/* + * (C) 2017-2018 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * 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 + +/* Helper function for match_codec_pref(), looks up a matching chan mode for + * a given permitted speech value */ +enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) +{ + switch (speech) { + case GSM0808_PERM_HR1: + case GSM0808_PERM_FR1: + return GSM48_CMODE_SPEECH_V1; + break; + case GSM0808_PERM_HR2: + case GSM0808_PERM_FR2: + return GSM48_CMODE_SPEECH_EFR; + break; + case GSM0808_PERM_HR3: + case GSM0808_PERM_FR3: + return GSM48_CMODE_SPEECH_AMR; + break; + default: + LOGP(DMSC, LOGL_FATAL, "Unsupported permitted speech selected, assuming AMR as channel mode...\n"); + return GSM48_CMODE_SPEECH_AMR; + } +} + +/* Helper function for match_codec_pref(), looks up a matching permitted speech + * value for a given msc audio codec pref */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) +{ + if (audio->hr) { + switch (audio->ver) { + case 1: + return GSM0808_PERM_HR1; + break; + case 2: + return GSM0808_PERM_HR2; + break; + case 3: + return GSM0808_PERM_HR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: hr%d, using hr1 instead\n", audio->ver); + return GSM0808_PERM_HR1; + } + } else { + switch (audio->ver) { + case 1: + return GSM0808_PERM_FR1; + break; + case 2: + return GSM0808_PERM_FR2; + break; + case 3: + return GSM0808_PERM_FR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: fr%d, using fr1 instead\n", audio->ver); + return GSM0808_PERM_FR1; + } + } +} + +/* Helper function for match_codec_pref(), tests if a given audio support + * matches one of the permitted speech settings of the channel type element. + * The matched permitted speech value is then also compared against the + * speech codec list. (optional, only relevant for AoIP) */ +static bool test_codec_pref(const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, uint8_t perm_spch) +{ + unsigned int i; + bool match = false; + struct gsm0808_speech_codec sc; + int rc; + + /* Try to find the given permitted speech value in the + * codec list of the channel type element */ + for (i = 0; i < ct->perm_spch_len; i++) { + if (ct->perm_spch[i] == perm_spch) { + match = true; + break; + } + } + + /* If we do not have a speech codec list to test against, + * we just exit early (will be always the case in non-AoIP networks) */ + if (!scl) + return match; + + /* If we failed to match until here, there is no + * point in testing further */ + if (match == false) + return false; + + /* Extrapolate speech codec data */ + rc = gsm0808_speech_codec_from_chan_type(&sc, perm_spch); + if (rc < 0) + return false; + + /* Try to find extrapolated speech codec data in + * the speech codec list */ + for (i = 0; i < scl->len; i++) { + if (sc.type == scl->codec[i].type) + return true; + } + + return false; +} + +/*! Helper function for bssmap_handle_assignm_req(), matches the codec + * preferences from the MSC with the codec preferences + * \param[out] full_rate '1' if full-rate, '0' if half-rate, '-1' if no match + * \param[out] chan_mode GSM 04.08 channel mode + * \param[in] ct GSM 08.08 channel type + * \param[in] scl GSM 08.08 speech codec list + * \param[in] msc MSC data [for configuration] + * \returns 0 on success, -1 in case no match was found */ +int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, + const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, const struct bsc_msc_data *msc) +{ + unsigned int i; + uint8_t perm_spch; + bool match = false; + + for (i = 0; i < msc->audio_length; i++) { + perm_spch = audio_support_to_gsm88(msc->audio_support[i]); + if (test_codec_pref(ct, scl, perm_spch)) { + match = true; + break; + } + } + + /* Exit without result, in case no match can be deteched */ + if (!match) { + *full_rate = -1; + *chan_mode = GSM48_CMODE_SIGN; + return -1; + } + + /* Check if the result is a half or full rate codec */ + if (perm_spch == GSM0808_PERM_HR1 || perm_spch == GSM0808_PERM_HR2 || perm_spch == GSM0808_PERM_HR3 + || perm_spch == GSM0808_PERM_HR4 || perm_spch == GSM0808_PERM_HR6) + *full_rate = 0; + else + *full_rate = 1; + + /* Lookup a channel mode for the selected codec */ + *chan_mode = gsm88_to_chan_mode(perm_spch); + + return 0; +} -- cgit v1.2.3