MT#65413 move codec defs into separate files

Change-Id: Iabf8f8f4de3d45a92933c658084b3f9b72a9b414
pull/1126/merge
Richard Fuchs 4 days ago
parent 250df2af5e
commit 92b0b1104d

@ -92,7 +92,8 @@ endif
LIBSRCS := loglib.c auxlib.c rtplib.c str.c socket.c streambuf.c ssllib.c dtmflib.c mix_buffer.c poller.c \
bufferpool.c bencode.c netfilter_api.c
ifeq ($(with_transcoding),yes)
LIBSRCS += codeclib.strhash.c resample.c
LIBSRCS += codeclib.c resample.c
LIBSRCS += $(CODEC_SRCS)
LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S
endif
ifneq ($(have_liburing),yes)

@ -11,7 +11,7 @@ ifeq ($(with_transcoding),yes)
CFLAGS += $(CFLAGS_OPUS)
endif
SRCS := auxlib.c bencode.c bufferpool.c codeclib.strhash.c dtmflib.c
SRCS := auxlib.c bencode.c bufferpool.c codeclib.c dtmflib.c
SRCS += http.c loglib.c mix_buffer.c
SRCS += netfilter_api.c oauth.c poller.c resample.c
SRCS += rtplib.c s3utils.c socket.c

@ -0,0 +1,34 @@
#include "codecmod.h"
static const codec_def_t ac3 = {
.rtpname = "ac3",
.avcodec_id = AV_CODEC_ID_AC3,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
static const codec_def_t eac3 = {
.rtpname = "eac3",
.avcodec_id = AV_CODEC_ID_EAC3,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&ac3);
codeclib_register_codec(&eac3);
}

@ -0,0 +1,708 @@
#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);
}

@ -0,0 +1,34 @@
#include "codecmod.h"
static const codec_def_t atrac3 = {
.rtpname = "ATRAC3",
.avcodec_id = AV_CODEC_ID_ATRAC3,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
static const codec_def_t atrac_x = {
.rtpname = "ATRAC-X",
.avcodec_id = AV_CODEC_ID_ATRAC3P,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&atrac3);
codeclib_register_codec(&atrac_x);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,36 @@
#ifndef _CODECMOD_H_
#define _CODECMOD_H_
#include "codeclib.h"
extern const codec_type_t codec_type_avcodec;
extern const dtx_method_t dtx_method_silence;
extern const dtx_method_t dtx_method_cn;
packetizer_f packetizer_samplestream; // flat stream of samples
int format_cmp_ignore(const struct rtp_payload_type *, const struct rtp_payload_type *);
void avc_def_init(struct codec_def_s *);
const char *avc_decoder_init(decoder_t *, const str *);
int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out);
void avc_decoder_close(decoder_t *);
const char *avc_encoder_init(encoder_t *enc, const str *);
int avc_encoder_input(encoder_t *enc, AVFrame **frame);
void avc_encoder_close(encoder_t *enc);
int codeclib_set_av_opt_int(encoder_t *enc, const char *opt, int64_t val);
void codeclib_key_value_parse(const str *instr, bool need_value,
void (*cb)(str *key, str *value, void *data), void *data);
void *dlsym_assert(void *handle, const char *sym, const char *fn);
void codeclib_register_codec(const codec_def_t *);
#endif

@ -41,7 +41,7 @@ $(OBJS): Makefile ../include/* ../lib/*.h ../kernel-module/*.h
-M "date:$(BUILD_DATE)" \
-o "$@"
resample.c codeclib.strhash.c mix.c packet.c: ../lib/fix_frame_channel_layout.compat
resample.c codeclib.c mix.c packet.c: ../lib/fix_frame_channel_layout.compat
ifeq ($(with_transcoding),yes)
../daemon/codec.c codec.c: ../lib/dtmf_rx_fillin.compat

@ -0,0 +1,53 @@
#include "codecmod.h"
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0)
static const codec_def_t evrc = {
.rtpname = "EVRC",
.avcodec_id = AV_CODEC_ID_EVRC,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
static const codec_def_t evrc0 = {
.rtpname = "EVRC0",
.avcodec_id = AV_CODEC_ID_EVRC,
.default_clockrate = 8000,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
static const codec_def_t evrc1 = {
.rtpname = "EVRC1",
.avcodec_id = AV_CODEC_ID_EVRC,
.default_clockrate = 8000,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&evrc);
codeclib_register_codec(&evrc0);
codeclib_register_codec(&evrc1);
}
#endif

File diff suppressed because it is too large Load Diff

@ -1,4 +1,5 @@
with_transcoding ?= yes
CODEC_SRCS :=
ifeq ($(origin CFLAGS),undefined)
CFLAGS := -g -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter -Wstrict-prototypes -Werror=return-type \
@ -15,6 +16,9 @@ CFLAGS += -fPIE
ifeq ($(with_transcoding),yes)
CFLAGS += -DWITH_TRANSCODING
CODEC_SRCS += g711.c g723.c g722.c qcelp.c g729.c speex.c gsm.c ilbc.strhash.c opus.strhash.c
CODEC_SRCS += evs.strhash.c vorbis.c ac3.c atrac.c evrc.c amr.strhash.c pseudo.c
CODEC_SRCS += g726.c l16.c u8.c mp3.c
endif
LDFLAGS += -pie

@ -0,0 +1,48 @@
#include "codecmod.h"
static const codec_def_t pcma = {
.rtpname = "PCMA",
.avcodec_id = AV_CODEC_ID_PCM_ALAW,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.packetizer = packetizer_samplestream,
.format_cmp = format_cmp_ignore,
.bits_per_sample = 8,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.silence_pattern = STR_CONST("\xd5"),
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t pcmu = {
.rtpname = "PCMU",
.avcodec_id = AV_CODEC_ID_PCM_MULAW,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.packetizer = packetizer_samplestream,
.bits_per_sample = 8,
.format_cmp = format_cmp_ignore,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.silence_pattern = STR_CONST("\xff"),
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&pcma);
codeclib_register_codec(&pcmu);
}

@ -0,0 +1,27 @@
#include "codecmod.h"
static const codec_def_t g722 = {
.rtpname = "G722",
.avcodec_id = AV_CODEC_ID_ADPCM_G722,
.default_clockrate_fact = {2,1},
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.format_cmp = format_cmp_ignore,
.packetizer = packetizer_samplestream,
.bits_per_sample = 4,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.silence_pattern = STR_CONST("\xfa"),
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&g722);
}

@ -0,0 +1,24 @@
#include "codecmod.h"
static const codec_def_t g723 = {
.rtpname = "G723",
.avcodec_id = AV_CODEC_ID_G723_1,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 30,
.minimum_ptime = 30,
.default_bitrate = 6300,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&g723);
}

@ -0,0 +1,105 @@
#include "codecmod.h"
static const char *g726_encoder_init(encoder_t *enc, const str *extra_opts) {
const char *err = avc_encoder_init(enc, extra_opts);
if (err)
return err;
enc->samples_per_packet = enc->ptime * 8;
return NULL;
}
static const codec_type_t codec_type_g726 = {
.def_init = avc_def_init,
.decoder_init = avc_decoder_init,
.decoder_input = avc_decoder_input,
.decoder_close = avc_decoder_close,
.encoder_init = g726_encoder_init,
.encoder_input = avc_encoder_input,
.encoder_close = avc_encoder_close,
};
static const codec_def_t g726_16 = {
.rtpname = "G726-16",
.avcodec_id = AV_CODEC_ID_ADPCM_G726,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_bitrate = 16000,
.packetizer = packetizer_samplestream,
.bits_per_sample = 2,
.media_type = MT_AUDIO,
.codec_type = &codec_type_g726,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t g726_24 = {
.rtpname = "G726-24",
.avcodec_id = AV_CODEC_ID_ADPCM_G726,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_bitrate = 24000,
.packetizer = packetizer_samplestream,
.bits_per_sample = 3,
.media_type = MT_AUDIO,
.codec_type = &codec_type_g726,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t g726_32 = {
.rtpname = "G726-32",
.avcodec_id = AV_CODEC_ID_ADPCM_G726,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_bitrate = 32000,
.packetizer = packetizer_samplestream,
.bits_per_sample = 4,
.media_type = MT_AUDIO,
.codec_type = &codec_type_g726,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t g726_40 = {
.rtpname = "G726-40",
.avcodec_id = AV_CODEC_ID_ADPCM_G726,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_bitrate = 40000,
.packetizer = packetizer_samplestream,
.bits_per_sample = 5,
.media_type = MT_AUDIO,
.codec_type = &codec_type_g726,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&g726_16);
codeclib_register_codec(&g726_24);
codeclib_register_codec(&g726_32);
codeclib_register_codec(&g726_40);
}

@ -0,0 +1,275 @@
#include "codecmod.h"
#ifndef HAVE_BCG729
static const codec_def_t g729 = {
.rtpname = "G729",
.avcodec_id = AV_CODEC_ID_G729,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t g729a = {
.rtpname = "G729a",
.avcodec_id = AV_CODEC_ID_G729,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
#else
#include <bcg729/encoder.h>
#include <bcg729/decoder.h>
#include "fix_frame_channel_layout.compat"
#include "loglib.h"
static packetizer_f packetizer_g729; // aggregate some frames into packets
static void bcg729_def_init(struct codec_def_s *);
static const char *bcg729_decoder_init(decoder_t *, const str *);
static int bcg729_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static void bcg729_decoder_close(decoder_t *);
static const char *bcg729_encoder_init(encoder_t *enc, const str *);
static int bcg729_encoder_input(encoder_t *enc, AVFrame **frame);
static void bcg729_encoder_close(encoder_t *enc);
static const codec_type_t codec_type_bcg729 = {
.def_init = bcg729_def_init,
.decoder_init = bcg729_decoder_init,
.decoder_input = bcg729_decoder_input,
.decoder_close = bcg729_decoder_close,
.encoder_init = bcg729_encoder_init,
.encoder_input = bcg729_encoder_input,
.encoder_close = bcg729_encoder_close,
};
static const codec_def_t g729 = {
.rtpname = "G729",
.avcodec_id = -1,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_fmtp = "annexb=yes",
.format_cmp = format_cmp_ignore,
.packetizer = packetizer_g729,
.bits_per_sample = 1, // 10 ms frame has 80 samples and encodes as (max) 10 bytes = 80 bits
.media_type = MT_AUDIO,
.codec_type = &codec_type_bcg729,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static const codec_def_t g729a = {
.rtpname = "G729a",
.avcodec_id = -1,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.default_fmtp = "annexb=no",
.format_cmp = format_cmp_ignore,
.packetizer = packetizer_g729,
.bits_per_sample = 1, // 10 ms frame has 80 samples and encodes as (max) 10 bytes = 80 bits
.media_type = MT_AUDIO,
.codec_type = &codec_type_bcg729,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.fixed_sizes = 1,
};
static void bcg729_def_init(struct codec_def_s *def) {
// test init
bcg729EncoderChannelContextStruct *e = initBcg729EncoderChannel(0);
bcg729DecoderChannelContextStruct *d = initBcg729DecoderChannel();
if (e) {
def->support_encoding = 1;
closeBcg729EncoderChannel(e);
}
if (d) {
def->support_decoding = 1;
closeBcg729DecoderChannel(d);
}
}
static const char *bcg729_decoder_init(decoder_t *dec, const str *extra_opts) {
dec->bcg729 = initBcg729DecoderChannel();
if (!dec->bcg729)
return "failed to initialize bcg729";
return NULL;
}
static int bcg729_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
str input = *data;
uint64_t pts = dec->pts;
while (input.len >= 2) {
int frame_len = input.len >= 10 ? 10 : 2;
str inp_frame = input;
inp_frame.len = frame_len;
str_shift(&input, frame_len);
AVFrame *frame = av_frame_alloc();
frame->nb_samples = 80;
frame->format = AV_SAMPLE_FMT_S16;
frame->sample_rate = dec->in_format.clockrate; // 8000
DEF_CH_LAYOUT(&frame->CH_LAYOUT, dec->in_format.channels);
frame->pts = pts;
if (av_frame_get_buffer(frame, 0) < 0)
abort();
pts += frame->nb_samples;
// XXX handle lost packets and comfort noise
bcg729Decoder(dec->bcg729, (void *) inp_frame.s, inp_frame.len, 0, 0, 0,
(void *) frame->extended_data[0]);
g_queue_push_tail(out, frame);
}
return 0;
}
static void bcg729_decoder_close(decoder_t *dec) {
if (dec->bcg729)
closeBcg729DecoderChannel(dec->bcg729);
dec->bcg729 = NULL;
}
static const char *bcg729_encoder_init(encoder_t *enc, const str *extra_opts) {
enc->bcg729 = initBcg729EncoderChannel(0); // no VAD
if (!enc->bcg729)
return "failed to initialize bcg729";
enc->actual_format.format = AV_SAMPLE_FMT_S16;
enc->actual_format.channels = 1;
enc->actual_format.clockrate = 8000;
enc->samples_per_frame = 80;
enc->samples_per_packet = enc->actual_format.clockrate * enc->ptime / 1000;
return NULL;
}
static int bcg729_encoder_input(encoder_t *enc, AVFrame **frame) {
if (!*frame)
return 0;
if ((*frame)->nb_samples != 80) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "bcg729: input %u samples instead of 80", (*frame)->nb_samples);
return -1;
}
av_new_packet(enc->avpkt, 10);
unsigned char len = 0;
bcg729Encoder(enc->bcg729, (void *) (*frame)->extended_data[0], enc->avpkt->data, &len);
if (!len) {
av_packet_unref(enc->avpkt);
return 0;
}
enc->avpkt->size = len;
enc->avpkt->pts = (*frame)->pts;
enc->avpkt->duration = len * 8; // Duration is used by encoder_input_data for pts calculation
return 0;
}
static void bcg729_encoder_close(encoder_t *enc) {
if (enc->bcg729)
closeBcg729EncoderChannel(enc->bcg729);
enc->bcg729 = NULL;
}
static int packetizer_g729(AVPacket *pkt, GString *buf, str *input_output, size_t num_bytes, encoder_t *enc,
int64_t *__restrict pts, int64_t *__restrict duration)
{
// how many frames do we want?
int want_frames = input_output->len / 10;
// easiest case: we only want one frame. return what we got
if (want_frames == 1 && pkt)
return packetizer_passthrough(pkt, buf, input_output, num_bytes, enc, pts, duration);
// any other case, we go through our buffer
str output = *input_output; // remaining output buffer
if (pkt)
g_string_append_len(buf, (char *) pkt->data, pkt->size);
// how many frames do we have?
int have_audio_frames = buf->len / 10;
int have_noise_frames = (buf->len % 10) / 2;
// we have enough?
// special case: 4 noise frames (8 bytes) must be returned now, as otherwise
// (5 noise frames) they might become indistinguishable from an audio frame
if (have_audio_frames + have_noise_frames < want_frames
&& have_noise_frames != 4)
return -1;
int64_t dur = 0;
// return non-silence/noise frames while we can
while (buf->len >= 10 && want_frames && output.len >= 10) {
memcpy(output.s, buf->str, 10);
g_string_erase(buf, 0, 10);
want_frames--;
str_shift(&output, 10);
dur += 80;
}
// append silence/noise frames if we can
while (buf->len >= 2 && want_frames && output.len >= 2) {
memcpy(output.s, buf->str, 2);
g_string_erase(buf, 0, 2);
want_frames--;
str_shift(&output, 2);
dur += 80;
}
*pts = enc->packet_pts;
*duration = dur;
enc->packet_pts += dur;
if (output.len == input_output->len)
return -1; // got nothing
input_output->len = output.s - input_output->s;
return buf->len >= 2 ? 1 : 0;
}
#endif
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&g729);
codeclib_register_codec(&g729a);
}

@ -0,0 +1,24 @@
#include "codecmod.h"
static const codec_def_t gsm = {
.rtpname = "GSM",
.avcodec_id = AV_CODEC_ID_GSM,
.default_clockrate = 8000,
.default_channels = 1,
//.default_bitrate = 13200,
.default_ptime = 20,
.minimum_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&gsm);
}

@ -0,0 +1,132 @@
#include "codecmod.h"
#include "loglib.h"
static bool ilbc_format_parse(struct rtp_codec_format *f, const str *fmtp) {
switch (__csh_lookup(fmtp)) {
case CSH_LOOKUP("mode=20"):
f->parsed.ilbc.mode = 20;
break;
case CSH_LOOKUP("mode=30"):
f->parsed.ilbc.mode = 30;
break;
default:
return false;
}
return true;
}
static int ilbc_mode(int ptime, const union codec_format_options *fmtp, const char *direction) {
int mode = 0;
if (fmtp)
mode = fmtp->ilbc.mode;
if (!mode) {
switch (ptime) {
case 20:
case 40:
case 60:
case 80:
case 100:
case 120:
mode = 20;
ilog(LOG_DEBUG, "Setting iLBC %s mode to 20 ms based on ptime %i",
direction, ptime);
break;
case 30:
case 90:
mode = 30;
ilog(LOG_DEBUG, "Setting iLBC %s mode to 30 ms based on ptime %i",
direction, ptime);
break;
}
}
if (!mode) {
mode = 20;
ilog(LOG_WARNING, "No iLBC %s mode specified, setting to 20 ms", direction);
}
return mode;
}
static void ilbc_set_enc_options(encoder_t *enc, const str *codec_opts) {
int mode = ilbc_mode(enc->ptime, &enc->format_options, "encoder");
codeclib_set_av_opt_int(enc, "mode", mode);
}
static void ilbc_set_dec_options(decoder_t *dec, const str *codec_opts) {
int mode = ilbc_mode(dec->ptime, &dec->format_options, "decoder");
if (mode == 20)
dec->avc.avcctx->block_align = 38;
else if (mode == 30)
dec->avc.avcctx->block_align = 50;
else
ilog(LOG_WARN, "Unsupported iLBC mode %i", mode);
}
static int ilbc_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
int mode = 0, block_align = 0;
static const union codec_format_options mode_20 = { .ilbc = { 20 } };
static const union codec_format_options mode_30 = { .ilbc = { 30 } };
const union codec_format_options *fmtp;
if (data->len % 50 == 0) {
mode = 30;
block_align = 50;
fmtp = &mode_30;
}
else if (data->len % 38 == 0) {
mode = 20;
block_align = 38;
fmtp = &mode_20;
}
else
ilog(LOG_WARNING | LOG_FLAG_LIMIT, "iLBC received %i bytes packet, does not match "
"one of the block sizes", (int) data->len);
if (block_align && dec->avc.avcctx->block_align != block_align) {
ilog(LOG_INFO | LOG_FLAG_LIMIT, "iLBC decoder set to %i bytes blocks, but received packet "
"of %i bytes, therefore resetting decoder and switching to %i bytes "
"block mode (%i ms mode)",
(int) dec->avc.avcctx->block_align, (int) data->len, block_align, mode);
avc_decoder_close(dec);
dec->format_options = *fmtp;
avc_decoder_init(dec, NULL);
}
return avc_decoder_input(dec, data, out);
}
static const codec_type_t codec_type_ilbc = {
.def_init = avc_def_init,
.decoder_init = avc_decoder_init,
.decoder_input = ilbc_decoder_input,
.decoder_close = avc_decoder_close,
.encoder_init = avc_encoder_init,
.encoder_input = avc_encoder_input,
.encoder_close = avc_encoder_close,
};
static const codec_def_t ilbc = {
.rtpname = "iLBC",
.avcodec_id = AV_CODEC_ID_ILBC,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 30,
.default_fmtp = "mode=30",
.format_parse = ilbc_format_parse,
//.default_bitrate = 15200,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_ilbc,
.set_enc_options = ilbc_set_enc_options,
.set_dec_options = ilbc_set_dec_options,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&ilbc);
}

@ -0,0 +1,35 @@
#include "codecmod.h"
static const codec_def_t l16 = {
.rtpname = "L16",
.avcodec_id = AV_CODEC_ID_PCM_S16BE,
.default_clockrate = 44100,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.bits_per_sample = 16,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
};
static const codec_def_t x_l16 = {
.rtpname = "X-L16",
.avcodec_id = AV_CODEC_ID_PCM_S16LE,
.default_clockrate = 44100,
.default_channels = 1,
.default_ptime = 20,
.minimum_ptime = 20,
.bits_per_sample = 16,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&l16);
codeclib_register_codec(&x_l16);
}

@ -0,0 +1,15 @@
#include "codecmod.h"
static const codec_def_t mp3 = {
.rtpname = "MP3",
.avcodec_id = AV_CODEC_ID_MP3,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&mp3);
}

@ -0,0 +1,471 @@
#include "codecmod.h"
#include <opus.h>
#include "loglib.h"
#include "fix_frame_channel_layout.compat"
static void opus_init(struct rtp_payload_type *pt) {
if (pt->clock_rate != 48000) {
ilog(LOG_WARN, "Opus is only supported with a clock rate of 48 kHz");
pt->clock_rate = 48000;
}
switch (pt->ptime) {
case 5:
case 10:
case 20:
case 40:
case 60:
break;
default:
;
int np;
if (pt->ptime < 10)
np = 5;
else if (pt->ptime < 20)
np = 10;
else if (pt->ptime < 40)
np = 20;
else if (pt->ptime < 60)
np = 40;
else
np = 60;
ilog(LOG_INFO, "Opus doesn't support a ptime of %i ms; using %i ms instead",
pt->ptime, np);
pt->ptime = np;
break;
}
if (pt->bitrate) {
if (pt->bitrate < 6000) {
ilog(LOG_DEBUG, "Opus bitrate %i bps too small, assuming %i kbit/s",
pt->bitrate, pt->bitrate);
pt->bitrate *= 1000;
}
return;
}
if (pt->channels == 1)
pt->bitrate = 24000;
else if (pt->channels == 2)
pt->bitrate = 32000;
else
pt->bitrate = 64000;
ilog(LOG_DEBUG, "Using default bitrate of %i bps for %i-channel Opus", pt->bitrate, pt->channels);
}
static const char *libopus_decoder_init(decoder_t *dec, const str *extra_opts) {
if (dec->in_format.channels != 1 && dec->in_format.channels != 2)
return "invalid number of channels";
switch (dec->in_format.clockrate) {
case 48000:
case 24000:
case 16000:
case 12000:
case 8000:
break;
default:
return "invalid clock rate";
}
int err = 0;
dec->opus = opus_decoder_create(dec->in_format.clockrate, dec->in_format.channels, &err);
if (!dec->opus) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error from libopus: %s", opus_strerror(err));
return "failed to alloc codec context";
}
return NULL;
}
static void libopus_decoder_close(decoder_t *dec) {
opus_decoder_destroy(dec->opus);
}
static int libopus_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
// get frame with buffer large enough for the max
AVFrame *frame = av_frame_alloc();
frame->nb_samples = 960;
frame->format = AV_SAMPLE_FMT_S16;
frame->sample_rate = dec->in_format.clockrate;
DEF_CH_LAYOUT(&frame->CH_LAYOUT, dec->in_format.channels);
frame->pts = dec->pts;
if (av_frame_get_buffer(frame, 0) < 0)
abort();
int ret = opus_decode(dec->opus, (unsigned char *) data->s, data->len,
(int16_t *) frame->extended_data[0], frame->nb_samples, 0);
if (ret < 0) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error decoding Opus packet: %s", opus_strerror(ret));
av_frame_free(&frame);
return -1;
}
frame->nb_samples = ret;
g_queue_push_tail(out, frame);
return 0;
}
struct libopus_encoder_options {
int complexity;
int vbr;
int vbr_constraint;
int pl;
int application;
};
static void libopus_set_enc_opts(str *key, str *val, void *p) {
struct libopus_encoder_options *opts = p;
switch (__csh_lookup(key)) {
case CSH_LOOKUP("complexity"):
case CSH_LOOKUP("compression_level"):
opts->complexity = str_to_i(val, -1);
break;
case CSH_LOOKUP("application"):
switch (__csh_lookup(val)) {
case CSH_LOOKUP("VOIP"):
case CSH_LOOKUP("VoIP"):
case CSH_LOOKUP("voip"):
opts->application = OPUS_APPLICATION_VOIP;
break;
case CSH_LOOKUP("audio"):
opts->application = OPUS_APPLICATION_AUDIO;
break;
case CSH_LOOKUP("low-delay"):
case CSH_LOOKUP("low delay"):
case CSH_LOOKUP("lowdelay"):
opts->application = OPUS_APPLICATION_RESTRICTED_LOWDELAY;
break;
default:
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Unknown Opus application: '"
STR_FORMAT "'", STR_FMT(val));
};
break;
case CSH_LOOKUP("vbr"):
case CSH_LOOKUP("VBR"):
// aligned with ffmpeg vbr=0/1/2 option
opts->vbr = str_to_i(val, -1);
if (opts->vbr == 2) {
opts->vbr = 1;
opts->vbr_constraint = 1;
}
break;
case CSH_LOOKUP("packet_loss"):
case CSH_LOOKUP("packet loss"):
opts->pl = str_to_i(val, -1);
break;
default:
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Unknown Opus encoder option encountered: '"
STR_FORMAT "'", STR_FMT(key));
}
}
static const char *libopus_encoder_init(encoder_t *enc, const str *extra_opts) {
if (enc->requested_format.channels != 1 && enc->requested_format.channels != 2)
return "invalid number of channels";
if (enc->requested_format.format == -1)
enc->requested_format.format = AV_SAMPLE_FMT_S16;
else if (enc->requested_format.format != AV_SAMPLE_FMT_S16)
return "invalid sample format";
switch (enc->requested_format.clockrate) {
case 48000:
case 24000:
case 16000:
case 12000:
case 8000:
break;
default:
return "invalid clock rate";
}
struct libopus_encoder_options opts = { .vbr = 1, .complexity = 10, .application = OPUS_APPLICATION_VOIP };
codeclib_key_value_parse(extra_opts, true, libopus_set_enc_opts, &opts);
int err;
enc->opus = opus_encoder_create(enc->requested_format.clockrate, enc->requested_format.channels,
opts.application, &err);
if (!enc->opus) {
ilog(LOG_ERR, "Error from libopus: %s", opus_strerror(err));
return "failed to alloc codec context";
}
enc->actual_format = enc->requested_format;
enc->samples_per_frame = enc->actual_format.clockrate * enc->ptime / 1000;
enc->samples_per_packet = enc->samples_per_frame;
err = opus_encoder_ctl(enc->opus, OPUS_SET_BITRATE(enc->bitrate));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus bitrate to %i: %s", enc->bitrate,
opus_strerror(err));
err = opus_encoder_ctl(enc->opus, OPUS_SET_COMPLEXITY(opts.complexity));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus complexity to %i': %s",
opts.complexity, opus_strerror(err));
err = opus_encoder_ctl(enc->opus, OPUS_SET_VBR(opts.vbr));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus VBR to %i': %s",
opts.vbr, opus_strerror(err));
err = opus_encoder_ctl(enc->opus, OPUS_SET_VBR_CONSTRAINT(opts.vbr_constraint));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus VBR constraint to %i': %s",
opts.vbr_constraint, opus_strerror(err));
err = opus_encoder_ctl(enc->opus, OPUS_SET_PACKET_LOSS_PERC(opts.pl));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus PL%% to %i': %s",
opts.pl, opus_strerror(err));
err = opus_encoder_ctl(enc->opus, OPUS_SET_INBAND_FEC(enc->format_options.opus.fec_send >= 0));
if (err != OPUS_OK)
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus FEC to %i': %s",
enc->format_options.opus.fec_send >= 0, opus_strerror(err));
return NULL;
}
static void libopus_encoder_close(encoder_t *enc) {
opus_encoder_destroy(enc->opus);
}
#define MAX_OPUS_FRAME_SIZE 1275 /* 20 ms at 510 kbps */
#define MAX_OPUS_FRAMES_PER_PACKET 6 /* 120 ms = 6 * 20 ms */
#define MAX_OPUS_HEADER_SIZE 7
static int libopus_encoder_input(encoder_t *enc, AVFrame **frame) {
if (!*frame)
return 0;
// max length of Opus packet:
av_new_packet(enc->avpkt, MAX_OPUS_FRAME_SIZE * MAX_OPUS_FRAMES_PER_PACKET + MAX_OPUS_HEADER_SIZE);
int ret = opus_encode(enc->opus, (int16_t *) (*frame)->extended_data[0], (*frame)->nb_samples,
enc->avpkt->data, enc->avpkt->size);
if (ret < 0) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error encoding Opus packet: %s", opus_strerror(ret));
av_packet_unref(enc->avpkt);
return -1;
}
enc->avpkt->size = ret;
enc->avpkt->pts = (*frame)->pts;
enc->avpkt->duration = (*frame)->nb_samples;
return 0;
}
// opus RTP always runs at 48 kHz
static void opus_select_encoder_format(encoder_t *enc, format_t *req_format, const format_t *f,
const struct rtp_codec_format *fmtp)
{
if (req_format->clockrate != 48000)
return; // bail - encoder will fail to initialise
// check against natively supported rates first
switch (f->clockrate) {
case 48000:
case 24000:
case 16000:
case 12000:
case 8000:
enc->clockrate_fact = (struct fraction) {1, 48000 / f->clockrate};
break;
default:
// resample to next best rate
if (f->clockrate > 24000)
enc->clockrate_fact = (struct fraction) {1,1};
else if (f->clockrate > 16000)
enc->clockrate_fact = (struct fraction) {1,2};
else if (f->clockrate > 12000)
enc->clockrate_fact = (struct fraction) {1,3};
else if (f->clockrate > 8000)
enc->clockrate_fact = (struct fraction) {1,4};
else
enc->clockrate_fact = (struct fraction) {1,6};
break;
}
// honour remote stereo=0/1 flag if given,
// otherwise go with the input format
if (fmtp && fmtp->parsed.opus.stereo_send == -1)
req_format->channels = 1;
else if (fmtp && fmtp->parsed.opus.stereo_send == 1)
req_format->channels = 2;
else if (req_format->channels == 2 && f->channels == 1)
req_format->channels = 1;
}
static void opus_select_decoder_format(decoder_t *dec, const struct rtp_codec_format *fmtp) {
if (dec->in_format.clockrate != 48000)
return;
// check against natively supported rates first
switch (dec->dest_format.clockrate) {
case 48000:
case 24000:
case 16000:
case 12000:
case 8000:
dec->clockrate_fact = (struct fraction) {1, 48000 / dec->dest_format.clockrate};
break;
default:
// resample to next best rate
if (dec->dest_format.clockrate > 24000)
dec->clockrate_fact = (struct fraction) {1,1};
else if (dec->dest_format.clockrate > 16000)
dec->clockrate_fact = (struct fraction) {1,2};
else if (dec->dest_format.clockrate > 12000)
dec->clockrate_fact = (struct fraction) {1,3};
else if (dec->dest_format.clockrate > 8000)
dec->clockrate_fact = (struct fraction) {1,4};
else
dec->clockrate_fact = (struct fraction) {1,6};
break;
}
// switch to mono decoding if possible
if (dec->in_format.channels == 2 && dec->dest_format.channels == 1)
dec->in_format.channels = 1;
}
static void opus_parse_format_cb(str *key, str *token, void *data) {
union codec_format_options *opts = data;
__auto_type o = &opts->opus;
switch (__csh_lookup(key)) {
#define YNFLAG(flag, varname) \
case flag: \
if (token->len == 1 && token->s[0] == '1') \
o->varname = 1; \
else if (token->len == 1 && token->s[0] == '0') \
o->varname = -1; \
break;
YNFLAG(CSH_LOOKUP("stereo"), stereo_recv)
YNFLAG(CSH_LOOKUP("sprop-stereo"), stereo_send)
YNFLAG(CSH_LOOKUP("useinbandfec"), fec_recv)
YNFLAG(CSH_LOOKUP("cbr"), cbr)
YNFLAG(CSH_LOOKUP("usedtx"), usedtx)
#undef YNFLAG
case CSH_LOOKUP("maxplaybackrate"):
opts->opus.maxplaybackrate = str_to_i(token, 0);
break;
case CSH_LOOKUP("sprop-maxcapturerate"):
opts->opus.sprop_maxcapturerate = str_to_i(token, 0);
break;
case CSH_LOOKUP("maxaveragebitrate"):
opts->opus.maxaveragebitrate = str_to_i(token, 0);
break;
case CSH_LOOKUP("minptime"):
opts->opus.minptime = str_to_i(token, 0);
break;
}
}
static bool opus_format_parse(struct rtp_codec_format *f, const str *fmtp) {
codeclib_key_value_parse(fmtp, true, opus_parse_format_cb, &f->parsed);
return true;
}
static GString *opus_format_print(const struct rtp_payload_type *p) {
if (!p->format.fmtp_parsed)
return NULL;
GString *s = g_string_new("");
__auto_type f = &p->format.parsed.opus;
if (f->stereo_recv)
g_string_append_printf(s, "stereo=%i; ", f->stereo_recv == -1 ? 0 : 1);
if (f->stereo_send)
g_string_append_printf(s, "sprop-stereo=%i; ", f->stereo_send == -1 ? 0 : 1);
if (f->fec_recv)
g_string_append_printf(s, "useinbandfec=%i; ", f->fec_recv == -1 ? 0 : 1);
if (f->usedtx)
g_string_append_printf(s, "usedtx=%i; ", f->usedtx == -1 ? 0 : 1);
if (f->cbr)
g_string_append_printf(s, "cbr=%i; ", f->cbr == -1 ? 0 : 1);
if (f->maxplaybackrate)
g_string_append_printf(s, "maxplaybackrate=%i; ", f->maxplaybackrate);
if (f->maxaveragebitrate)
g_string_append_printf(s, "maxaveragebitrate=%i; ", f->maxaveragebitrate);
if (f->sprop_maxcapturerate)
g_string_append_printf(s, "sprop-maxcapturerate=%i; ", f->sprop_maxcapturerate);
if (f->minptime)
g_string_append_printf(s, "minptime=%i; ", f->minptime);
if (s->len != 0)
g_string_truncate(s, s->len - 2);
return s;
}
static void opus_format_answer(struct rtp_payload_type *p, const struct rtp_payload_type *src) {
if (!p->format.fmtp_parsed)
return;
__auto_type f = &p->format.parsed.opus;
// swap send/recv
int t = f->stereo_send;
f->stereo_send = f->stereo_recv;
f->stereo_recv = t;
t = f->fec_send;
f->fec_send = f->fec_recv;
f->fec_recv = t;
// if stereo recv is unset, base it on input format
if (f->stereo_recv == 0)
f->stereo_recv = src->channels == 1 ? -1 : 1;
// we can always use FEC, unless we've been told that we should lie
if (f->fec_recv == 0)
f->fec_recv = 1;
// set everything unsupported to 0
f->usedtx = 0;
f->cbr = 0;
f->maxplaybackrate = 0;
f->sprop_maxcapturerate = 0;
f->maxaveragebitrate = 0;
f->minptime = 0;
}
static const codec_type_t codec_type_libopus = {
.decoder_init = libopus_decoder_init,
.decoder_input = libopus_decoder_input,
.decoder_close = libopus_decoder_close,
.encoder_init = libopus_encoder_init,
.encoder_input = libopus_encoder_input,
.encoder_close = libopus_encoder_close,
};
static const codec_def_t opus = {
.rtpname = "opus",
.avcodec_id = -1,
.default_clockrate = 48000,
.default_channels = 2,
.default_bitrate = 32000,
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_libopus,
.init = opus_init,
.default_fmtp = "useinbandfec=1",
.format_parse = opus_format_parse,
.format_print = opus_format_print,
.format_cmp = format_cmp_ignore,
.format_answer = opus_format_answer,
.select_encoder_format = opus_select_encoder_format,
.select_decoder_format = opus_select_decoder_format,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
.support_encoding = 1,
.support_decoding = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&opus);
}

@ -0,0 +1,225 @@
#include "codecmod.h"
#include <arpa/inet.h>
#include "loglib.h"
#include "fix_frame_channel_layout.compat"
#include "dtmflib.h"
static const char *dtmf_decoder_init(decoder_t *dec, const str *extra_opts) {
dec->dtmf.event = -1;
return NULL;
}
static AVFrame *dtmf_frame_int16_t_mono(unsigned long frame_ts, unsigned long num_samples, unsigned int event,
unsigned int volume,
unsigned int sample_rate)
{
// synthesise PCM
// first get our frame and figure out how many samples we need, and the start offset
AVFrame *frame = av_frame_alloc();
frame->nb_samples = num_samples;
frame->format = AV_SAMPLE_FMT_S16;
frame->sample_rate = sample_rate;
frame->CH_LAYOUT = (CH_LAYOUT_T) MONO_LAYOUT;
frame->pts = frame_ts;
if (av_frame_get_buffer(frame, 0) < 0)
abort();
// fill samples
dtmf_samples_int16_t_mono(frame->extended_data[0], frame_ts, frame->nb_samples, event,
volume, sample_rate);
return frame;
}
static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
struct telephone_event_payload *dtmf;
if (data->len < sizeof(*dtmf)) {
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Short DTMF event packet (len %zu)", data->len);
return -1;
}
dtmf = (void *) data->s;
// init if we need to
if (dtmf->event != dec->dtmf.event || dec->rtp_ts != dec->dtmf.start_ts) {
ZERO(dec->dtmf);
dec->dtmf.event = dtmf->event;
dec->dtmf.start_ts = dec->rtp_ts;
ilog(LOG_DEBUG, "New DTMF event starting: %u at TS %lu", dtmf->event, dec->rtp_ts);
}
unsigned long duration = ntohs(dtmf->duration);
unsigned long frame_ts = dec->rtp_ts - dec->dtmf.start_ts + dec->dtmf.duration;
long num_samples = duration - dec->dtmf.duration;
ilog(LOG_DEBUG, "Generate DTMF samples for event %u, start TS %lu, TS now %lu, frame TS %lu, "
"duration %lu, "
"old duration %lu, num samples %li",
dtmf->event, dec->dtmf.start_ts, dec->rtp_ts, frame_ts,
duration, dec->dtmf.duration, num_samples);
if (num_samples <= 0)
return 0;
if (num_samples > dec->in_format.clockrate) {
ilog(LOG_ERR, "Cannot generate %li DTMF samples (clock rate %u)", num_samples,
dec->in_format.clockrate);
return -1;
}
AVFrame *frame = dtmf_frame_int16_t_mono(frame_ts, num_samples, dtmf->event, dtmf->volume,
dec->in_format.clockrate);
frame->pts += dec->dtmf.start_ts;
g_queue_push_tail(out, frame);
dec->dtmf.duration = duration;
return 0;
}
static const char *cn_decoder_init(decoder_t *dec, const str *opts) {
// the ffmpeg cngdec always runs at 8000
dec->in_format.clockrate = 8000;
dec->in_format.channels = 1;
dec->resampler.no_filter = true;
return avc_decoder_init(dec, opts);
}
static int cn_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
// generate one set of ptime worth of samples
int ptime = dec->ptime;
if (ptime <= 0)
ptime = 20; // ?
int samples = dec->in_format.clockrate * ptime / 1000;
int max_size = dec->avc.avcctx->frame_size;
AVFrame *aframe = NULL;
do {
if (samples < max_size)
dec->avc.avcctx->frame_size = samples;
int ret = avc_decoder_input(dec, data, out);
dec->avc.avcctx->frame_size = max_size;
if (ret)
return ret;
if (!out->length)
return -1;
AVFrame *oframe = out->head->data;
// one-shot handling if fewer samples than the CNG's frame size are requested
if (!aframe && out->length == 1) {
if (oframe->nb_samples >= samples) {
oframe->nb_samples = samples;
return 0;
}
}
// consume frames and merge into single output frame
if (!aframe) {
aframe = av_frame_alloc();
aframe->nb_samples = samples;
assert(oframe->format == AV_SAMPLE_FMT_S16);
aframe->format = oframe->format;
assert(oframe->sample_rate == 8000);
aframe->sample_rate = oframe->sample_rate;
aframe->CH_LAYOUT = oframe->CH_LAYOUT; // should be mono
aframe->pts = oframe->pts;
aframe->pkt_dts = oframe->pkt_dts;
if (av_frame_get_buffer(aframe, 0) < 0)
abort();
aframe->nb_samples = 0; // to track progress
}
while (out->length) {
oframe = g_queue_pop_head(out);
if (oframe->nb_samples <= 0) // error
return -1; // XXX leaves frames in `out`
// use as much as we have and as much as we need
int rsamples = MIN(oframe->nb_samples, samples);
memcpy(aframe->extended_data[0] + aframe->nb_samples * 2,
oframe->extended_data[0], rsamples * 2);
aframe->nb_samples += rsamples;
samples -= rsamples; // drop to zero when finished
av_frame_free(&oframe);
};
} while (samples > 0);
g_queue_push_tail(out, aframe);
return 0;
}
static const codec_type_t codec_type_dtmf = {
.decoder_init = dtmf_decoder_init,
.decoder_input = dtmf_decoder_input,
};
static const codec_type_t codec_type_cn = {
.def_init = avc_def_init,
.decoder_init = cn_decoder_init,
.decoder_input = cn_decoder_input,
.decoder_close = avc_decoder_close,
};
static const codec_def_t dtmf = {
.rtpname = "telephone-event",
.avcodec_id = -1,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.supplemental = 1,
.dtmf = 1,
.default_clockrate = 8000,
.default_channels = 1,
.default_fmtp = "0-15",
.format_cmp = format_cmp_ignore,
.codec_type = &codec_type_dtmf,
.support_encoding = 1,
.support_decoding = 1,
};
static const codec_def_t cn = {
.rtpname = "CN",
.avcodec_id = AV_CODEC_ID_COMFORT_NOISE,
.avcodec_name_enc = "comfortnoise",
.avcodec_name_dec = "comfortnoise",
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.supplemental = 1,
.default_clockrate = 8000,
.default_channels = 1,
.default_ptime = 20,
.format_cmp = format_cmp_ignore,
.codec_type = &codec_type_cn,
};
static const codec_def_t red = {
.rtpname = "red",
.avcodec_id = -1,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.supplemental = 1,
.default_clockrate = 8000,
.default_channels = 1,
.format_cmp = format_cmp_ignore,
.support_encoding = 1,
.support_decoding = 1,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&dtmf);
codeclib_register_codec(&cn);
codeclib_register_codec(&red);
}

@ -0,0 +1,20 @@
#include "codecmod.h"
static const codec_def_t qcelp = {
.rtpname = "QCELP",
.avcodec_id = AV_CODEC_ID_QCELP,
.default_ptime = 20,
.minimum_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&qcelp);
}

@ -0,0 +1,25 @@
#include "codecmod.h"
static const codec_def_t speex = {
.rtpname = "speex",
.avcodec_id = AV_CODEC_ID_SPEEX,
.default_clockrate = 16000,
.default_channels = 1,
.default_bitrate = 11000,
.default_ptime = 20,
.minimum_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&speex);
}

@ -0,0 +1,15 @@
#include "codecmod.h"
static const codec_def_t u8 = {
.rtpname = "PCM-U8",
.avcodec_id = AV_CODEC_ID_PCM_U8,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&u8);
}

@ -0,0 +1,23 @@
#include "codecmod.h"
static const codec_def_t vorbis = {
.rtpname = "vorbis",
.avcodec_id = AV_CODEC_ID_VORBIS,
.avcodec_name_enc = "libvorbis",
.avcodec_name_dec = "libvorbis",
.default_ptime = 20,
.packetizer = packetizer_passthrough,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.dtx_methods = {
[DTX_SILENCE] = &dtx_method_silence,
[DTX_CN] = &dtx_method_cn,
},
};
__attribute__((constructor))
static void init(void) {
codeclib_register_codec(&vorbis);
}

@ -45,8 +45,9 @@ CFLAGS += $(CFLAGS_CODEC_CHAIN)
LDLIBS += $(LDLIBS_CODEC_CHAIN)
SRCS := main.c log.c
LIBSRCS := codeclib.strhash.c loglib.c auxlib.c resample.c str.c dtmflib.c rtplib.c poller.c ssllib.c bufferpool.c \
LIBSRCS := codeclib.c loglib.c auxlib.c resample.c str.c dtmflib.c rtplib.c poller.c ssllib.c bufferpool.c \
bencode.c uring.c
LIBSRCS += $(CODEC_SRCS)
LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S
include ../lib/common.Makefile

@ -41,8 +41,9 @@ LDLIBS += $(LDLIBS_BCG729)
SRCS := epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \
decoder.c output.c mix.c db.c log.c forward.c tag.c custom_poller.c notify.c tls_send.c s3.c \
gcs.c
LIBSRCS := loglib.c auxlib.c rtplib.c codeclib.strhash.c resample.c str.c socket.c streambuf.c ssllib.c \
LIBSRCS := loglib.c auxlib.c rtplib.c codeclib.c resample.c str.c socket.c streambuf.c ssllib.c \
dtmflib.c bufferpool.c bencode.c http.c s3utils.c oauth.c
LIBSRCS += $(CODEC_SRCS)
LIBASM := mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S
MDS := rtpengine-recording.ronn

@ -290,7 +290,7 @@ test-mix-buffer: test-mix-buffer.o \
../daemon/ssrc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mix_buffer.o \
../lib/mix_in_x64_avx2.o \
@ -315,8 +315,9 @@ spandsp_raw_fax_tests: spandsp_send_fax_pcm spandsp_recv_fax_pcm spandsp_send_fa
test-amr-decode: test-amr-decode.o \
$(COMMONOBJS) \
../lib/amr.strhash.o \
../lib/bencode.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mvr2s_x64_avx2.o \
../lib/mvr2s_x64_avx512.o \
@ -325,8 +326,9 @@ test-amr-decode: test-amr-decode.o \
test-amr-encode: test-amr-encode.o \
$(COMMONOBJS) \
../lib/amr.strhash.o \
../lib/bencode.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mvr2s_x64_avx2.o \
../lib/mvr2s_x64_avx512.o \
@ -386,7 +388,7 @@ test-stats: test-stats.o \
../daemon/websocket.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mix_buffer.o \
../lib/mix_in_x64_avx2.o \
@ -443,17 +445,23 @@ test-transcode: test-transcode.o \
../daemon/timerthread.o \
../daemon/udp_listener.o \
../daemon/websocket.o \
../lib/amr.strhash.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/g711.o \
../lib/g722.o \
../lib/gsm.o \
../lib/mix_buffer.o \
../lib/mix_in_x64_avx2.o \
../lib/mix_in_x64_avx512bw.o \
../lib/mix_in_x64_sse2.o \
../lib/mvr2s_x64_avx2.o \
../lib/mvr2s_x64_avx512.o \
../lib/opus.strhash.o \
../lib/poller.o \
../lib/pseudo.o \
../lib/resample.o \
../lib/socket.o \
../lib/streambuf.o \
@ -463,7 +471,7 @@ test-transcode: test-transcode.o \
test-resample: test-resample.o \
$(COMMONOBJS) \
../lib/bencode.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mvr2s_x64_avx2.o \
../lib/mvr2s_x64_avx512.o \
@ -478,7 +486,7 @@ test-payload-tracker: test-payload-tracker.o \
../daemon/ssrc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/codeclib.strhash.o \
../lib/codeclib.o \
../lib/dtmflib.o \
../lib/mvr2s_x64_avx2.o \
../lib/mvr2s_x64_avx512.o \

Loading…
Cancel
Save