From acce44d40fd11bf1ab843f54c71c131af1da4aa9 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Fri, 31 Dec 2021 00:31:58 +0100 Subject: [PATCH] amr: Introduce APIs to convert BE to IuUP/IuFP format These APIs allow for easy re-formatting of received AMR to forward between regular RTP and IuUP. Related: OS#1937 Change-Id: Id2bd32d5f2060abe581730996dc4251381bf7d4f --- include/osmocom/netif/amr.h | 4 +++ src/amr.c | 71 +++++++++++++++++++++++++++++++++++++ tests/amr/amr_test.c | 59 ++++++++++++++++++++++++++++++ tests/amr/amr_test.ok | 6 ++++ 4 files changed, 140 insertions(+) diff --git a/include/osmocom/netif/amr.h b/include/osmocom/netif/amr.h index 1a90af9..30e263c 100644 --- a/include/osmocom/netif/amr.h +++ b/include/osmocom/netif/amr.h @@ -115,5 +115,9 @@ bool osmo_amr_is_oa(uint8_t *payload, unsigned int payload_len); int osmo_amr_oa_to_bwe(uint8_t *payload, unsigned int payload_len); int osmo_amr_bwe_to_oa(uint8_t *payload, unsigned int payload_len, unsigned int payload_maxlen); +int osmo_amr_bwe_to_iuup(uint8_t *payload, unsigned int payload_len); +int osmo_amr_iuup_to_bwe(uint8_t *payload, unsigned int payload_len, + unsigned int payload_maxlen); +int osmo_amr_bytes_to_ft(size_t bytes); #endif diff --git a/src/amr.c b/src/amr.c index 0cd1821..1cb2ccc 100644 --- a/src/amr.c +++ b/src/amr.c @@ -63,6 +63,17 @@ size_t osmo_amr_bytes(uint8_t amr_ft) return amr_ft_to_bytes[amr_ft]; } +int osmo_amr_bytes_to_ft(size_t bytes) +{ + int ft; + + for (ft = 0; ft < AMR_FT_MAX; ft++) { + if (amr_ft_to_bytes[ft] == bytes) + return ft; + } + return -1; +} + int osmo_amr_ft_valid(uint8_t amr_ft) { /* @@ -207,3 +218,63 @@ int osmo_amr_bwe_to_oa(uint8_t *payload, unsigned int payload_len, memcpy(payload, buf, oa_payload_len); return oa_payload_len; } + +/*! Convert an AMR frame from bandwith-efficient mode to IuuP/IuFP payload. + * The IuuP/IuPF payload only contains the class a, b, c bits. No header. + * \param[inout] payload user provided memory containing the AMR payload. + * \param[in] payload_len overall length of the AMR payload. + * \param[in] payload_maxlen maximum length of the user provided memory. + * \returns resulting payload length, negative on error. */ +int osmo_amr_bwe_to_iuup(uint8_t *payload, unsigned int payload_len) +{ + /* The header is only valid after shifting first two bytes to OA mode */ + unsigned int i; + unsigned int amr_speech_len; + uint8_t ft; + + if (payload_len < 2) + return -1; + + /* Calculate new payload length */ + ft = (payload[0] & 0xf0) >> 4; + if (!osmo_amr_ft_valid(ft)) + return -1; + + amr_speech_len = osmo_amr_bytes(ft); + if (payload_len < amr_speech_len + 2) + return -1; + + for (i = 0; i < amr_speech_len; i++) { + /* we have to shift the payload by 10 bits to get only the Class A, B, C bits */ + payload[i] = (payload[i + 1] << 2) | ((payload[i + 2]) >> 6); + } + + return amr_speech_len; +} + +/*! Convert an AMR frame from IuuP/IuFP payload to bandwith-efficient mode. + * The IuuP/IuPF payload only contains the class a, b, c bits. No header. + * The resulting buffer has space at the start prepared to be filled by CMR, TOC. + * \param[inout] payload user provided memory containing the AMR payload. + * \param[in] payload_len overall length of the AMR payload. + * \param[in] payload_maxlen maximum length of the user provided memory (payload_len + 2 required). + * \returns resulting payload length, negative on error. */ +int osmo_amr_iuup_to_bwe(uint8_t *payload, unsigned int payload_len, + unsigned int payload_maxlen) +{ + /* shift all bits by 10 */ + int i; + + if (payload_maxlen < payload_len + 2) + return -1; + + i = payload_len + 1; + payload[i] = (payload[i - 2] << 6); + for (i = payload_len; i >= 2; i--) { + /* we have to shift the payload by 10 bits to get only the Class A, B, C bits */ + payload[i] = (payload[i - 1] >> 2) | (payload[i - 2] << 6); + } + payload[i] = (payload[i - 1] >> 2); + payload[0] = 0; + return payload_len + 2; +} diff --git a/tests/amr/amr_test.c b/tests/amr/amr_test.c index fabf948..fc579e9 100644 --- a/tests/amr/amr_test.c +++ b/tests/amr/amr_test.c @@ -63,6 +63,15 @@ char *bwe_amr_samples[] = { "END", }; +/* Some IuUP payload containg AMR */ +char *iuup_amr_samples[] = { + "7a687b8b29ba02013d3cb863a5af4206869618fb5336d504968d7541663210", + "4482803977dc1880b56aab097728a6bbbb48a57e3affe41125838a5ce65fc0", + "2683c1d26e", + "END", +}; + + void dump_bits(uint8_t *buf, int len) { unsigned int i; @@ -185,6 +194,55 @@ void osmo_amr_oa_to_bwe_and_inverse_test(void) } } +void osmo_amr_iuup_to_bwe_and_inverse_test(void) +{ + uint8_t buf[256]; + uint8_t buf_chk[256]; + unsigned int ft; + + unsigned int i = 0; + int len; + int rc; + + printf("\n\n"); + printf + ("Testing conversion from IuUP to bw-efficient and inverse:\n"); + + while (1) { + if (strcmp(iuup_amr_samples[i], "END") == 0) + return; + printf("Sample No.: %d...", i); + + len = osmo_hexparse(iuup_amr_samples[i], buf, sizeof(buf)); + OSMO_ASSERT(len > 0); + i++; + + ft = osmo_amr_bytes_to_ft(len); + if (ft < 0) { + printf(" skipping a sample with a wrong FT\n"); + continue; + } + printf(" AMR mode: %d, IuUP: %d bytes,", ft, len); + memcpy(buf_chk, buf, sizeof(buf)); + + rc = osmo_amr_iuup_to_bwe(buf, len, sizeof(buf)); + OSMO_ASSERT(rc > 0); + OSMO_ASSERT(rc == len + 2); + printf(" BE: %d bytes (%s),", rc, osmo_hexdump(buf, rc)); + + buf[0] = (ft << 4) & 0xf0; + rc = osmo_amr_bwe_to_iuup(buf, rc); + printf(" IuUP: %d bytes\n", rc); + OSMO_ASSERT(rc > 0); + OSMO_ASSERT(len == rc); + if (memcmp(buf, buf_chk, len) != 0) { + printf("Got: %s\n", osmo_hexdump(buf, len)); + printf("Expected:%s\n", osmo_hexdump(buf_chk, len)); + } + OSMO_ASSERT(memcmp(buf, buf_chk, len) == 0); + } +} + void osmo_amr_is_oa_test(void) { uint8_t buf[256]; @@ -239,6 +297,7 @@ int main(int argc, char **argv) osmo_amr_oa_to_bwe_test(); osmo_amr_bwe_to_oa_test(); osmo_amr_oa_to_bwe_and_inverse_test(); + osmo_amr_iuup_to_bwe_and_inverse_test(); osmo_amr_is_oa_test(); fprintf(stdout, "OK: Test passed\n"); diff --git a/tests/amr/amr_test.ok b/tests/amr/amr_test.ok index eea513b..7a762d9 100644 --- a/tests/amr/amr_test.ok +++ b/tests/amr/amr_test.ok @@ -234,6 +234,12 @@ Sample No.: 20... AMR mode: 0, OA: 14 bytes, BE: 14 bytes, OA: 14 bytes Sample No.: 21... skipping a sample with a wrong FT +Testing conversion from IuUP to bw-efficient and inverse: +Sample No.: 0... AMR mode: 7, IuUP: 31 bytes, BE: 33 bytes (00 1e 9a 1e e2 ca 6e 80 80 4f 4f 2e 18 e9 6b d0 81 a1 a5 86 3e d4 cd b5 41 25 a3 5d 50 59 8c 84 00 ), IuUP: 31 bytes +Sample No.: 1... AMR mode: 7, IuUP: 31 bytes, BE: 33 bytes (00 11 20 a0 0e 5d f7 06 20 2d 5a aa c2 5d ca 29 ae ee d2 29 5f 8e bf f9 04 49 60 e2 97 39 97 f0 00 ), IuUP: 31 bytes +Sample No.: 2... AMR mode: 8, IuUP: 5 bytes, BE: 7 bytes (00 09 a0 f0 74 9b 80 ), IuUP: 5 bytes + + Testing detection of octet-aligned mode payloads: Sample No.: 0 ==>octet aligned Sample No.: 1 ==>octet aligned