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
This commit is contained in:
Harald Welte 2017-05-28 11:04:26 +02:00
parent 494d92c3c7
commit 8b01f0ca05
8 changed files with 245 additions and 2 deletions

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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

121
src/codec_amr.c Normal file
View File

@ -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
};

View File

@ -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;
}

51
src/fmt_amr_opencore.c Normal file
View File

@ -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,
};

60
src/fmt_rtp_amr.c Normal file
View File

@ -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,
};

View File

@ -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,
};