You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rtpengine/lib/amr.c

709 lines
19 KiB

#include "codecmod.h"
#include "loglib.h"
#include "bitstr.h"
static int codeclib_set_av_opt_intstr(encoder_t *enc, const char *opt, str *val) {
int i = val ? str_to_i(val, -1) : -1;
if (i == -1) {
ilog(LOG_WARN, "Failed to parse '" STR_FORMAT "' as integer value for ffmpeg option '%s'",
STR_FMT0(val), opt);
return -1;
}
return codeclib_set_av_opt_int(enc, opt, i);
}
static const unsigned int amr_bitrates[AMR_FT_TYPES] = {
4750, // 0
5150, // 1
5900, // 2
6700, // 3
7400, // 4
7950, // 5
10200, // 6
12200, // 7
0, // comfort noise // 8
0, // comfort noise // 9
0, // comfort noise // 10
0, // comfort noise // 11
0, // invalid // 12
0, // invalid // 13
};
static const unsigned int amr_bits_per_frame[AMR_FT_TYPES] = {
95, // 4.75 kbit/s // 0
103, // 5.15 kbit/s // 1
118, // 5.90 kbit/s // 2
134, // 6.70 kbit/s // 3
148, // 7.40 kbit/s // 4
159, // 7.95 kbit/s // 5
204, // 10.2 kbit/s // 6
244, // 12.2 kbit/s // 7
40, // comfort noise // 8
40, // comfort noise // 9
40, // comfort noise // 10
40, // comfort noise // 11
0, // invalid // 12
0, // invalid // 13
};
static const unsigned int amr_wb_bitrates[AMR_FT_TYPES] = {
6600, // 0
8850, // 1
12650, // 2
14250, // 3
15850, // 4
18250, // 5
19850, // 6
23050, // 7
23850, // 8
0, // comfort noise // 9
0, // invalid // 10
0, // invalid // 11
0, // invalid // 12
0, // invalid // 13
};
static const unsigned int amr_wb_bits_per_frame[AMR_FT_TYPES] = {
132, // 6.60 kbit/s // 0
177, // 8.85 kbit/s // 1
253, // 12.65 kbit/s // 2
285, // 14.25 kbit/s // 3
317, // 15.85 kbit/s // 4
365, // 18.25 kbit/s // 5
397, // 19.85 kbit/s // 6
461, // 23.05 kbit/s // 7
477, // 23.85 kbit/s // 8
40, // comfort noise // 9
0, // invalid // 10
0, // invalid // 11
0, // invalid // 12
0, // invalid // 13
};
static void amr_parse_format_cb(str *key, str *token, void *data) {
union codec_format_options *opts = data;
switch (__csh_lookup(key)) {
case CSH_LOOKUP("octet-align"):
if (token->len == 1 && token->s[0] == '1')
opts->amr.octet_aligned = 1;
break;
case CSH_LOOKUP("crc"):
if (token->len == 1 && token->s[0] == '1') {
opts->amr.octet_aligned = 1;
opts->amr.crc = 1;
}
break;
case CSH_LOOKUP("robust-sorting"):
if (token->len == 1 && token->s[0] == '1') {
opts->amr.octet_aligned = 1;
opts->amr.robust_sorting = 1;
}
break;
case CSH_LOOKUP("interleaving"):
opts->amr.octet_aligned = 1;
opts->amr.interleaving = str_to_i(token, 0);
break;
case CSH_LOOKUP("mode-set"):;
str mode;
while (str_token_sep(&mode, token, ',')) {
int m = str_to_i(&mode, -1);
if (m < 0 || m >= AMR_FT_TYPES)
continue;
opts->amr.mode_set |= (1 << m);
}
break;
case CSH_LOOKUP("mode-change-period"):
opts->amr.mode_change_period = str_to_i(token, 0);
break;
case CSH_LOOKUP("mode-change-neighbor"):
if (token->len == 1 && token->s[0] == '1')
opts->amr.mode_change_neighbor = 1;
break;
}
}
static bool amr_format_parse(struct rtp_codec_format *f, const str *fmtp) {
codeclib_key_value_parse(fmtp, true, amr_parse_format_cb, f);
return true;
}
static void amr_set_encdec_options(codec_options_t *opts, codec_def_t *def) {
if (!strcmp(def->rtpname, "AMR")) {
opts->amr.bits_per_frame = amr_bits_per_frame;
opts->amr.bitrates = amr_bitrates;
}
else {
opts->amr.bits_per_frame = amr_wb_bits_per_frame;
opts->amr.bitrates = amr_wb_bitrates;
}
}
static void amr_set_dec_codec_options(str *key, str *value, void *data) {
decoder_t *dec = data;
if (!str_cmp(key, "CMR-interval"))
dec->codec_options.amr.cmr_interval_us = str_to_i(value, 0) * 1000L;
else if (!str_cmp(key, "mode-change-interval"))
dec->codec_options.amr.mode_change_interval_us = str_to_i(value, 0) * 1000L;
}
static void amr_set_enc_codec_options(str *key, str *value, void *data) {
encoder_t *enc = data;
if (!str_cmp(key, "CMR-interval"))
; // not an encoder option
else if (!str_cmp(key, "mode-change-interval"))
; // not an encoder option
else {
// our string might not be null terminated
char *s = g_strdup_printf(STR_FORMAT, STR_FMT(key));
codeclib_set_av_opt_intstr(enc, s, value);
g_free(s);
}
}
static void amr_set_enc_options(encoder_t *enc, const str *codec_opts) {
amr_set_encdec_options(&enc->codec_options, enc->def);
codeclib_key_value_parse(codec_opts, true, amr_set_enc_codec_options, enc);
// if a mode-set was given, pick the highest supported bitrate
if (enc->format_options.amr.mode_set) {
int max_bitrate = enc->avc.avcctx->bit_rate;
int use_bitrate = 0;
for (int i = 0; i < AMR_FT_TYPES; i++) {
if (!(enc->format_options.amr.mode_set & (1 << i)))
continue;
unsigned int br = enc->codec_options.amr.bitrates[i];
// we depend on the list being in ascending order, with
// invalid modes at the end
if (!br) // end of list
break;
if (br > max_bitrate && use_bitrate) // done
break;
use_bitrate = br;
}
if (!use_bitrate)
ilog(LOG_WARN, "Unable to determine a valid bitrate from %s mode-set, using default",
enc->def->rtpname);
else {
ilog(LOG_DEBUG, "Using %i as initial %s bitrate based on mode-set",
use_bitrate, enc->def->rtpname);
enc->avc.avcctx->bit_rate = use_bitrate;
}
}
}
static void amr_set_dec_options(decoder_t *dec, const str *codec_opts) {
amr_set_encdec_options(&dec->codec_options, dec->def);
codeclib_key_value_parse(codec_opts, true, amr_set_dec_codec_options, dec);
}
static int amr_mode_set_cmp(unsigned int a, unsigned int b) {
if (a && b) {
// `a` must be broader than `b`:
// `b` must not have any bits set that `a` has set
if (a == b)
return 0;
else if ((b & ~a) == 0)
return 1;
else
return -1;
}
else if (!a && b) // `a` is broader (allow anything) than `b` (restricted)
return 1;
else if (a && !b)
return -1;
return 0;
}
static int amr_format_cmp(const struct rtp_payload_type *A, const struct rtp_payload_type *B) {
// params must have been parsed successfully
if (!A->format.fmtp_parsed || !B->format.fmtp_parsed)
return -1;
__auto_type a = &A->format.parsed.amr;
__auto_type b = &B->format.parsed.amr;
// reject anything that is outright incompatible (RFC 4867, 8.3.1)
if (a->octet_aligned != b->octet_aligned)
return -1;
if (a->crc != b->crc)
return -1;
if (a->interleaving != b->interleaving)
return -1;
if (a->robust_sorting != b->robust_sorting)
return -1;
// determine whether codecs are compatible
int compat = 0;
if (a->mode_change_neighbor != b->mode_change_neighbor)
compat++;
if (a->mode_change_period != b->mode_change_period)
compat++;
int match = amr_mode_set_cmp(a->mode_set, b->mode_set);
if (match == 1)
compat++;
else if (match == -1)
return -1;
return (compat == 0) ? 0 : 1;
}
static void amr_bitrate_tracker(decoder_t *dec, unsigned int ft) {
if (dec->codec_options.amr.cmr_interval_us <= 0)
return;
if (dec->avc.amr.tracker_end
&& dec->avc.amr.tracker_end >= rtpe_now) {
// analyse the data we gathered
int next_highest = -1;
int lowest_used = -1;
for (int i = 0; i < AMR_FT_TYPES; i++) {
unsigned int br = dec->codec_options.amr.bitrates[i];
if (!br)
break; // end of list
// ignore restricted modes
if (dec->format_options.amr.mode_set) {
if (!(dec->format_options.amr.mode_set & (1 << i)))
continue;
}
// would this be a "next step up" mode?
if (next_highest == -1)
next_highest = i;
// did we see any frames?
if (!dec->avc.amr.bitrate_tracker[i])
continue;
next_highest = -1;
lowest_used = i;
}
if (lowest_used != -1 && next_highest != -1) {
// we can request a switch up
ilog(LOG_DEBUG, "Sending %s CMR to request upping bitrate to %u",
dec->def->rtpname, dec->codec_options.amr.bitrates[next_highest]);
decoder_event(dec, CE_AMR_SEND_CMR, GINT_TO_POINTER(next_highest));
}
// and reset tracker
ZERO(dec->avc.amr.tracker_end);
}
if (!dec->avc.amr.tracker_end) {
// init
ZERO(dec->avc.amr.bitrate_tracker);
dec->avc.amr.tracker_end = rtpe_now;
dec->avc.amr.tracker_end += dec->codec_options.amr.cmr_interval_us;
}
dec->avc.amr.bitrate_tracker[ft]++;
}
static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
const char *err = NULL;
g_auto(GQueue) toc = G_QUEUE_INIT;
if (!data || !data->s)
goto err;
bitstr d;
bitstr_init(&d, data);
unsigned int ill = 0, ilp = 0;
unsigned char cmr_chr[2];
str cmr = STR_CONST_BUF(cmr_chr);
err = "no CMR";
if (bitstr_shift_ret(&d, 4, &cmr))
goto err;
unsigned int cmr_int = cmr_chr[0] >> 4;
if (cmr_int != 15) {
decoder_event(dec, CE_AMR_CMR_RECV, GUINT_TO_POINTER(cmr_int));
dec->avc.amr.last_cmr = rtpe_now;
}
else if (dec->codec_options.amr.mode_change_interval_us) {
// no CMR, check if we're due to do our own mode change
if (!dec->avc.amr.last_cmr) // start tracking now
dec->avc.amr.last_cmr = rtpe_now;
else if (rtpe_now - dec->avc.amr.last_cmr
>= dec->codec_options.amr.mode_change_interval_us) {
// switch up if we can
decoder_event(dec, CE_AMR_CMR_RECV, GUINT_TO_POINTER(0xffff));
dec->avc.amr.last_cmr = rtpe_now;
}
}
if (dec->format_options.amr.octet_aligned) {
if (bitstr_shift(&d, 4))
goto err;
if (dec->format_options.amr.interleaving) {
unsigned char ill_ilp_chr[2];
str ill_ilp = STR_CONST_BUF(ill_ilp_chr);
err = "no ILL/ILP";
if (bitstr_shift_ret(&d, 8, &ill_ilp))
goto err;
ill = ill_ilp_chr[0] >> 4;
ilp = ill_ilp_chr[0] & 0xf;
}
}
err = "ILP > ILL";
if (ilp > ill)
goto err;
err = "interleaving unimplemented";
if (ill)
goto err;
// TOC
int num_crcs = 0;
while (1) {
unsigned char toc_byte[2];
str toc_entry = STR_CONST_BUF(toc_byte);
err = "missing TOC entry";
if (bitstr_shift_ret(&d, 6, &toc_entry))
goto err;
if (dec->format_options.amr.octet_aligned)
if (bitstr_shift(&d, 2))
goto err;
unsigned char ft = (toc_byte[0] >> 3) & 0xf;
if (ft != 14 && ft != 15) {
num_crcs++;
err = "invalid frame type";
if (ft >= AMR_FT_TYPES)
goto err;
if (dec->codec_options.amr.bits_per_frame[ft] == 0)
goto err;
}
g_queue_push_tail(&toc, GUINT_TO_POINTER(toc_byte[0]));
// no F bit = last TOC entry
if (!(toc_byte[0] & 0x80))
break;
}
if (dec->format_options.amr.crc) {
// CRCs is one byte per frame
err = "missing CRC entry";
if (bitstr_shift(&d, num_crcs * 8))
goto err;
// XXX use/check CRCs
}
while (toc.length) {
unsigned char toc_byte = GPOINTER_TO_UINT(g_queue_pop_head(&toc));
unsigned char ft = (toc_byte >> 3) & 0xf;
if (ft >= AMR_FT_TYPES) // invalid
continue;
unsigned int bits = dec->codec_options.amr.bits_per_frame[ft];
// AMR decoder expects an octet aligned TOC byte plus the payload
unsigned char frame_buf[(bits + 7) / 8 + 1 + 1];
str frame = STR_CONST_BUF(frame_buf);
str_shift(&frame, 1);
err = "short frame";
if (bitstr_shift_ret(&d, bits, &frame))
goto err;
// add TOC byte
str_unshift(&frame, 1);
frame.s[0] = toc_byte & 0x7c; // strip F bit, keep FT and Q, zero padding (01111100)
if (dec->format_options.amr.octet_aligned && (bits % 8) != 0) {
unsigned int padding_bits = 8 - (bits % 8);
if (bitstr_shift(&d, padding_bits))
goto err;
}
err = "failed to decode AMR data";
if (bits == 40) {
// SID
if (dec->dtx.method_id == DTX_NATIVE) {
if (avc_decoder_input(dec, &frame, out))
goto err;
}
else {
// use the DTX generator to replace SID
if (dec->dtx.do_dtx(dec, out, 20))
goto err;
}
}
else {
if (avc_decoder_input(dec, &frame, out))
goto err;
}
amr_bitrate_tracker(dec, ft);
}
return 0;
err:
if (err)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Error unpacking AMR packet: %s", err);
return -1;
}
static unsigned int amr_encoder_find_next_mode(encoder_t *enc) {
int mode = -1;
for (int i = 0; i < AMR_FT_TYPES; i++) {
int br = enc->codec_options.amr.bitrates[i];
if (!br) // end of list
break;
if (br == enc->avc.avcctx->bit_rate) {
mode = i;
break;
}
}
if (mode == -1)
return -1;
int next_mode = mode + 1;
// if modes are restricted, find the next one up
if (enc->format_options.amr.mode_set) {
// is there anything?
if ((1 << next_mode) > enc->format_options.amr.mode_set)
return -1;
int next_up = -1;
for (; next_mode < AMR_FT_TYPES; next_mode++) {
if (!(enc->format_options.amr.mode_set & (1 << next_mode)))
continue;
next_up = next_mode;
break;
}
if (next_up == -1)
return -1;
next_mode = next_up;
}
// valid mode?
if (next_mode >= AMR_FT_TYPES || enc->codec_options.amr.bitrates[next_mode] == 0)
return -1;
return next_mode;
}
static void amr_encoder_mode_change(encoder_t *enc) {
if (enc->callback.amr.cmr_in_ts == enc->avc.amr.cmr_in_ts)
return;
// mode change requested: check if this is allowed right now
if (enc->format_options.amr.mode_change_period == 2 && (enc->avc.amr.pkt_seq & 1) != 0)
return;
unsigned int cmr = enc->callback.amr.cmr_in;
if (cmr == 0xffff)
cmr = amr_encoder_find_next_mode(enc);
if (cmr >= AMR_FT_TYPES)
return;
// ignore CMR for invalid modes
if (enc->format_options.amr.mode_set && !(enc->format_options.amr.mode_set & (1 << cmr)))
return;
int req_br = enc->codec_options.amr.bitrates[cmr];
if (!req_br)
return;
int cmr_done = 1;
if (enc->format_options.amr.mode_change_neighbor) {
// handle non-neighbour mode changes
int cur_br = enc->avc.avcctx->bit_rate;
// step up or down from the requested bitrate towards the current one
int cmr_diff = (req_br > cur_br) ? -1 : 1;
int neigh_br = req_br;
int cmr_br = req_br;
while (1) {
// step up or down towards the current bitrate
cmr += cmr_diff;
// still in bounds?
if (cmr >= AMR_FT_TYPES)
break;
cmr_br = enc->codec_options.amr.bitrates[cmr];
if (cmr_br == cur_br)
break;
// allowed by mode set?
if (enc->format_options.amr.mode_set) {
if (!(enc->format_options.amr.mode_set & (1 << cmr)))
continue; // go to next mode
}
// valid bitrate - continue stepping
neigh_br = cmr_br;
}
// did we finish stepping or is there more to go?
if (neigh_br != req_br)
cmr_done = 0;
req_br = neigh_br; // set to this
}
enc->avc.avcctx->bit_rate = req_br;
if (cmr_done)
enc->avc.amr.cmr_in_ts = enc->callback.amr.cmr_in_ts;
}
static void amr_encoder_got_packet(encoder_t *enc) {
amr_encoder_mode_change(enc);
enc->avc.amr.pkt_seq++;
}
static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, size_t num_bytes, encoder_t *enc,
int64_t *__restrict pts, int64_t *__restrict duration)
{
assert(pkt->size >= 1);
// CMR + TOC byte (already included) + optional ILL/ILP + optional CRC + payload
if (output->len < pkt->size + 3) {
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Output AMR packet size too small (%zu < %i + 3)",
output->len, pkt->size);
return -1;
}
unsigned char toc = pkt->data[0];
unsigned char ft = (toc >> 3) & 0xf;
if (ft > 15) {
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Received bogus AMR FT %u from encoder", ft);
return -1;
}
if (ft >= 14) {
// NO_DATA or SPEECH_LOST
return -1;
}
assert(ft < AMR_FT_TYPES); // internal bug
unsigned int bits = enc->codec_options.amr.bits_per_frame[ft];
if (bits == 0) {
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Received bogus AMR FT %u from encoder", ft);
return -1;
}
unsigned char *s = (unsigned char *) output->s; // for safe bit shifting
*pts = pkt->pts;
*duration = enc->actual_format.clockrate * 20LL / 1000; // 160 or 320
s[0] = '\xf0'; // no CMR req (4 bits)
// or do we have a CMR?
if (!enc->avc.amr.cmr_out_seq) {
if (enc->avc.amr.cmr_out_ts != enc->callback.amr.cmr_out_ts) {
enc->avc.amr.cmr_out_seq += 3; // make this configurable?
enc->avc.amr.cmr_out_ts = enc->callback.amr.cmr_out_ts;
}
}
if (enc->avc.amr.cmr_out_seq) {
enc->avc.amr.cmr_out_seq--;
unsigned int cmr = enc->callback.amr.cmr_out;
if (cmr < AMR_FT_TYPES && enc->codec_options.amr.bitrates[cmr])
s[0] = cmr << 4;
}
if (enc->format_options.amr.octet_aligned) {
unsigned int offset = 1; // CMR byte
if (enc->format_options.amr.interleaving)
s[offset++] = 0; // no interleaving
if (enc->format_options.amr.crc)
s[offset++] = 0; // not implemented
memcpy(s + offset, pkt->data, pkt->size);
output->len = pkt->size + offset;
return 0;
}
// bit shift TOC byte in (6 bits)
s[0] |= pkt->data[0] >> 4;
s[1] = (pkt->data[0] & 0x0c) << 4;
// bit shift payload in (shifted by 4+6 = 10 bits = 1 byte + 2 bits
for (int i = 1; i < pkt->size; i++) {
s[i] |= pkt->data[i] >> 2;
s[i+1] = pkt->data[i] << 6;
}
// is the last byte just padding?
bits += 4 + 6; // CMR and TOC
unsigned int bytes = (bits + 7) / 8;
output->len = bytes;
return 0;
}
static int amr_dtx(decoder_t *dec, GQueue *out, int ptime) {
// ignore ptime, must be 20
ilog(LOG_DEBUG, "pushing empty/lost frame to AMR decoder");
unsigned char frame_buf[1];
frame_buf[0] = 0xf << 3; // no data
str frame = STR_CONST_BUF(frame_buf);
if (avc_decoder_input(dec, &frame, out))
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Error while writing 'no data' frame to AMR decoder");
return 0;
}
static const codec_type_t codec_type_amr = {
.def_init = avc_def_init,
.decoder_init = avc_decoder_init,
.decoder_input = amr_decoder_input,
.decoder_close = avc_decoder_close,
.encoder_init = avc_encoder_init,
.encoder_input = avc_encoder_input,
.encoder_got_packet = amr_encoder_got_packet,
.encoder_close = avc_encoder_close,
};
static const dtx_method_t dtx_method_amr = {
.method_id = DTX_NATIVE,
.do_dtx = amr_dtx,
};
static const codec_def_t amr = {
.rtpname = "AMR",
.avcodec_id = AV_CODEC_ID_AMR_NB,
.avcodec_name_enc = "libopencore_amrnb",
.avcodec_name_dec = "libopencore_amrnb",
.default_clockrate = 8000,
.default_channels = 1,
.default_bitrate = 6700,
.default_ptime = 20,
.minimum_ptime = 20,
.format_parse = amr_format_parse,
.format_cmp = amr_format_cmp,
.default_fmtp = "octet-align=1;mode-change-capability=2",
.packetizer = packetizer_amr,
.bits_per_sample = 2, // max is 12200 / 8000 = 1.525 bits per sample, rounded up
.media_type = MT_AUDIO,
.codec_type = &codec_type_amr,
.set_enc_options = amr_set_enc_options,
.set_dec_options = amr_set_dec_options,
.amr = 1,
.dtx_methods = {
[DTX_NATIVE] = &dtx_method_amr,
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
static const codec_def_t amr_wb = {
.rtpname = "AMR-WB",
.avcodec_id = AV_CODEC_ID_AMR_WB,
.avcodec_name_enc = "libvo_amrwbenc",
.avcodec_name_dec = "libopencore_amrwb",
.default_clockrate = 16000,
.default_channels = 1,
.default_bitrate = 14250,
.default_ptime = 20,
.minimum_ptime = 20,
.format_parse = amr_format_parse,
.format_cmp = amr_format_cmp,
.default_fmtp = "octet-align=1;mode-change-capability=2",
.packetizer = packetizer_amr,
.bits_per_sample = 2, // max is 23850 / 16000 = 1.490625 bits per sample, rounded up
.media_type = MT_AUDIO,
.codec_type = &codec_type_amr,
.set_enc_options = amr_set_enc_options,
.set_dec_options = amr_set_dec_options,
.amr = 1,
.dtx_methods = {
[DTX_NATIVE] = &dtx_method_amr,
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&amr);
codeclib_register_codec(&amr_wb);
}