aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-05-28 11:04:26 +0200
committerHarald Welte <laforge@gnumonks.org>2017-05-28 14:29:59 +0200
commit8b01f0ca05d92c593d9f33e40bae3f6dbb78a521 (patch)
tree1feb93efd06b9d1f920da49bcad764dc1f570b9f
parent494d92c3c7c2197381807f999d9d2a6ba23299e4 (diff)
Add AMR codec support
After merging this change, there is support for the AMR codec (by means of libopencore-amr, which is already used for EFR). In terms of gapk formats, we introdude * the "amr-opencore" format, which serves both as the canonical format, and as the input format to opencore-amrnb itself. * the "rtp-amr" format, which is the payload of RFC4867 octet-aligned mode You can use the following command for a real-time RTP playback for AMR frames: ./gapk -I 0.0.0.0/30000 -f rtp-amr -A default -g rawpcm-s16le
-rw-r--r--include/gapk/codecs.h1
-rw-r--r--include/gapk/formats.h4
-rw-r--r--src/Makefile.am4
-rw-r--r--src/codec_amr.c121
-rw-r--r--src/codecs.c2
-rw-r--r--src/fmt_amr_opencore.c51
-rw-r--r--src/fmt_rtp_amr.c60
-rw-r--r--src/formats.c4
8 files changed, 245 insertions, 2 deletions
diff --git a/include/gapk/codecs.h b/include/gapk/codecs.h
index 00c34b5..f62cb25 100644
--- a/include/gapk/codecs.h
+++ b/include/gapk/codecs.h
@@ -33,6 +33,7 @@ enum codec_type {
CODEC_HR, /* GSM Half Rate codec GSM 06.20 */
CODEC_FR, /* GSM Full Rate codec GSM 06.10 */
CODEC_EFR, /* GSM Enhanced Full Rate codec GSM 06.60 */
+ CODEC_AMR, /* GSM Adaptive Multi Rate codec GSM 26.071 */
_CODEC_MAX,
};
diff --git a/include/gapk/formats.h b/include/gapk/formats.h
index 4b2418a..e010713 100644
--- a/include/gapk/formats.h
+++ b/include/gapk/formats.h
@@ -48,6 +48,10 @@ enum format_type {
FMT_TI_FR,
FMT_TI_EFR,
+ /* AMR encoded data, variable length */
+ FMT_AMR_OPENCORE,
+ FMT_RTP_AMR,
+
_FMT_MAX,
};
diff --git a/src/Makefile.am b/src/Makefile.am
index e33a490..07f1d51 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,8 +6,8 @@ AM_LDFLAGS=$(LIBOSMOCODEC_LIBS) $(LIBOSMOCORE_LIBS) \
COM_SOURCES = procqueue.c pq_file.c pq_format.c pq_codec.c pq_rtp.c pq_alsa.c \
formats.c fmt_amr.c fmt_gsm.c fmt_hr_ref.c fmt_racal.c \
- fmt_rawpcm.c fmt_ti.c benchmark.c \
- codecs.c codec_pcm.c codec_hr.c codec_fr.c codec_efr.c
+ fmt_amr_opencore.c fmt_rtp_amr.c fmt_rawpcm.c fmt_ti.c benchmark.c \
+ codecs.c codec_pcm.c codec_hr.c codec_fr.c codec_efr.c codec_amr.c
bin_PROGRAMS = gapk
diff --git a/src/codec_amr.c b/src/codec_amr.c
new file mode 100644
index 0000000..9922ece
--- /dev/null
+++ b/src/codec_amr.c
@@ -0,0 +1,121 @@
+/* AMR (GSM 06.90) codec */
+/* (C) 2017 Harald Welte <laforge@gnumonks.org> */
+
+/*
+ * This file is part of gapk (GSM Audio Pocket Knife).
+ *
+ * gapk 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gapk 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 gapk. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gapk/codecs.h>
+#include <gapk/benchmark.h>
+
+#include "config.h"
+
+
+#ifdef HAVE_OPENCORE_AMRNB
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <opencore-amrnb/interf_dec.h>
+#include <opencore-amrnb/interf_enc.h>
+
+struct codec_amr_state {
+ void *encoder;
+ void *decoder;
+};
+
+
+static void *
+codec_amr_init(void)
+{
+ struct codec_amr_state *st;
+
+ st = calloc(1, sizeof(*st));
+ if (!st)
+ return NULL;
+
+ st->encoder = Encoder_Interface_init(0);
+ st->decoder = Decoder_Interface_init();
+
+ return (void *)st;
+}
+
+static void
+codec_amr_exit(void *state)
+{
+ struct codec_amr_state *st = state;
+
+ Decoder_Interface_exit(st->decoder);
+ Encoder_Interface_exit(st->encoder);
+
+ return;
+}
+
+static int
+codec_amr_encode(void *state, uint8_t *cod, const uint8_t *pcm, unsigned int pcm_len)
+{
+ struct codec_amr_state *st = state;
+ int rv;
+
+ BENCHMARK_START;
+ rv = Encoder_Interface_Encode(
+ st->encoder,
+ MR122,
+ (const short*) pcm,
+ (unsigned char*) cod,
+ 1
+ );
+ BENCHMARK_STOP(CODEC_EFR, 1);
+
+ return rv;
+}
+
+static int
+codec_amr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod_len)
+{
+ struct codec_amr_state *st = state;
+
+ printf("%s(): %u bytes in\n", __func__, cod_len);
+
+ BENCHMARK_START;
+ Decoder_Interface_Decode(
+ st->decoder,
+ (const unsigned char*) cod,
+ (short *) pcm,
+ 0
+ );
+ BENCHMARK_STOP(CODEC_EFR, 0);
+
+ return PCM_CANON_LEN;
+}
+
+#endif /* HAVE_OPENCORE_AMRNB */
+
+
+const struct codec_desc codec_amr_desc = {
+ .type = CODEC_AMR,
+ .name = "amr",
+ .description = "GSM 26.071 Adaptive Multi Rate codec",
+ .canon_frame_len = 0,
+#ifdef HAVE_OPENCORE_AMRNB
+ .codec_enc_format_type = FMT_AMR_OPENCORE,
+ .codec_dec_format_type = FMT_AMR_OPENCORE,
+ .codec_init = codec_amr_init,
+ .codec_exit = codec_amr_exit,
+ .codec_encode = codec_amr_encode,
+ .codec_decode = codec_amr_decode,
+#endif
+};
diff --git a/src/codecs.c b/src/codecs.c
index 7a9a6d3..623e80c 100644
--- a/src/codecs.c
+++ b/src/codecs.c
@@ -26,6 +26,7 @@ extern const struct codec_desc codec_pcm_desc;
extern const struct codec_desc codec_hr_desc;
extern const struct codec_desc codec_fr_desc;
extern const struct codec_desc codec_efr_desc;
+extern const struct codec_desc codec_amr_desc;
const struct codec_desc *
@@ -36,6 +37,7 @@ codec_get_from_type(enum codec_type type)
case CODEC_HR: return &codec_hr_desc;
case CODEC_FR: return &codec_fr_desc;
case CODEC_EFR: return &codec_efr_desc;
+ case CODEC_AMR: return &codec_amr_desc;
default:
return NULL;
}
diff --git a/src/fmt_amr_opencore.c b/src/fmt_amr_opencore.c
new file mode 100644
index 0000000..3fa547b
--- /dev/null
+++ b/src/fmt_amr_opencore.c
@@ -0,0 +1,51 @@
+/* Input format to libopencore-amrnb: Exactly like our canonical AMR */
+/* (C) 2017 Harald Welte <laforge@gnumonks.org> */
+
+/*
+ * This file is part of gapk (GSM Audio Pocket Knife).
+ *
+ * gapk 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gapk 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 gapk. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <gapk/codecs.h>
+#include <gapk/formats.h>
+#include <gapk/utils.h>
+
+static int
+amr_opencore_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
+{
+ memcpy(dst, src, src_len);
+ return src_len;
+}
+
+static int
+amr_opencore_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
+{
+ memcpy(dst, src, src_len);
+ return src_len;
+}
+
+const struct format_desc fmt_amr_opencore = {
+ .type = FMT_AMR_OPENCORE,
+ .codec_type = CODEC_AMR,
+ .name = "amr-opencore",
+ .description = "Input format to libopencore-amrnb",
+
+ .frame_len = 0,
+ .conv_from_canon = amr_opencore_from_canon,
+ .conv_to_canon = amr_opencore_to_canon,
+};
diff --git a/src/fmt_rtp_amr.c b/src/fmt_rtp_amr.c
new file mode 100644
index 0000000..1d5357d
--- /dev/null
+++ b/src/fmt_rtp_amr.c
@@ -0,0 +1,60 @@
+/* AMR RTP Payload according to RFC4867. Only one codec frame per RTP */
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org> */
+
+/*
+ * This file is part of gapk (GSM Audio Pocket Knife).
+ *
+ * gapk 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gapk 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 gapk. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/codec/codec.h>
+
+#include <gapk/codecs.h>
+#include <gapk/formats.h>
+#include <gapk/utils.h>
+
+/* conversion function: RTP payload -> canonical format */
+static int
+rtp_amr_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
+{
+ /* add Payload Header according to RFC4867 4.4.1 */
+ dst[0] = 0xf0; /* no request */
+ memcpy(dst+1, src, src_len);
+
+ return src_len+1;
+}
+
+/* conversion function: canonical format -> RTP payload */
+static int
+rtp_amr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
+{
+ /* skip Payload Header according to RFC4867 4.4.1 */
+ memcpy(dst, src+1, src_len-1);
+
+ return src_len-1;
+}
+
+const struct format_desc fmt_rtp_amr = {
+ .type = FMT_RTP_AMR,
+ .codec_type = CODEC_AMR,
+ .name = "rtp-amr",
+ .description = "RTP payload for AMR according to RFC4867",
+
+ .frame_len = 0,
+ .conv_from_canon = rtp_amr_from_canon,
+ .conv_to_canon = rtp_amr_to_canon,
+};
diff --git a/src/formats.c b/src/formats.c
index c776115..0d4c4a0 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -34,6 +34,8 @@ extern const struct format_desc fmt_rawpcm_s16le;
extern const struct format_desc fmt_ti_hr;
extern const struct format_desc fmt_ti_fr;
extern const struct format_desc fmt_ti_efr;
+extern const struct format_desc fmt_amr_opencore;
+extern const struct format_desc fmt_rtp_amr;
static const struct format_desc *supported_formats[_FMT_MAX] = {
[FMT_INVALID] = NULL,
@@ -48,6 +50,8 @@ static const struct format_desc *supported_formats[_FMT_MAX] = {
[FMT_TI_HR] = &fmt_ti_hr,
[FMT_TI_FR] = &fmt_ti_fr,
[FMT_TI_EFR] = &fmt_ti_efr,
+ [FMT_AMR_OPENCORE] = &fmt_amr_opencore,
+ [FMT_RTP_AMR] = &fmt_rtp_amr,
};