diff --git a/daemon/Makefile b/daemon/Makefile index c274ce8a7..6e456b447 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -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) diff --git a/lib/Makefile b/lib/Makefile index 67636d7d9..2e4231c3d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/ac3.c b/lib/ac3.c new file mode 100644 index 000000000..4585e0e79 --- /dev/null +++ b/lib/ac3.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); +} diff --git a/lib/amr.c b/lib/amr.c new file mode 100644 index 000000000..cf50c567b --- /dev/null +++ b/lib/amr.c @@ -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); +} diff --git a/lib/atrac.c b/lib/atrac.c new file mode 100644 index 000000000..cff16da58 --- /dev/null +++ b/lib/atrac.c @@ -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); +} diff --git a/lib/codeclib.c b/lib/codeclib.c index 67a3fe9d7..eda02b121 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -4,13 +4,7 @@ #include #include #include -#include #include -#ifdef HAVE_BCG729 -#include -#include -#endif -#include #ifdef HAVE_CODEC_CHAIN #include #include @@ -19,9 +13,9 @@ #include "loglib.h" #include "resample.h" #include "rtplib.h" -#include "bitstr.h" #include "dtmflib.h" #include "fix_frame_channel_layout.compat" +#include "codecmod.h" @@ -34,116 +28,13 @@ - -static packetizer_f packetizer_samplestream; // flat stream of samples -static packetizer_f packetizer_amr; - - -static void codeclib_key_value_parse(const str *instr, bool need_value, - void (*cb)(str *key, str *value, void *data), void *data); - -static const char *libopus_decoder_init(decoder_t *, const str *); -static int libopus_decoder_input(decoder_t *dec, const str *data, GQueue *out); -static void libopus_decoder_close(decoder_t *); -static const char *libopus_encoder_init(encoder_t *enc, const str *); -static int libopus_encoder_input(encoder_t *enc, AVFrame **frame); -static void libopus_encoder_close(encoder_t *enc); -static format_init_f opus_init; -static select_encoder_format_f opus_select_encoder_format; -static select_decoder_format_f opus_select_decoder_format; -static format_parse_f opus_format_parse; -static format_print_f opus_format_print; -static format_answer_f opus_format_answer; - -static format_parse_f ilbc_format_parse; -static set_enc_options_f ilbc_set_enc_options; -static set_dec_options_f ilbc_set_dec_options; - -static format_parse_f amr_format_parse; -static set_enc_options_f amr_set_enc_options; -static set_dec_options_f amr_set_dec_options; -static format_cmp_f amr_format_cmp; - -static void avc_def_init(struct codec_def_s *); -static const char *avc_decoder_init(decoder_t *, const str *); -static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out); -static void avc_decoder_close(decoder_t *); -static const char *avc_encoder_init(encoder_t *enc, const str *); -static int avc_encoder_input(encoder_t *enc, AVFrame **frame); -static void avc_encoder_close(encoder_t *enc); - -static const char *g726_encoder_init(encoder_t *enc, const str *); - -static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out); -static void amr_encoder_got_packet(encoder_t *enc); -static int ilbc_decoder_input(decoder_t *dec, const str *data, GQueue *out); - -static const char *dtmf_decoder_init(decoder_t *, const str *); -static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out); - -static const char *cn_decoder_init(decoder_t *, const str *); -static int cn_decoder_input(decoder_t *dec, const str *data, GQueue *out); - -static int format_cmp_ignore(const struct rtp_payload_type *, const struct rtp_payload_type *); - static int generic_silence_dtx(decoder_t *, GQueue *, int); -static int amr_dtx(decoder_t *, GQueue *, int); -static int evs_dtx(decoder_t *, GQueue *, int); static int generic_cn_dtx_init(decoder_t *); static void generic_cn_dtx_cleanup(decoder_t *); static int generic_cn_dtx(decoder_t *, GQueue *, int); -#if defined(__x86_64__) -// mvr2s_x64_avx2.S -void mvr2s_avx2(float *in, const uint16_t len, int16_t *out); - -// mvr2s_x64_avx512.S -void mvr2s_avx512(float *in, const uint16_t len, int16_t *out); -#endif - - - -static void *evs_lib_handle; -static unsigned int evs_decoder_size; -static unsigned int evs_encoder_size; -static unsigned int evs_encoder_ind_list_size; -static void (*evs_init_decoder)(void *); -static void (*evs_init_encoder)(void *); -static void (*evs_destroy_decoder)(void *); -static void (*evs_destroy_encoder)(void *); -static void (*evs_set_encoder_opts)(void *, unsigned long, void *); -static void (*evs_set_encoder_brate)(void *, unsigned long br, unsigned int bwidth, - unsigned int mode, unsigned int amr); -static void (*evs_set_decoder_Fs)(void *, unsigned long); -static void (*evs_enc_in)(void *, const uint16_t *s, const uint16_t n); -static void (*evs_amr_enc_in)(void *, const uint16_t *s, const uint16_t n); -static void (*evs_enc_out)(void *, unsigned char *buf, uint16_t *len); -static void (*evs_dec_in)(void *, char *in, uint16_t len, uint16_t amr_mode, uint16_t core_mode, - uint16_t q_bit, uint16_t partial_frame, uint16_t next_type); -static void (*evs_dec_out)(void *, void *, int frame_mode); // frame_mode=1: missing -static void (*evs_dec_inc_frame)(void *); -static void (*evs_amr_dec_out)(void *, void *); -static void (*evs_syn_output)(float *in, const uint16_t len, int16_t *out); -static void (*evs_reset_enc_ind)(void *); - -static void evs_def_init(struct codec_def_s *); -static const char *evs_decoder_init(decoder_t *, const str *); -static int evs_decoder_input(decoder_t *dec, const str *data, GQueue *out); -static void evs_decoder_close(decoder_t *); -static const char *evs_encoder_init(encoder_t *enc, const str *); -static int evs_encoder_input(encoder_t *enc, AVFrame **frame); -static void evs_encoder_close(encoder_t *); -static format_parse_f evs_format_parse; -static format_cmp_f evs_format_cmp; -static format_print_f evs_format_print; -static format_answer_f evs_format_answer; -static select_encoder_format_f evs_select_encoder_format; - - - - static void *cc_lib_handle; #ifdef HAVE_CODEC_CHAIN @@ -242,678 +133,29 @@ codec_cc_t *(*codec_cc_new)(codec_def_t *src, format_t *src_format, codec_def_t -static const codec_type_t codec_type_avcodec = { - .def_init = avc_def_init, - .decoder_init = avc_decoder_init, - .decoder_input = avc_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_type_t codec_type_g726 = { +const codec_type_t codec_type_avcodec = { .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_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_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_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 codec_type_t codec_type_evs = { - .def_init = evs_def_init, - .decoder_init = evs_decoder_init, - .decoder_input = evs_decoder_input, - .decoder_close = evs_decoder_close, - .encoder_init = evs_encoder_init, - .encoder_input = evs_encoder_input, -// .encoder_got_packet = amr_encoder_got_packet, - .encoder_close = evs_encoder_close, -}; -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 dtx_method_t dtx_method_silence = { +const dtx_method_t dtx_method_silence = { .method_id = DTX_SILENCE, .do_dtx = generic_silence_dtx, }; -static const dtx_method_t dtx_method_cn = { +const dtx_method_t dtx_method_cn = { .method_id = DTX_CN, .do_dtx = generic_cn_dtx, .init = generic_cn_dtx_init, .cleanup = generic_cn_dtx_cleanup, }; -static const dtx_method_t dtx_method_amr = { - .method_id = DTX_NATIVE, - .do_dtx = amr_dtx, -}; -static const dtx_method_t dtx_method_evs = { - .method_id = DTX_NATIVE, - .do_dtx = evs_dtx, -}; - -#ifdef HAVE_BCG729 -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, -}; -#endif - - -static struct codec_def_s __codec_defs[] = { - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - }, -#ifndef HAVE_BCG729 - { - .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, - }, - { - .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 - { - .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, - }, - { - .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, - }, -#endif - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - { - .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, - }, - { - .rtpname = "EVS", - .avcodec_id = -1, - .default_clockrate_fact = {3,1}, - .default_clockrate = 16000, - .default_channels = 1, - .default_ptime = 20, - .default_bitrate = 16400, - .default_fmtp = "dtx=0;dtx-recv=0", - .format_parse = evs_format_parse, - .format_cmp = evs_format_cmp, - .format_print = evs_format_print, - .format_answer = evs_format_answer, - .select_encoder_format = evs_select_encoder_format, - .packetizer = packetizer_passthrough, - .bits_per_sample = 1, - .evs = 1, - .media_type = MT_AUDIO, - .codec_type = &codec_type_evs, - .dtx_methods = { - [DTX_NATIVE] = &dtx_method_evs, - [DTX_SILENCE] = &dtx_method_silence, - [DTX_CN] = &dtx_method_cn, - }, - }, - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - }, -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - }, -#endif - { - .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, - }, - }, - { - .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, - }, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - { - .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, - }, - // for file reading and writing - { - .rtpname = "PCM-U8", - .avcodec_id = AV_CODEC_ID_PCM_U8, - .packetizer = packetizer_passthrough, - .media_type = MT_AUDIO, - .codec_type = &codec_type_avcodec, - }, - { - .rtpname = "MP3", - .avcodec_id = AV_CODEC_ID_MP3, - .packetizer = packetizer_passthrough, - .media_type = MT_AUDIO, - .codec_type = &codec_type_avcodec, - }, -}; +static struct codec_def_s *__codec_defs; +static unsigned int __num_codec_defs; static GQueue __supplemental_codecs = G_QUEUE_INIT; const GQueue * const codec_supplemental_codecs = &__supplemental_codecs; @@ -951,7 +193,7 @@ codec_def_t *codec_get_pcm16(void) { -static const char *avc_decoder_init(decoder_t *dec, const str *extra_opts) { +const char *avc_decoder_init(decoder_t *dec, const str *extra_opts) { const AVCodec *codec = dec->def->decoder; if (!codec) return "codec not supported"; @@ -1130,7 +372,7 @@ gboolean decoder_has_dtx(decoder_t *dec) { } -static void avc_decoder_close(decoder_t *dec) { +void avc_decoder_close(decoder_t *dec) { #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 1, 0) avcodec_free_context(&dec->avc.avcctx); #else @@ -1156,7 +398,7 @@ void decoder_close(decoder_t *dec) { } -static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out) { +int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out) { if (!dec->avc.avpkt) return -1; // decoder shut down @@ -1377,7 +619,7 @@ static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) { } -static void avc_def_init(struct codec_def_s *def) { +void avc_def_init(struct codec_def_s *def) { // look up AVCodec structs if (def->avcodec_name_enc) def->encoder = avcodec_find_encoder_by_name(def->avcodec_name_enc); @@ -1407,8 +649,6 @@ void codeclib_free(void) { t_hash_table_destroy(generic_ffmpeg_codecs); avformat_network_deinit(); cc_cleanup(); - if (evs_lib_handle) - dlclose(evs_lib_handle); if (cc_lib_handle) dlclose(cc_lib_handle); } @@ -1451,7 +691,7 @@ bool rtpe_has_cpu_flag(enum rtpe_cpu_flag flag) { } -static void *dlsym_assert(void *handle, const char *sym, const char *fn) { +void *dlsym_assert(void *handle, const char *sym, const char *fn) { void *ret = dlsym(handle, sym); if (!ret) die("Failed to resolve symbol '%s' from '%s': %s", sym, fn, dlerror()); @@ -1616,6 +856,22 @@ static void cc_cleanup(void) { } #endif + +void codeclib_register_codec(const codec_def_t *c) { + struct codec_def_s *n = realloc(__codec_defs, sizeof(*__codec_defs) * (__num_codec_defs + 1)); + if (!n) { + fprintf(stderr, "Out of memory initialising codecs\n"); + abort(); + } + + __codec_defs = n; + + memcpy(&__codec_defs[__num_codec_defs], c, sizeof(*c)); + + __num_codec_defs++; +} + + void codeclib_init(int print) { #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) av_register_all(); @@ -1630,7 +886,7 @@ void codeclib_init(int print) { cc_init(); - for (int i = 0; i < G_N_ELEMENTS(__codec_defs); i++) { + for (unsigned int i = 0; i < __num_codec_defs; i++) { // add to hash table struct codec_def_s *def = &__codec_defs[i]; def->rtpname_str = STR(def->rtpname); @@ -1880,7 +1136,7 @@ encoder_t *encoder_new(void) { return ret; } -static const char *avc_encoder_init(encoder_t *enc, const str *extra_opts) { +const char *avc_encoder_init(encoder_t *enc, const str *extra_opts) { enc->avc.codec = enc->def->encoder; if (!enc->avc.codec) return "output codec not found"; @@ -1935,16 +1191,6 @@ static const char *avc_encoder_init(encoder_t *enc, const str *extra_opts) { return NULL; } -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; -} - int encoder_config(encoder_t *enc, codec_def_t *def, int bitrate, int ptime, const format_t *requested_format, format_t *actual_format) { @@ -2035,7 +1281,7 @@ err: return -1; } -static void avc_encoder_close(encoder_t *enc) { +void avc_encoder_close(encoder_t *enc) { if (enc->avc.avcctx) { #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(61, 0, 0) avcodec_close(enc->avc.avcctx); @@ -2066,7 +1312,7 @@ void encoder_free(encoder_t *enc) { g_free(enc); } -static int avc_encoder_input(encoder_t *enc, AVFrame **frame) { +int avc_encoder_input(encoder_t *enc, AVFrame **frame) { int keep_going = 0; int got_packet = 0; int av_ret = 0; @@ -2236,7 +1482,7 @@ int packetizer_passthrough(AVPacket *pkt, GString *buf, str *output, size_t num_ // returns: -1 = not enough data, nothing returned; 0 = returned a packet; // 1 = returned a packet and there's more -static int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output, size_t num_bytes, +int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output, size_t num_bytes, encoder_t *enc, int64_t *__restrict pts, int64_t *__restrict duration) { // avoid moving buffers around if possible: @@ -2274,7 +1520,7 @@ static int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_outpu } -static int codeclib_set_av_opt_int(encoder_t *enc, const char *opt, int64_t val) { +int codeclib_set_av_opt_int(encoder_t *enc, const char *opt, int64_t val) { ilog(LOG_DEBUG, "Setting ffmpeg '%s' option for '%s' to %" PRId64, opt, enc->def->rtpname, val); @@ -2286,2803 +1532,158 @@ static int codeclib_set_av_opt_int(encoder_t *enc, const char *opt, int64_t val) opt, enc->def->rtpname, val, av_error(ret)); return -1; } -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); -} +void codeclib_key_value_parse(const str *instr, bool need_value, + void (*cb)(str *key, str *value, void *data), void *data) +{ + if (!instr || !instr->s) + return; + + // semicolon-separated key=value + str s = *instr; + str key, value; + while (str_token_sep(&value, &s, ';')) { + if (!str_token(&key, &value, '=')) { + if (need_value) + continue; + value = STR_NULL; + } + // truncate whitespace + while (key.len && key.s[0] == ' ') + str_shift(&key, 1); + while (key.len && key.s[key.len - 1] == ' ') + key.len--; + while (value.len && value.s[0] == ' ') + str_shift(&value, 1); + while (value.len && value.s[value.len - 1] == ' ') + value.len--; -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; - } + if (key.len == 0) + continue; - 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; + cb(&key, &value, data); } - 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(); +static int generic_silence_dtx(decoder_t *dec, GQueue *out, int ptime) { + if (dec->dec_out_format.format == -1) + return -1; + if (!dec->avc.avpkt) + return -1; + + if (ptime <= 0) + ptime = 20; + int num_samples = ptime * dec->in_format.clockrate / 1000; + ilog(LOG_DEBUG, "pushing %i silence samples into %s decoder", num_samples, dec->def->rtpname); - 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)); + // create dummy frame, fill with silence, pretend it was returned from the decoder + AVFrame *frame = av_frame_alloc(); + frame->nb_samples = num_samples; + frame->format = dec->dec_out_format.format; + frame->sample_rate = dec->dec_out_format.clockrate; + DEF_CH_LAYOUT(&frame->CH_LAYOUT, dec->dec_out_format.channels); + if (av_frame_get_buffer(frame, 0) < 0) { av_frame_free(&frame); return -1; } - frame->nb_samples = ret; + memset(frame->extended_data[0], 0, frame->linesize[0]); + + // advance PTS + frame->pts = dec->avc.avpkt->pts; + dec->avc.avpkt->pts += frame->nb_samples; + 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 int cn_append_frame(decoder_t *dec, AVFrame *f, void *u1, void *u2) { + GQueue *out = u1; + g_queue_push_tail(out, f); + return 0; } -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)); +static int generic_cn_dtx(decoder_t *dec, GQueue *out, int ptime) { + dec->dtx.cn.cn_dec->ptime = ptime; + return decoder_input_data(dec->dtx.cn.cn_dec, dec->dtx.cn.cn_payload, + dec->rtp_ts, cn_append_frame, out, NULL); +} - return NULL; +static int generic_cn_dtx_init(decoder_t *dec) { + // upsample CN output to same params as output of parent codec + format_t cn_format = dec->dest_format; + cn_format.channels = dec->in_format.channels; + cn_format.clockrate = dec->in_format.clockrate; + dec->dtx.cn.cn_dec = decoder_new_fmt(codec_def_cn, 8000, 1, dec->ptime, &cn_format); + return 0; } -static void libopus_encoder_close(encoder_t *enc) { - opus_encoder_destroy(enc->opus); + +static void generic_cn_dtx_cleanup(decoder_t *dec) { + decoder_close(dec->dtx.cn.cn_dec); } -#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; +int format_cmp_ignore(const struct rtp_payload_type *a, const struct rtp_payload_type *b) { 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) +void frame_fill_tone_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num, + unsigned int freq, unsigned int volume, unsigned int sample_rate, unsigned int channels) { - 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}; + switch (fmt) { + case AV_SAMPLE_FMT_S16: + tone_samples_int16_t(samples, offset, num, freq, volume, sample_rate, channels); 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}; + case AV_SAMPLE_FMT_S32: + tone_samples_int32_t(samples, offset, num, freq, volume, sample_rate, channels); 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}; + case AV_SAMPLE_FMT_DBL: + tone_samples_double(samples, offset, num, freq, volume, sample_rate, channels); + break; + case AV_SAMPLE_FMT_FLT: + tone_samples_float(samples, offset, num, freq, volume, sample_rate, channels); 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}; + ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unsupported sample format %u", fmt); 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 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 void codeclib_key_value_parse(const str *instr, bool need_value, - void (*cb)(str *key, str *value, void *data), void *data) -{ - if (!instr || !instr->s) - return; - - // semicolon-separated key=value - str s = *instr; - str key, value; - while (str_token_sep(&value, &s, ';')) { - if (!str_token(&key, &value, '=')) { - if (need_value) - continue; - value = STR_NULL; - } - - // truncate whitespace - while (key.len && key.s[0] == ' ') - str_shift(&key, 1); - while (key.len && key.s[key.len - 1] == ' ') - key.len--; - while (value.len && value.s[0] == ' ') - str_shift(&value, 1); - while (value.len && value.s[value.len - 1] == ' ') - value.len--; - - if (key.len == 0) - continue; - - cb(&key, &value, data); - } - -} - - - - - -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 int generic_silence_dtx(decoder_t *dec, GQueue *out, int ptime) { - if (dec->dec_out_format.format == -1) - return -1; - if (!dec->avc.avpkt) - return -1; - - if (ptime <= 0) - ptime = 20; - int num_samples = ptime * dec->in_format.clockrate / 1000; - ilog(LOG_DEBUG, "pushing %i silence samples into %s decoder", num_samples, dec->def->rtpname); - - // create dummy frame, fill with silence, pretend it was returned from the decoder - AVFrame *frame = av_frame_alloc(); - frame->nb_samples = num_samples; - frame->format = dec->dec_out_format.format; - frame->sample_rate = dec->dec_out_format.clockrate; - DEF_CH_LAYOUT(&frame->CH_LAYOUT, dec->dec_out_format.channels); - if (av_frame_get_buffer(frame, 0) < 0) { - av_frame_free(&frame); - return -1; - } - - memset(frame->extended_data[0], 0, frame->linesize[0]); - - // advance PTS - frame->pts = dec->avc.avpkt->pts; - dec->avc.avpkt->pts += frame->nb_samples; - - g_queue_push_tail(out, frame); - - return 0; -} - - -static int cn_append_frame(decoder_t *dec, AVFrame *f, void *u1, void *u2) { - GQueue *out = u1; - g_queue_push_tail(out, f); - return 0; -} - -static int generic_cn_dtx(decoder_t *dec, GQueue *out, int ptime) { - dec->dtx.cn.cn_dec->ptime = ptime; - return decoder_input_data(dec->dtx.cn.cn_dec, dec->dtx.cn.cn_payload, - dec->rtp_ts, cn_append_frame, out, NULL); -} - -static int generic_cn_dtx_init(decoder_t *dec) { - // upsample CN output to same params as output of parent codec - format_t cn_format = dec->dest_format; - cn_format.channels = dec->in_format.channels; - cn_format.clockrate = dec->in_format.clockrate; - dec->dtx.cn.cn_dec = decoder_new_fmt(codec_def_cn, 8000, 1, dec->ptime, &cn_format); - return 0; -} - -static void generic_cn_dtx_cleanup(decoder_t *dec) { - decoder_close(dec->dtx.cn.cn_dec); -} - - - - -#ifdef HAVE_BCG729 -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 - - -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 int format_cmp_ignore(const struct rtp_payload_type *a, const struct rtp_payload_type *b) { - 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; -} - - -void frame_fill_tone_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num, - unsigned int freq, unsigned int volume, unsigned int sample_rate, unsigned int channels) -{ - switch (fmt) { - case AV_SAMPLE_FMT_S16: - tone_samples_int16_t(samples, offset, num, freq, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_S32: - tone_samples_int32_t(samples, offset, num, freq, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_DBL: - tone_samples_double(samples, offset, num, freq, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_FLT: - tone_samples_float(samples, offset, num, freq, volume, sample_rate, channels); - break; - default: - ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unsupported sample format %u", fmt); - break; - } -} - -void frame_fill_dtmf_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num, - unsigned int event, unsigned int volume, unsigned int sample_rate, unsigned int channels) -{ - switch (fmt) { - case AV_SAMPLE_FMT_S16: - dtmf_samples_int16_t(samples, offset, num, event, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_S32: - dtmf_samples_int32_t(samples, offset, num, event, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_DBL: - dtmf_samples_double(samples, offset, num, event, volume, sample_rate, channels); - break; - case AV_SAMPLE_FMT_FLT: - dtmf_samples_float(samples, offset, num, event, volume, sample_rate, channels); - break; - default: - ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unsupported sample format %u", fmt); - break; - } -} - - - - -// lamely parse out decimal numbers without using floating point -static unsigned int str_to_i_k(str *s) { - str intg; - str frac = *s; - if (str_token(&intg, &frac, '.')) { - unsigned int ret = str_to_i(s, 0) * 1000; - if (frac.len > 1) // at most one decimal digit - frac.len = 1; - return ret + str_to_i(&frac, 0) * 100; - } - return str_to_i(s, 0) * 1000; -} - -static const char *evs_bw_strings[__EVS_BW_MAX] = { "nb", "wb", "swb", "fb" }; - -static void evs_parse_bw(enum evs_bw *minp, enum evs_bw *maxp, const str *token) { - switch (__csh_lookup(token)) { - case CSH_LOOKUP("nb"): - *maxp = EVS_BW_NB; - break; - case CSH_LOOKUP("wb"): - *maxp = EVS_BW_WB; - break; - case CSH_LOOKUP("swb"): - *maxp = EVS_BW_SWB; - break; - case CSH_LOOKUP("fb"): - *maxp = EVS_BW_FB; - break; - case CSH_LOOKUP("nb-wb"): - *minp = EVS_BW_NB; - *maxp = EVS_BW_WB; - break; - case CSH_LOOKUP("nb-swb"): - *minp = EVS_BW_NB; - *maxp = EVS_BW_SWB; - break; - case CSH_LOOKUP("nb-fb"): - *minp = EVS_BW_NB; - *maxp = EVS_BW_FB; - break; - // the ones below are not mentioned in the spec - lower bound ignored - case CSH_LOOKUP("wb-swb"): - *minp = EVS_BW_WB; - *maxp = EVS_BW_SWB; - break; - case CSH_LOOKUP("wb-fb"): - *minp = EVS_BW_WB; - *maxp = EVS_BW_FB; - break; - case CSH_LOOKUP("swb-fb"): - *minp = EVS_BW_SWB; - *maxp = EVS_BW_FB; - break; - default: - ilog(LOG_WARN, "EVS: bandwidth selection '" STR_FORMAT "' not understood", - STR_FMT(token)); - } -} -static void evs_parse_br(unsigned int *minp, unsigned int *maxp, str *token) { - str min; - str max = *token; - if (str_token(&min, &max, '-')) { - *minp = str_to_i_k(&min); - *maxp = str_to_i_k(&max); - } - else - *minp = *maxp = str_to_i_k(token); - if (*minp > *maxp) { - ilog(LOG_WARN, "EVS: min bitrate %u is larger than max bitrate %u", - *minp, *maxp); - *maxp = *minp; - } -} -// lamely print fractional number -static void evs_print_frac_num(GString *s, unsigned int num) { - unsigned int frac = (num / 100 % 10); - unsigned int intg = num / 1000; - if (frac) - g_string_append_printf(s, "%u.%u", intg, frac); - else - g_string_append_printf(s, "%u", intg); -} -static void evs_format_print_br(GString *s, const char *k, unsigned int min, unsigned int max) { - if (!max) - return; - - g_string_append(s, k); - g_string_append_c(s, '='); - - if (min != max) { - evs_print_frac_num(s, min); - g_string_append_c(s, '-'); - } - evs_print_frac_num(s, max); - g_string_append(s, "; "); -} -static void evs_format_print_bw(GString *s, const char *k, enum evs_bw min, enum evs_bw max) { - if (max == EVS_BW_UNSPEC) - return; - - g_string_append(s, k); - g_string_append_c(s, '='); - - if (min != EVS_BW_UNSPEC) { - g_string_append(s, evs_bw_strings[min]); - g_string_append_c(s, '-'); - } - g_string_append(s, evs_bw_strings[max]); - g_string_append(s, "; "); -} -static GString *evs_format_print(const struct rtp_payload_type *p) { - if (!p->format.fmtp_parsed) - return false; - - GString *s = g_string_new(""); - __auto_type f = &p->format.parsed.evs; - - if (f->hf_only) - g_string_append(s, "hf-only=1; "); - if (f->no_dtx) - g_string_append(s, "dtx=0; "); - if (f->no_dtx_recv) - g_string_append(s, "dtx-recv=0; "); - if (f->cmr) - g_string_append_printf(s, "cmr=%i; ", f->cmr); - - if (f->amr_io) { - // AMR - g_string_append(s, "evs-mode-switch=1; "); - - if (f->mode_set) { - g_string_append(s, "mode-set="); - for (unsigned int i = 0; i < 8; i++) { - if ((f->mode_set & (1 << i))) - g_string_append_printf(s, "%u,", i); - } - g_string_truncate(s, s->len - 1); // remove trailing "," - g_string_append(s, "; "); - } - - if (f->mode_change_neighbor) - g_string_append(s, "mode-change-neighbor=1; "); - if (f->mode_change_period) - g_string_append_printf(s, "mode-change-period=%i; ", f->mode_change_period); - } - else { - // EVS - evs_format_print_br(s, "br", f->min_br, f->max_br); - evs_format_print_br(s, "br-send", f->min_br_send, f->max_br_send); - evs_format_print_br(s, "br-recv", f->min_br_recv, f->max_br_recv); - - evs_format_print_bw(s, "bw", f->min_bw, f->max_bw); - evs_format_print_bw(s, "bw-send", f->min_bw_send, f->max_bw_send); - evs_format_print_bw(s, "bw-recv", f->min_bw_recv, f->max_bw_recv); - } - - if (s->len != 0) - g_string_truncate(s, s->len - 2); // remove trailing "; " if anything was printed - - return s; -} -static void evs_parse_format_cb(str *key, str *token, void *data) { - union codec_format_options *opts = data; - __auto_type o = &opts->evs; - - switch (__csh_lookup(key)) { - case CSH_LOOKUP("hf-only"): - if (token->len == 1 && token->s[0] == '1') - o->hf_only = 1; - break; - case CSH_LOOKUP("evs-mode-switch"): - if (token->len == 1 && token->s[0] == '1') - o->amr_io = 1; - break; - case CSH_LOOKUP("dtx"): - if (token->len == 1 && token->s[0] == '0') - o->no_dtx = 1; - break; - case CSH_LOOKUP("dtx-recv"): - if (token->len == 1 && token->s[0] == '0') - o->no_dtx_recv = 1; - break; - case CSH_LOOKUP("cmr"): - if (token->len == 1 && token->s[0] == '1') - o->cmr = 1; - else if (token->len == 2 && token->s[0] == '-' && token->s[1] == '1') - o->cmr = -1; - break; - case CSH_LOOKUP("br"): - evs_parse_br(&o->min_br, &o->max_br, token); - break; - case CSH_LOOKUP("br-send"): - evs_parse_br(&o->min_br_send, &o->max_br_send, token); - break; - case CSH_LOOKUP("br-recv"): - evs_parse_br(&o->min_br_recv, &o->max_br_recv, token); - break; - case CSH_LOOKUP("bw"): - evs_parse_bw(&o->min_bw, &o->max_bw, token); - break; - case CSH_LOOKUP("bw-send"): - evs_parse_bw(&o->min_bw_send, &o->max_bw_send, token); - break; - case CSH_LOOKUP("bw-recv"): - evs_parse_bw(&o->min_bw_recv, &o->max_bw_recv, token); - 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 > 8) - continue; - o->mode_set |= (1 << m); - } - break; - case CSH_LOOKUP("mode-change-period"): - o->mode_change_period = str_to_i(token, 0); - break; - case CSH_LOOKUP("mode-change-neighbor"): - if (token->len == 1 && token->s[0] == '1') - o->mode_change_neighbor = 1; - break; - } -} -static bool evs_format_parse(struct rtp_codec_format *f, const str *fmtp) { - // initialise - f->parsed.evs.max_bw = EVS_BW_UNSPEC; - f->parsed.evs.min_bw = EVS_BW_UNSPEC; - f->parsed.evs.max_bw_send = EVS_BW_UNSPEC; - f->parsed.evs.min_bw_send = EVS_BW_UNSPEC; - f->parsed.evs.max_bw_recv = EVS_BW_UNSPEC; - f->parsed.evs.min_bw_recv = EVS_BW_UNSPEC; - - codeclib_key_value_parse(fmtp, true, evs_parse_format_cb, &f->parsed); - return true; -} -static void evs_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.evs; - - // swap send/recv - - __auto_type t1 = f->max_br_recv; - f->max_br_recv = f->max_br_send; - f->max_br_send = t1; - - t1 = f->min_br_recv; - f->min_br_recv = f->min_br_send; - f->min_br_send = t1; - - __auto_type t2 = f->max_bw_recv; - f->max_bw_recv = f->max_bw_send; - f->max_bw_send = t2; - - t2 = f->min_bw_recv; - f->min_bw_recv = f->min_bw_send; - f->min_bw_send = t2; -} -static int evs_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.evs; - __auto_type b = &B->format.parsed.evs; - - // reject what is incompatible - if (a->amr_io != b->amr_io) - return -1; - if (a->hf_only != b->hf_only) - return -1; - - // determine whether we are compatible - int compat = 0; - -#define FEATURE_CMP(field, compat_op, undefined_val) \ - if (a->field != undefined_val && b->field != undefined_val) { \ - if (a->field == b->field) \ - ; \ - else if (a->field compat_op b->field) \ - compat++; \ - else \ - return -1; \ - } \ - else if (a->field == undefined_val && b->field != undefined_val) /* `a` is broader than `b` */ \ - compat++; \ - else if (a->field != undefined_val && b->field == undefined_val) \ - return -1; - if (!a->amr_io) { - // EVS - FEATURE_CMP(max_br, >, 0) - FEATURE_CMP(min_br, <, 0) - FEATURE_CMP(max_br_recv, >, 0) - FEATURE_CMP(min_br_recv, <, 0) - FEATURE_CMP(max_br_send, >, 0) - FEATURE_CMP(min_br_send, <, 0) - - FEATURE_CMP(max_bw, >, EVS_BW_UNSPEC) - FEATURE_CMP(min_bw, <, EVS_BW_UNSPEC) - FEATURE_CMP(max_bw_recv, >, EVS_BW_UNSPEC) - FEATURE_CMP(min_bw_recv, <, EVS_BW_UNSPEC) - FEATURE_CMP(max_bw_send, >, EVS_BW_UNSPEC) - FEATURE_CMP(min_bw_send, <, EVS_BW_UNSPEC) - } - else { - // AMR - int match = amr_mode_set_cmp(a->mode_set, b->mode_set); - if (match == 1) - compat++; - else if (match == -1) - return -1; - } - -#undef FEATURE_CMP - - return (compat == 0) ? 0 : 1; -} -// EVS RTP always runs at 16 kHz -static void evs_select_encoder_format(encoder_t *enc, format_t *req_format, const format_t *f, - const struct rtp_codec_format *fmtp) +void frame_fill_dtmf_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num, + unsigned int event, unsigned int volume, unsigned int sample_rate, unsigned int channels) { - if (req_format->clockrate != 16000) - return; // bail - encoder will fail to initialise - - // check against natively supported rates first - switch (f->clockrate) { - case 48000: - case 32000: - case 16000: - enc->clockrate_fact = (struct fraction) {48000 / f->clockrate, 1}; - break; - case 8000: - enc->clockrate_fact = (struct fraction) {1, 16000 / f->clockrate}; - break; - default: - // resample to next best rate - if (f->clockrate > 32000) - enc->clockrate_fact = (struct fraction) {3,1}; - else if (f->clockrate > 16000) - enc->clockrate_fact = (struct fraction) {2,1}; - else if (f->clockrate > 8000) - enc->clockrate_fact = (struct fraction) {1,1}; - else - enc->clockrate_fact = (struct fraction) {1,2}; + switch (fmt) { + case AV_SAMPLE_FMT_S16: + dtmf_samples_int16_t(samples, offset, num, event, volume, sample_rate, channels); break; - } -} - - - -static const char *evs_decoder_init(decoder_t *dec, const str *extra_opts) { - dec->evs = g_malloc0(evs_decoder_size); - if (dec->in_format.clockrate != 48000) - ilog(LOG_WARN, "EVS: invalid decoder clock rate (%i) requested", - fraction_div(dec->in_format.clockrate, &dec->clockrate_fact)); - if (dec->in_format.channels != 1) - ilog(LOG_WARN, "EVS: %i-channel EVS is not supported", - dec->in_format.channels); - dec->in_format.clockrate = 48000; - evs_set_decoder_Fs(dec->evs, dec->in_format.clockrate); - evs_init_decoder(dec->evs); - return NULL; -} -static void evs_decoder_close(decoder_t *dec) { - evs_destroy_decoder(dec->evs); - g_free(dec->evs); -} - - - -// upper 16 bits: 0 = EVS, 1 = AMR -// lower 8 bits: mode num -// 0x000000AA = mode num -// 0x00AAAA00 = actual number of bits -// 0xAA000000 = 0=EVS, 1=AMR -// -1 == invalid -static int32_t evs_mode_from_bytes(int bytes) { - switch (bytes) { - // EVS - case 7: // 2.8 - return 0 | (56 << 8); - case 18: // 7.2 - return 1 | (144 << 8); - case 20: // 8.0 - return 2 | (160 << 8); - case 24: // 9.6 - return 3 | (192 << 8); - case 33: // 13.2 - return 4 | (264 << 8); - case 41: // 16.4 - return 5 | (328 << 8); - case 61: // 24.4 - return 6 | (488 << 8); - case 80: // 32.0 - return 7 | (640 << 8); - case 120: // 48.8 - return 8 | (960 << 8); - case 160: // 64.0 - return 9 | (1280 << 8); - case 240: // 96.0 - return 10 | (1920 << 8); - case 320: // 128.0 - return 11 | (2560 << 8); - case 6: // sid - return 12 | (48 << 8); - // AMR - case 17: // (16.5) 6.60 kbit/s // 0 - return 0 | 0x01000000 | (132 << 8); - case 23: // (22.125) 8.85 kbit/s // 1 - return 1 | 0x01000000 | (177 << 8); - case 32: // (31.625) 12.65 kbit/s // 2 - return 2 | 0x01000000 | (253 << 8); - case 36: // (35.625) 14.25 kbit/s // 3 - return 3 | 0x01000000 | (285 << 8); - case 40: // (39.625) 15.85 kbit/s // 4 - return 4 | 0x01000000 | (317 << 8); - case 46: // (45.625) 18.25 kbit/s // 5 - return 5 | 0x01000000 | (365 << 8); - case 50: // (49.625) 19.85 kbit/s // 6 - return 6 | 0x01000000 | (397 << 8); - case 58: // (57.625) 23.05 kbit/s // 7 - return 7 | 0x01000000 | (461 << 8); - case 60: // (59.625) 23.85 kbit/s // 8 - return 8 | 0x01000000 | (477 << 8); - case 5: // sid - return 9 | 0x01000000 | (40 << 8); - } - return -1; -} -static int32_t evs_mode_from_bitrate(int bitrate) { - int bytes_per_frame = ((bitrate / 50) + 7) / 8; - if (bytes_per_frame >= 7) - return evs_mode_from_bytes(bytes_per_frame); - return -1; -} - -static int evs_bitrate_mode(int bitrate) { - switch (bitrate) { - // EVS - case 2800: - case 5900: - case 7200: - case 8000: - case 13200: - case 32000: - case 64000: - // AMR - case 6600: - case 8850: - case 12650: - case 14250: - case 15850: - case 18250: - case 19850: - case 23050: - case 23850: - return 1; - // EVS - case 9600: - case 16400: - case 24400: - case 48000: - case 96000: - case 128000: - return 2; - } - return 0; -} - -static const int evs_mode_bits[2][16] = { - // EVS - { - 56, // 0 - 144, // 1 - 160, // 2 - 192, // 3 - 264, // 4 - 328, // 5 - 488, // 6 - 640, // 7 - 960, // 8 - 1280, // 9 - 1920, // 10 - 2560, // 11 - 48, // 12 - 0, // 13 invalid - 0, // 14 invalid - 0, // 15 invalid - }, - // AMR - { - 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 - 0, // invalid // 14 - 0, // invalid // 15 - }, -}; -static const int evs_mode_bitrates[2][16] = { - // EVS - { - 5900, // 0 (VBR) - 7200, // 1 - 8000, // 2 - 9600, // 3 - 13200, // 4 - 16400, // 5 - 24400, // 6 - 32000, // 7 - 48800, // 8 - 64000, // 9 - 96000, // 10 - 128000, // 11 - 0, // 12 SID - 0, // 13 invalid - 0, // 14 invalid - 0, // 15 invalid - }, - // AMR - { - 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 - 0, // invalid // 14 - 0, // invalid // 15 - }, -}; -static const uint8_t evs_min_max_modes_by_bw[__EVS_BW_MAX][2] = { - { 0, 6 }, // NB - { 0, 11 }, // WB - { 3, 11 }, // SWB - { 5, 11 }, // FB -}; -static uint8_t evs_clamp_mode_by_bw(const uint8_t mode, const enum evs_bw bw) { - if (mode < evs_min_max_modes_by_bw[bw][0]) - return evs_min_max_modes_by_bw[bw][0]; - else if (mode > evs_min_max_modes_by_bw[bw][1]) - return evs_min_max_modes_by_bw[bw][1]; - return mode; -} - -static int evs_match_bitrate(int orig_br, unsigned int amr) { - // is it already a valid bitrate? - int32_t mode = evs_mode_from_bitrate(orig_br); - if (mode >= 0) { - int bits = (mode >> 8) & 0xffff; - if (mode > 0 && (mode >> 24) == amr && bits * 50 == orig_br) - return orig_br; - } - - // find closest match - int max_mode = amr ? 8 : 11; - int test_mode = max_mode / 2; - int mode_off = (max_mode + 1) / 2; - bool last = false; - while (1) { - int new_br = evs_mode_bitrates[amr][test_mode]; - int new_off = (mode_off + 1) / 2; - if (new_br > orig_br) { - if (test_mode == 0 || last) - return new_br; - test_mode -= new_off; - } - else { // new_br < orig_br - if (test_mode == max_mode) - return new_br; - test_mode += new_off; - } - if (mode_off == 1) - last = true; - mode_off = new_off; - } -} - - - -static const char *evs_encoder_init(encoder_t *enc, const str *extra_opts) { - enc->evs.ctx = g_malloc0(evs_encoder_size); - enc->evs.ind_list = g_malloc(evs_encoder_ind_list_size); - if (enc->requested_format.channels != 1) - ilog(LOG_WARN, "EVS: %i-channel EVS is not supported", - enc->requested_format.channels); - enc->actual_format = enc->requested_format; - enc->actual_format.format = AV_SAMPLE_FMT_S16; - enc->samples_per_frame = enc->actual_format.clockrate * 20 / 1000; - - __auto_type o = &enc->format_options.evs; - - // determine max BW - if (o->max_bw_send != EVS_BW_UNSPEC) - enc->codec_options.evs.max_bw = o->max_bw_send; - else if (o->max_bw != EVS_BW_UNSPEC) - enc->codec_options.evs.max_bw = o->max_bw; - else - enc->codec_options.evs.max_bw = EVS_BW_WB; - assert(enc->codec_options.evs.max_bw >= 0 && enc->codec_options.evs.max_bw < __EVS_BW_MAX); - - switch (enc->requested_format.clockrate) { - case 48000: - case 32000: - if (enc->codec_options.evs.max_bw > EVS_BW_SWB) - enc->codec_options.evs.max_bw = EVS_BW_SWB; + case AV_SAMPLE_FMT_S32: + dtmf_samples_int32_t(samples, offset, num, event, volume, sample_rate, channels); break; - case 16000: - if (enc->codec_options.evs.max_bw > EVS_BW_WB) - enc->codec_options.evs.max_bw = EVS_BW_WB; + case AV_SAMPLE_FMT_DBL: + dtmf_samples_double(samples, offset, num, event, volume, sample_rate, channels); break; - case 8000: - enc->codec_options.evs.max_bw = EVS_BW_NB; + case AV_SAMPLE_FMT_FLT: + dtmf_samples_float(samples, offset, num, event, volume, sample_rate, channels); break; default: - ilog(LOG_WARN, "EVS: invalid encoder clock rate (%i) requested", - fraction_div(enc->requested_format.clockrate, &enc->clockrate_fact)); - } - evs_set_encoder_opts(enc->evs.ctx, enc->actual_format.clockrate, enc->evs.ind_list); - - // limit bitrate to given range - if (!o->amr_io) { - // EVS - if (o->max_br && enc->bitrate > o->max_br) - enc->bitrate = o->max_br; - if (o->min_br && enc->bitrate < o->max_br) - enc->bitrate = o->min_br; - - // verify bitrate - int bitrate = evs_match_bitrate(enc->bitrate, 0); - if (bitrate != enc->bitrate) { - ilog(LOG_INFO, "EVS: Using bitrate %i instead of %i", bitrate, enc->bitrate); - enc->bitrate = bitrate; - } - - // limit max bitrate to one supported by the selected BW - int32_t mode = evs_mode_from_bitrate(enc->bitrate); - if (mode == -1) - ilog(LOG_WARN, "EVS: ended up with unknown bitrate %i", enc->bitrate); - else { - mode &= 0xff; - mode = evs_clamp_mode_by_bw(mode, enc->codec_options.evs.max_bw); - bitrate = evs_mode_bitrates[0][mode]; - ilog(LOG_INFO, "EVS: using bitrate %i instead of %i as restricted by BW %i", - bitrate, enc->bitrate, enc->codec_options.evs.max_bw); - enc->bitrate = bitrate; - } - } - else { - // AMR - int32_t mode = evs_mode_from_bitrate(enc->bitrate); - if (mode != -1) { - if (mode >> 24 != 1) - mode = -1; // EVS bitrate - else if (o->mode_set) { - if ((o->mode_set & (1 << (mode & 0xff))) == 0) - mode = -1; // not part of the mode-set - } - } - if (mode == -1) { - // find closest match bitrate - int bitrate = evs_match_bitrate(enc->bitrate, 1); - mode = evs_mode_from_bitrate(bitrate); - if (mode == -1 || (mode >> 24 != 1)) - ilog(LOG_WARN, "EVS: ended up with unknown bitrate %i", bitrate); - else { - mode &= 0xff; - // restrict by mode-set if there is one - if (o->mode_set) { - if ((o->mode_set & (1 << (mode & 0xff))) == 0) { - // pick next higher mode if possible, otherwise go lower: - // clear lower unwanted modes from mode-set - unsigned int mode_set = o->mode_set & (0xfe << mode); - if (mode_set) { - // got a higher mode: which one? - mode = __builtin_ffs(mode_set) - 1; - } - else { - // no higher mode, get next lower one - mode = sizeof(int) * 8 - __builtin_clz(o->mode_set) - 1; - } - } - } - bitrate = evs_mode_bitrates[1][mode]; - ilog(LOG_INFO, "EVS: using bitrate %i instead of %i as restricted by mode-set", - bitrate, enc->bitrate); - enc->bitrate = bitrate; - } - } - } - - evs_set_encoder_brate(enc->evs.ctx, enc->bitrate, enc->codec_options.evs.max_bw, - evs_bitrate_mode(enc->bitrate), o->amr_io); - evs_init_encoder(enc->evs.ctx); - - return NULL; -} -static void evs_encoder_close(encoder_t *enc) { - evs_destroy_encoder(enc->evs.ctx); - g_free(enc->evs.ctx); - g_free(enc->evs.ind_list); -} - - - - -static void evs_handle_cmr(encoder_t *enc) { - if ((enc->callback.evs.cmr_in & 0x80) == 0) - return; - if (enc->callback.evs.cmr_in_ts == enc->evs.cmr_in_ts) - return; - - enc->evs.cmr_in_ts = enc->callback.evs.cmr_in_ts; // XXX should use a queue or something instead - - __auto_type f = &enc->format_options.evs; - __auto_type o = &enc->codec_options.evs; - unsigned char type = (enc->callback.evs.cmr_in >> 4) & 0x7; - unsigned char req = enc->callback.evs.cmr_in & 0xf; - int bitrate; - - if (type == 1) { - // AMR - if (!f->amr_io) - goto err; - if (req > 8) - goto err; - bitrate = evs_mode_bitrates[1][req]; - } - else if (type <= 4) { - // EVS modes - if (f->amr_io) - goto err; - if (req > 11) - goto err; - int bw = type; - if (bw >= 2) - bw--; // 0..3 - // ignore min BW - // instead of ignoring invalid request, clamp them to what is allowed by BW - if (o->max_bw != EVS_BW_UNSPEC && o->max_bw < bw) - bw = o->max_bw; - req = evs_clamp_mode_by_bw(req, bw); - bitrate = evs_mode_bitrates[0][req]; - } - else - goto err; - - enc->bitrate = bitrate; - evs_set_encoder_brate(enc->evs.ctx, bitrate, o->max_bw, - evs_bitrate_mode(bitrate), f->amr_io); - - return; - -err: - if (f->amr_io) - ilog(LOG_WARN | LOG_FLAG_LIMIT, "EVS: received invalid CMR (type %u, " - "request %u) in AMR mode", type, req); - else - ilog(LOG_WARN | LOG_FLAG_LIMIT, "EVS: received invalid CMR (type %u, " - "request %u) with BW <= %i", type, req, o->max_bw); -} - -static int evs_encoder_input(encoder_t *enc, AVFrame **frame) { - if (!*frame) - return 0; - - if ((*frame)->nb_samples != enc->actual_format.clockrate * 20 / 1000) { - ilog(LOG_ERR | LOG_FLAG_LIMIT, "EVS: input %u samples instead of %i", (*frame)->nb_samples, - enc->actual_format.clockrate * 20 / 1000); - return -1; - } - - evs_handle_cmr(enc); - - if (!enc->format_options.evs.amr_io) - evs_enc_in(enc->evs.ctx, (void *) (*frame)->extended_data[0], (*frame)->nb_samples); - else - evs_amr_enc_in(enc->evs.ctx, (void *) (*frame)->extended_data[0], (*frame)->nb_samples); - - // max output: 320 bytes, plus some overhead - av_new_packet(enc->avpkt, 340); - - unsigned char *out = enc->avpkt->data; - unsigned char *cmr = NULL; - - if (!enc->format_options.evs.amr_io) { - // EVS - if (enc->format_options.evs.cmr == 1) { - cmr = out; - *cmr = 0xff; // no CMR - out++; - } - } - else { - // AMR IO - if (!enc->format_options.evs.hf_only) { - // compact - cmr = out; - *cmr = 0xe0; // no CMR - out++; // to be shuffled below - } - else { - // header-full - if (enc->format_options.evs.cmr == 1) { - cmr = out; - *cmr = 0xff; // no CMR - out++; - } - } - } - - // TOC byte - unsigned char *toc = NULL; - if (enc->format_options.evs.hf_only) { - // header-full always has TOC - toc = out; - out++; - } - else { - // compact - if (cmr && !enc->format_options.evs.amr_io) { - // EVS with CMR is also header-full with TOC - toc = out; - out++; - } - } - - uint16_t bits = 0; - evs_enc_out(enc->evs.ctx, out, &bits); - uint16_t bytes = (bits + 7) / 8; - int32_t mode = evs_mode_from_bytes(bytes); - if (mode < 0) { - ilog(LOG_ERR | LOG_FLAG_LIMIT, "EVS: invalid encoding received from codec " - "(%i bits per frame)", bits); - av_packet_unref(enc->avpkt); - return -1; - } - evs_reset_enc_ind(enc->evs.ctx); - - if (toc) { - *toc = (mode & 0xff); - if (enc->format_options.evs.amr_io) - *toc |= 0x30; - } - - if (enc->format_options.evs.amr_io && !enc->format_options.evs.hf_only) { - // how many output bytes (frame minus CMR bits) total? - bytes = (bits - 5 + 7) / 8; - // bit-shuffle payload - unsigned char first = out[0]; - *cmr |= (first >> 2) & 0x1f; - // XXX accelerate with larger word sizes - for (int i = 0; i < bytes; i++) { - out[i] <<= 6; - out[i] |= out[i+1] >> 2; - } - // restore first bit, clear out tail end padding bits - unsigned int first_bit_shift = (bits + 2) % 8; - out[bytes-1] &= (0xff << (8 - first_bit_shift)); // clear leftovers - out[bytes-1] |= ((first & 0x80) >> first_bit_shift); // last/first bit - } - - bytes += (out - enc->avpkt->data); - assert(bytes <= enc->avpkt->size); - - if (toc && !enc->format_options.evs.amr_io && !enc->format_options.evs.hf_only) { - // hf-only=0 but HF packet, check for size collisions and zero-pad if needed - while (evs_mode_from_bytes(bytes) != -1) { - enc->avpkt->data[bytes] = '\0'; - bytes++; - } - } - - enc->avpkt->size = bytes; - enc->avpkt->pts = (*frame)->pts; - enc->avpkt->duration = (*frame)->nb_samples; - - return 0; -} - - -// 3GPP TS 26.445 A.2.1.2.1 -> A.2.2.1.1 -static const char evs_amr_io_compact_cmr[8] = { - 0x90 | 0, // 6.6 - 0x90 | 1, // 8.85 - 0x90 | 2, // 12.65 - 0x90 | 4, // 15.85 - 0x90 | 5, // 18.25 - 0x90 | 7, // 23.05 - 0x90 | 8, // 23.85 - 0xff // no req -}; - - -#if defined(__x86_64__) && !defined(ASAN_BUILD) && HAS_ATTR(ifunc) && defined(__GLIBC__) -static void mvr2s_dynlib_wrapper(float *in, const uint16_t len, int16_t *out) { - evs_syn_output(in, len, out); -} -static void (*resolve_float2int16_array(void))(float *, const uint16_t, int16_t *) { -#if defined(__x86_64__) - if (rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX512BW) && rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX512F)) - return mvr2s_avx512; - if (rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX2)) - return mvr2s_avx2; -#endif - return mvr2s_dynlib_wrapper; -} -static void float2int16_array(float *in, const uint16_t len, int16_t *out) - __attribute__ ((ifunc ("resolve_float2int16_array"))); -#else -#define float2int16_array evs_syn_output -#endif - - - -static void evs_push_frame(decoder_t *dec, char *frame_data, int bits, int is_amr, int mode, int q_bit, - GQueue *out) -{ - const unsigned int n_samples = 960; // fixed 20 ms ptime - uint64_t pts = dec->pts; - - AVFrame *frame = av_frame_alloc(); - frame->nb_samples = n_samples; - frame->format = AV_SAMPLE_FMT_S16; - frame->sample_rate = 48000; - DEF_CH_LAYOUT(&frame->CH_LAYOUT, 1); - frame->pts = pts; - if (av_frame_get_buffer(frame, 0) < 0) - abort(); - - evs_dec_in(dec->evs, frame_data, bits, is_amr, mode, q_bit, 0, 0); - - // check for floating point implementation - if (evs_syn_output) { - // temp float buffer - float tmp[n_samples * 3]; - if (!is_amr) - evs_dec_out(dec->evs, tmp, 0); - else - evs_amr_dec_out(dec->evs, tmp); - float2int16_array(tmp, n_samples, (void *) frame->extended_data[0]); - } - else { - if (!is_amr) - evs_dec_out(dec->evs, frame->extended_data[0], 0); - else - evs_amr_dec_out(dec->evs, frame->extended_data[0]); - } - - evs_dec_inc_frame(dec->evs); - - pts += n_samples; - dec->pts = pts; - - g_queue_push_tail(out, frame); -} - -static int evs_decoder_input(decoder_t *dec, const str *data, GQueue *out) { - str input = *data; - const char *err = NULL; - - if (input.len == 0) - return 0; - - str frame_data = STR_NULL; - const unsigned char *toc = NULL, *toc_end = NULL; - unsigned char cmr = 0xff; - // check for single frame in compact format - int32_t mode = evs_mode_from_bytes(input.len); - int is_amr, bits, q_bit; - if ((mode & 0xff0000ff) == 0) { - // special case, clause A.2.1.3 - if ((input.s[0] & 0x80)) { - // AMR in HF format with CMR - mode = -1; - } - } - if (mode != -1) { - // single compact frame: consume all - frame_data = input; - input.len = 0; - - // extract mode information - bits = (mode >> 8) & 0xffff; - is_amr = mode >> 24; - q_bit = 1; - mode = mode & 0xff; - - if (is_amr) { - // save and clear CMR - unsigned char *shifter = (unsigned char *) frame_data.s; // use unsigned - cmr = shifter[0] & 0xe0; - shifter[0] &= 0x1f; - - // convert CMR to full byte format - cmr >>= 5; // now guaranteed to be 0..7 - cmr = evs_amr_io_compact_cmr[cmr]; - - // bit shift payload - // XXX use larger word sizes - for (size_t i = 0; i < frame_data.len; i++) { - shifter[i] <<= 2; - shifter[i] |= shifter[i+1] >> 6; - } - // restore first bit - size_t first_bit_octet = bits / 8; - size_t first_bit_bit = bits % 8; - shifter[0] |= (shifter[first_bit_octet] << first_bit_bit) & 0x80; - } - } - else { - // header-full - toc = (unsigned char *) input.s; - str_shift(&input, 1); - // is this TOC or CMR? - if ((*toc & 0x80)) { - cmr = *toc; - toc = (unsigned char *) input.s; - err = "short packet (no TOC after CMR)"; - if (str_shift(&input, 1)) - goto err; - err = "invalid TOC byte"; - if ((*toc & 0x80)) - goto err; - } - // skip over all TOC entries - unsigned char toc_ent = *toc; - while ((toc_ent & 0x40)) { - toc_ent = *((unsigned char *) input.s); - err = "short packet (no repeating TOC)"; - if (str_shift(&input, 1)) - goto err; - } - // `toc` is now the first TOC entry and `input` points to the first speech frame - toc_end = (void *) input.s; - } - - while (1) { - // process frame if we have one; we don't have one if - // this is the first iteration and this is not a compact frame - if (mode != -1) - evs_push_frame(dec, frame_data.s, bits, is_amr, mode, q_bit, out); - - // anything left? we break here in compact mode - if (!input.len) - break; - - // if we're here, we're in HF mode: look at the next TOC and extract speech frame - if (toc >= toc_end) // leftover data/padding at the end + ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unsupported sample format %u", fmt); break; - mode = *toc & 0xf; - is_amr = (*toc >> 5) & 0x1; - if (is_amr) - q_bit = (*toc >> 4) & 0x1; - else - q_bit = 1; - bits = evs_mode_bits[is_amr][mode]; // guaranteed to be 0..1 and 0..15 - - // consume and shift - toc++; - int bytes = (bits + 7) / 8; - frame_data = STR_LEN(input.s, bytes); - err = "speech frame truncated"; - if (str_shift(&input, bytes)) - goto err; - } - - if (cmr != 0xff) - decoder_event(dec, CE_EVS_CMR_RECV, GUINT_TO_POINTER(cmr)); - - return 0; - -err: - if (err) - ilog(LOG_WARN | LOG_FLAG_LIMIT, "Error unpacking EVS packet: %s", err); - return -1; -} - - -static void evs_load_so(const char *path) { - if (!path) - return; - - evs_lib_handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); - if (!evs_lib_handle) - die("Failed to open EVS codec .so '%s': %s", path, dlerror()); - - static unsigned int (*get_evs_decoder_size)(void); - static unsigned int (*get_evs_encoder_size)(void); - static unsigned int (*get_evs_encoder_ind_list_size)(void); - - // flp codec? - evs_init_decoder = dlsym(evs_lib_handle, "init_decoder"); - if (!evs_init_decoder) { - // fx codec? - evs_init_decoder = dlsym_assert(evs_lib_handle, "init_decoder_fx", path); - evs_init_encoder = dlsym_assert(evs_lib_handle, "init_encoder_fx", path); - evs_destroy_encoder = dlsym_assert(evs_lib_handle, "destroy_encoder_fx", path); - evs_enc_in = dlsym_assert(evs_lib_handle, "evs_enc_fx", path); - evs_amr_enc_in = dlsym_assert(evs_lib_handle, "amr_wb_enc_fx", path); - evs_reset_enc_ind = dlsym_assert(evs_lib_handle, "reset_indices_enc_fx", path); - evs_dec_in = dlsym_assert(evs_lib_handle, "read_indices_from_djb_fx", path); - evs_dec_out = dlsym_assert(evs_lib_handle, "evs_dec_fx", path); - evs_amr_dec_out = dlsym_assert(evs_lib_handle, "amr_wb_dec_fx", path); } - else { - // flp codec - evs_init_encoder = dlsym_assert(evs_lib_handle, "init_encoder", path); - evs_destroy_encoder = dlsym_assert(evs_lib_handle, "destroy_encoder", path); - evs_enc_in = dlsym_assert(evs_lib_handle, "evs_enc", path); - evs_amr_enc_in = dlsym_assert(evs_lib_handle, "amr_wb_enc", path); - evs_reset_enc_ind = dlsym_assert(evs_lib_handle, "reset_indices_enc", path); - evs_dec_in = dlsym_assert(evs_lib_handle, "read_indices_from_djb", path); - evs_dec_out = dlsym_assert(evs_lib_handle, "evs_dec", path); - evs_syn_output = dlsym_assert(evs_lib_handle, "syn_output", path); - evs_amr_dec_out = dlsym_assert(evs_lib_handle, "amr_wb_dec", path); - } - - // common - get_evs_decoder_size = dlsym_assert(evs_lib_handle, "decoder_size", path); - get_evs_encoder_size = dlsym_assert(evs_lib_handle, "encoder_size", path); - get_evs_encoder_ind_list_size = dlsym_assert(evs_lib_handle, "encoder_ind_list_size", path); - evs_destroy_decoder = dlsym_assert(evs_lib_handle, "destroy_decoder", path); - evs_enc_out = dlsym_assert(evs_lib_handle, "indices_to_serial", path); - evs_set_encoder_opts = dlsym_assert(evs_lib_handle, "encoder_set_opts", path); - evs_set_encoder_brate = dlsym_assert(evs_lib_handle, "encoder_set_brate", path); - evs_set_decoder_Fs = dlsym_assert(evs_lib_handle, "decoder_set_Fs", path); - evs_dec_inc_frame = dlsym_assert(evs_lib_handle, "decoder_inc_ini_frame", path); - - // all ok - - evs_decoder_size = get_evs_decoder_size(); - evs_encoder_size = get_evs_encoder_size(); - evs_encoder_ind_list_size = get_evs_encoder_ind_list_size(); - - return; -} - -static void evs_def_init(struct codec_def_s *def) { - evs_load_so(rtpe_common_config_ptr->evs_lib_path); - - if (evs_lib_handle) { - def->support_decoding = 1; - def->support_encoding = 1; - } -} - -static int evs_dtx(decoder_t *dec, GQueue *out, int ptime) { - ilog(LOG_DEBUG, "pushing empty/lost frame to EVS decoder"); - evs_push_frame(dec, NULL, 0, 0, 0, 0, out); - return 0; } - - - #ifdef HAVE_CODEC_CHAIN static codec_cc_state cc_run(codec_cc_t *c, const str *data, unsigned long ts, void *async_cb_obj) { AVPacket *pkt = c->avpkt; diff --git a/lib/codecmod.h b/lib/codecmod.h new file mode 100644 index 000000000..aa7d82df7 --- /dev/null +++ b/lib/codecmod.h @@ -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 diff --git a/lib/common.Makefile b/lib/common.Makefile index 813200ae9..c147d3b84 100644 --- a/lib/common.Makefile +++ b/lib/common.Makefile @@ -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 diff --git a/lib/evrc.c b/lib/evrc.c new file mode 100644 index 000000000..cae58218e --- /dev/null +++ b/lib/evrc.c @@ -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 diff --git a/lib/evs.c b/lib/evs.c new file mode 100644 index 000000000..d695ad961 --- /dev/null +++ b/lib/evs.c @@ -0,0 +1,1307 @@ +#include "codecmod.h" +#include +#include "loglib.h" +#include "fix_frame_channel_layout.compat" + + +static void *evs_lib_handle; +static unsigned int evs_decoder_size; +static unsigned int evs_encoder_size; +static unsigned int evs_encoder_ind_list_size; + +static void (*evs_init_decoder)(void *); +static void (*evs_init_encoder)(void *); +static void (*evs_destroy_decoder)(void *); +static void (*evs_destroy_encoder)(void *); +static void (*evs_set_encoder_opts)(void *, unsigned long, void *); +static void (*evs_set_encoder_brate)(void *, unsigned long br, unsigned int bwidth, + unsigned int mode, unsigned int amr); +static void (*evs_set_decoder_Fs)(void *, unsigned long); +static void (*evs_enc_in)(void *, const uint16_t *s, const uint16_t n); +static void (*evs_amr_enc_in)(void *, const uint16_t *s, const uint16_t n); +static void (*evs_enc_out)(void *, unsigned char *buf, uint16_t *len); +static void (*evs_dec_in)(void *, char *in, uint16_t len, uint16_t amr_mode, uint16_t core_mode, + uint16_t q_bit, uint16_t partial_frame, uint16_t next_type); +static void (*evs_dec_out)(void *, void *, int frame_mode); // frame_mode=1: missing +static void (*evs_dec_inc_frame)(void *); +static void (*evs_amr_dec_out)(void *, void *); +static void (*evs_syn_output)(float *in, const uint16_t len, int16_t *out); +static void (*evs_reset_enc_ind)(void *); + + + +static void evs_load_so(const char *path) { + if (!path) + return; + + evs_lib_handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); + if (!evs_lib_handle) + die("Failed to open EVS codec .so '%s': %s", path, dlerror()); + + static unsigned int (*get_evs_decoder_size)(void); + static unsigned int (*get_evs_encoder_size)(void); + static unsigned int (*get_evs_encoder_ind_list_size)(void); + + // flp codec? + evs_init_decoder = dlsym(evs_lib_handle, "init_decoder"); + if (!evs_init_decoder) { + // fx codec? + evs_init_decoder = dlsym_assert(evs_lib_handle, "init_decoder_fx", path); + evs_init_encoder = dlsym_assert(evs_lib_handle, "init_encoder_fx", path); + evs_destroy_encoder = dlsym_assert(evs_lib_handle, "destroy_encoder_fx", path); + evs_enc_in = dlsym_assert(evs_lib_handle, "evs_enc_fx", path); + evs_amr_enc_in = dlsym_assert(evs_lib_handle, "amr_wb_enc_fx", path); + evs_reset_enc_ind = dlsym_assert(evs_lib_handle, "reset_indices_enc_fx", path); + evs_dec_in = dlsym_assert(evs_lib_handle, "read_indices_from_djb_fx", path); + evs_dec_out = dlsym_assert(evs_lib_handle, "evs_dec_fx", path); + evs_amr_dec_out = dlsym_assert(evs_lib_handle, "amr_wb_dec_fx", path); + } + else { + // flp codec + evs_init_encoder = dlsym_assert(evs_lib_handle, "init_encoder", path); + evs_destroy_encoder = dlsym_assert(evs_lib_handle, "destroy_encoder", path); + evs_enc_in = dlsym_assert(evs_lib_handle, "evs_enc", path); + evs_amr_enc_in = dlsym_assert(evs_lib_handle, "amr_wb_enc", path); + evs_reset_enc_ind = dlsym_assert(evs_lib_handle, "reset_indices_enc", path); + evs_dec_in = dlsym_assert(evs_lib_handle, "read_indices_from_djb", path); + evs_dec_out = dlsym_assert(evs_lib_handle, "evs_dec", path); + evs_syn_output = dlsym_assert(evs_lib_handle, "syn_output", path); + evs_amr_dec_out = dlsym_assert(evs_lib_handle, "amr_wb_dec", path); + } + + // common + get_evs_decoder_size = dlsym_assert(evs_lib_handle, "decoder_size", path); + get_evs_encoder_size = dlsym_assert(evs_lib_handle, "encoder_size", path); + get_evs_encoder_ind_list_size = dlsym_assert(evs_lib_handle, "encoder_ind_list_size", path); + evs_destroy_decoder = dlsym_assert(evs_lib_handle, "destroy_decoder", path); + evs_enc_out = dlsym_assert(evs_lib_handle, "indices_to_serial", path); + evs_set_encoder_opts = dlsym_assert(evs_lib_handle, "encoder_set_opts", path); + evs_set_encoder_brate = dlsym_assert(evs_lib_handle, "encoder_set_brate", path); + evs_set_decoder_Fs = dlsym_assert(evs_lib_handle, "decoder_set_Fs", path); + evs_dec_inc_frame = dlsym_assert(evs_lib_handle, "decoder_inc_ini_frame", path); + + // all ok + + evs_decoder_size = get_evs_decoder_size(); + evs_encoder_size = get_evs_encoder_size(); + evs_encoder_ind_list_size = get_evs_encoder_ind_list_size(); + + return; +} + + +static void evs_def_init(struct codec_def_s *def) { + evs_load_so(rtpe_common_config_ptr->evs_lib_path); + + if (evs_lib_handle) { + def->support_decoding = 1; + def->support_encoding = 1; + } +} + + + +// 3GPP TS 26.445 A.2.1.2.1 -> A.2.2.1.1 +static const char evs_amr_io_compact_cmr[8] = { + 0x90 | 0, // 6.6 + 0x90 | 1, // 8.85 + 0x90 | 2, // 12.65 + 0x90 | 4, // 15.85 + 0x90 | 5, // 18.25 + 0x90 | 7, // 23.05 + 0x90 | 8, // 23.85 + 0xff // no req +}; + + +#if defined(__x86_64__) +// mvr2s_x64_avx2.S +void mvr2s_avx2(float *in, const uint16_t len, int16_t *out); + +// mvr2s_x64_avx512.S +void mvr2s_avx512(float *in, const uint16_t len, int16_t *out); +#endif + + +#if defined(__x86_64__) && !defined(ASAN_BUILD) && HAS_ATTR(ifunc) && defined(__GLIBC__) +static void mvr2s_dynlib_wrapper(float *in, const uint16_t len, int16_t *out) { + evs_syn_output(in, len, out); +} +static void (*resolve_float2int16_array(void))(float *, const uint16_t, int16_t *) { +#if defined(__x86_64__) + if (rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX512BW) && rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX512F)) + return mvr2s_avx512; + if (rtpe_has_cpu_flag(RTPE_CPU_FLAG_AVX2)) + return mvr2s_avx2; +#endif + return mvr2s_dynlib_wrapper; +} +static void float2int16_array(float *in, const uint16_t len, int16_t *out) + __attribute__ ((ifunc ("resolve_float2int16_array"))); +#else +#define float2int16_array evs_syn_output +#endif + + + +static void evs_push_frame(decoder_t *dec, char *frame_data, int bits, int is_amr, int mode, int q_bit, + GQueue *out) +{ + const unsigned int n_samples = 960; // fixed 20 ms ptime + uint64_t pts = dec->pts; + + AVFrame *frame = av_frame_alloc(); + frame->nb_samples = n_samples; + frame->format = AV_SAMPLE_FMT_S16; + frame->sample_rate = 48000; + DEF_CH_LAYOUT(&frame->CH_LAYOUT, 1); + frame->pts = pts; + if (av_frame_get_buffer(frame, 0) < 0) + abort(); + + evs_dec_in(dec->evs, frame_data, bits, is_amr, mode, q_bit, 0, 0); + + // check for floating point implementation + if (evs_syn_output) { + // temp float buffer + float tmp[n_samples * 3]; + if (!is_amr) + evs_dec_out(dec->evs, tmp, 0); + else + evs_amr_dec_out(dec->evs, tmp); + float2int16_array(tmp, n_samples, (void *) frame->extended_data[0]); + } + else { + if (!is_amr) + evs_dec_out(dec->evs, frame->extended_data[0], 0); + else + evs_amr_dec_out(dec->evs, frame->extended_data[0]); + } + + evs_dec_inc_frame(dec->evs); + + pts += n_samples; + dec->pts = pts; + + g_queue_push_tail(out, frame); +} + + +// upper 16 bits: 0 = EVS, 1 = AMR +// lower 8 bits: mode num +// 0x000000AA = mode num +// 0x00AAAA00 = actual number of bits +// 0xAA000000 = 0=EVS, 1=AMR +// -1 == invalid +static int32_t evs_mode_from_bytes(int bytes) { + switch (bytes) { + // EVS + case 7: // 2.8 + return 0 | (56 << 8); + case 18: // 7.2 + return 1 | (144 << 8); + case 20: // 8.0 + return 2 | (160 << 8); + case 24: // 9.6 + return 3 | (192 << 8); + case 33: // 13.2 + return 4 | (264 << 8); + case 41: // 16.4 + return 5 | (328 << 8); + case 61: // 24.4 + return 6 | (488 << 8); + case 80: // 32.0 + return 7 | (640 << 8); + case 120: // 48.8 + return 8 | (960 << 8); + case 160: // 64.0 + return 9 | (1280 << 8); + case 240: // 96.0 + return 10 | (1920 << 8); + case 320: // 128.0 + return 11 | (2560 << 8); + case 6: // sid + return 12 | (48 << 8); + // AMR + case 17: // (16.5) 6.60 kbit/s // 0 + return 0 | 0x01000000 | (132 << 8); + case 23: // (22.125) 8.85 kbit/s // 1 + return 1 | 0x01000000 | (177 << 8); + case 32: // (31.625) 12.65 kbit/s // 2 + return 2 | 0x01000000 | (253 << 8); + case 36: // (35.625) 14.25 kbit/s // 3 + return 3 | 0x01000000 | (285 << 8); + case 40: // (39.625) 15.85 kbit/s // 4 + return 4 | 0x01000000 | (317 << 8); + case 46: // (45.625) 18.25 kbit/s // 5 + return 5 | 0x01000000 | (365 << 8); + case 50: // (49.625) 19.85 kbit/s // 6 + return 6 | 0x01000000 | (397 << 8); + case 58: // (57.625) 23.05 kbit/s // 7 + return 7 | 0x01000000 | (461 << 8); + case 60: // (59.625) 23.85 kbit/s // 8 + return 8 | 0x01000000 | (477 << 8); + case 5: // sid + return 9 | 0x01000000 | (40 << 8); + } + return -1; +} + + + +static const int evs_mode_bits[2][16] = { + // EVS + { + 56, // 0 + 144, // 1 + 160, // 2 + 192, // 3 + 264, // 4 + 328, // 5 + 488, // 6 + 640, // 7 + 960, // 8 + 1280, // 9 + 1920, // 10 + 2560, // 11 + 48, // 12 + 0, // 13 invalid + 0, // 14 invalid + 0, // 15 invalid + }, + // AMR + { + 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 + 0, // invalid // 14 + 0, // invalid // 15 + }, +}; + + +static int evs_decoder_input(decoder_t *dec, const str *data, GQueue *out) { + str input = *data; + const char *err = NULL; + + if (input.len == 0) + return 0; + + str frame_data = STR_NULL; + const unsigned char *toc = NULL, *toc_end = NULL; + unsigned char cmr = 0xff; + // check for single frame in compact format + int32_t mode = evs_mode_from_bytes(input.len); + int is_amr, bits, q_bit; + if ((mode & 0xff0000ff) == 0) { + // special case, clause A.2.1.3 + if ((input.s[0] & 0x80)) { + // AMR in HF format with CMR + mode = -1; + } + } + if (mode != -1) { + // single compact frame: consume all + frame_data = input; + input.len = 0; + + // extract mode information + bits = (mode >> 8) & 0xffff; + is_amr = mode >> 24; + q_bit = 1; + mode = mode & 0xff; + + if (is_amr) { + // save and clear CMR + unsigned char *shifter = (unsigned char *) frame_data.s; // use unsigned + cmr = shifter[0] & 0xe0; + shifter[0] &= 0x1f; + + // convert CMR to full byte format + cmr >>= 5; // now guaranteed to be 0..7 + cmr = evs_amr_io_compact_cmr[cmr]; + + // bit shift payload + // XXX use larger word sizes + for (size_t i = 0; i < frame_data.len; i++) { + shifter[i] <<= 2; + shifter[i] |= shifter[i+1] >> 6; + } + // restore first bit + size_t first_bit_octet = bits / 8; + size_t first_bit_bit = bits % 8; + shifter[0] |= (shifter[first_bit_octet] << first_bit_bit) & 0x80; + } + } + else { + // header-full + toc = (unsigned char *) input.s; + str_shift(&input, 1); + // is this TOC or CMR? + if ((*toc & 0x80)) { + cmr = *toc; + toc = (unsigned char *) input.s; + err = "short packet (no TOC after CMR)"; + if (str_shift(&input, 1)) + goto err; + err = "invalid TOC byte"; + if ((*toc & 0x80)) + goto err; + } + // skip over all TOC entries + unsigned char toc_ent = *toc; + while ((toc_ent & 0x40)) { + toc_ent = *((unsigned char *) input.s); + err = "short packet (no repeating TOC)"; + if (str_shift(&input, 1)) + goto err; + } + // `toc` is now the first TOC entry and `input` points to the first speech frame + toc_end = (void *) input.s; + } + + while (1) { + // process frame if we have one; we don't have one if + // this is the first iteration and this is not a compact frame + if (mode != -1) + evs_push_frame(dec, frame_data.s, bits, is_amr, mode, q_bit, out); + + // anything left? we break here in compact mode + if (!input.len) + break; + + // if we're here, we're in HF mode: look at the next TOC and extract speech frame + if (toc >= toc_end) // leftover data/padding at the end + break; + mode = *toc & 0xf; + is_amr = (*toc >> 5) & 0x1; + if (is_amr) + q_bit = (*toc >> 4) & 0x1; + else + q_bit = 1; + bits = evs_mode_bits[is_amr][mode]; // guaranteed to be 0..1 and 0..15 + + // consume and shift + toc++; + int bytes = (bits + 7) / 8; + frame_data = STR_LEN(input.s, bytes); + err = "speech frame truncated"; + if (str_shift(&input, bytes)) + goto err; + } + + if (cmr != 0xff) + decoder_event(dec, CE_EVS_CMR_RECV, GUINT_TO_POINTER(cmr)); + + return 0; + +err: + if (err) + ilog(LOG_WARN | LOG_FLAG_LIMIT, "Error unpacking EVS packet: %s", err); + return -1; +} + + +static int evs_dtx(decoder_t *dec, GQueue *out, int ptime) { + ilog(LOG_DEBUG, "pushing empty/lost frame to EVS decoder"); + evs_push_frame(dec, NULL, 0, 0, 0, 0, out); + return 0; +} + + + +static const char *evs_bw_strings[__EVS_BW_MAX] = { "nb", "wb", "swb", "fb" }; + +static void evs_parse_bw(enum evs_bw *minp, enum evs_bw *maxp, const str *token) { + switch (__csh_lookup(token)) { + case CSH_LOOKUP("nb"): + *maxp = EVS_BW_NB; + break; + case CSH_LOOKUP("wb"): + *maxp = EVS_BW_WB; + break; + case CSH_LOOKUP("swb"): + *maxp = EVS_BW_SWB; + break; + case CSH_LOOKUP("fb"): + *maxp = EVS_BW_FB; + break; + case CSH_LOOKUP("nb-wb"): + *minp = EVS_BW_NB; + *maxp = EVS_BW_WB; + break; + case CSH_LOOKUP("nb-swb"): + *minp = EVS_BW_NB; + *maxp = EVS_BW_SWB; + break; + case CSH_LOOKUP("nb-fb"): + *minp = EVS_BW_NB; + *maxp = EVS_BW_FB; + break; + // the ones below are not mentioned in the spec - lower bound ignored + case CSH_LOOKUP("wb-swb"): + *minp = EVS_BW_WB; + *maxp = EVS_BW_SWB; + break; + case CSH_LOOKUP("wb-fb"): + *minp = EVS_BW_WB; + *maxp = EVS_BW_FB; + break; + case CSH_LOOKUP("swb-fb"): + *minp = EVS_BW_SWB; + *maxp = EVS_BW_FB; + break; + default: + ilog(LOG_WARN, "EVS: bandwidth selection '" STR_FORMAT "' not understood", + STR_FMT(token)); + } +} + + +// lamely parse out decimal numbers without using floating point +static unsigned int str_to_i_k(str *s) { + str intg; + str frac = *s; + if (str_token(&intg, &frac, '.')) { + unsigned int ret = str_to_i(s, 0) * 1000; + if (frac.len > 1) // at most one decimal digit + frac.len = 1; + return ret + str_to_i(&frac, 0) * 100; + } + return str_to_i(s, 0) * 1000; +} + + +static void evs_parse_br(unsigned int *minp, unsigned int *maxp, str *token) { + str min; + str max = *token; + if (str_token(&min, &max, '-')) { + *minp = str_to_i_k(&min); + *maxp = str_to_i_k(&max); + } + else + *minp = *maxp = str_to_i_k(token); + if (*minp > *maxp) { + ilog(LOG_WARN, "EVS: min bitrate %u is larger than max bitrate %u", + *minp, *maxp); + *maxp = *minp; + } +} +// lamely print fractional number +static void evs_print_frac_num(GString *s, unsigned int num) { + unsigned int frac = (num / 100 % 10); + unsigned int intg = num / 1000; + if (frac) + g_string_append_printf(s, "%u.%u", intg, frac); + else + g_string_append_printf(s, "%u", intg); +} +static void evs_format_print_br(GString *s, const char *k, unsigned int min, unsigned int max) { + if (!max) + return; + + g_string_append(s, k); + g_string_append_c(s, '='); + + if (min != max) { + evs_print_frac_num(s, min); + g_string_append_c(s, '-'); + } + evs_print_frac_num(s, max); + g_string_append(s, "; "); +} +static void evs_format_print_bw(GString *s, const char *k, enum evs_bw min, enum evs_bw max) { + if (max == EVS_BW_UNSPEC) + return; + + g_string_append(s, k); + g_string_append_c(s, '='); + + if (min != EVS_BW_UNSPEC) { + g_string_append(s, evs_bw_strings[min]); + g_string_append_c(s, '-'); + } + g_string_append(s, evs_bw_strings[max]); + g_string_append(s, "; "); +} +static GString *evs_format_print(const struct rtp_payload_type *p) { + if (!p->format.fmtp_parsed) + return false; + + GString *s = g_string_new(""); + __auto_type f = &p->format.parsed.evs; + + if (f->hf_only) + g_string_append(s, "hf-only=1; "); + if (f->no_dtx) + g_string_append(s, "dtx=0; "); + if (f->no_dtx_recv) + g_string_append(s, "dtx-recv=0; "); + if (f->cmr) + g_string_append_printf(s, "cmr=%i; ", f->cmr); + + if (f->amr_io) { + // AMR + g_string_append(s, "evs-mode-switch=1; "); + + if (f->mode_set) { + g_string_append(s, "mode-set="); + for (unsigned int i = 0; i < 8; i++) { + if ((f->mode_set & (1 << i))) + g_string_append_printf(s, "%u,", i); + } + g_string_truncate(s, s->len - 1); // remove trailing "," + g_string_append(s, "; "); + } + + if (f->mode_change_neighbor) + g_string_append(s, "mode-change-neighbor=1; "); + if (f->mode_change_period) + g_string_append_printf(s, "mode-change-period=%i; ", f->mode_change_period); + } + else { + // EVS + evs_format_print_br(s, "br", f->min_br, f->max_br); + evs_format_print_br(s, "br-send", f->min_br_send, f->max_br_send); + evs_format_print_br(s, "br-recv", f->min_br_recv, f->max_br_recv); + + evs_format_print_bw(s, "bw", f->min_bw, f->max_bw); + evs_format_print_bw(s, "bw-send", f->min_bw_send, f->max_bw_send); + evs_format_print_bw(s, "bw-recv", f->min_bw_recv, f->max_bw_recv); + } + + if (s->len != 0) + g_string_truncate(s, s->len - 2); // remove trailing "; " if anything was printed + + return s; +} +static void evs_parse_format_cb(str *key, str *token, void *data) { + union codec_format_options *opts = data; + __auto_type o = &opts->evs; + + switch (__csh_lookup(key)) { + case CSH_LOOKUP("hf-only"): + if (token->len == 1 && token->s[0] == '1') + o->hf_only = 1; + break; + case CSH_LOOKUP("evs-mode-switch"): + if (token->len == 1 && token->s[0] == '1') + o->amr_io = 1; + break; + case CSH_LOOKUP("dtx"): + if (token->len == 1 && token->s[0] == '0') + o->no_dtx = 1; + break; + case CSH_LOOKUP("dtx-recv"): + if (token->len == 1 && token->s[0] == '0') + o->no_dtx_recv = 1; + break; + case CSH_LOOKUP("cmr"): + if (token->len == 1 && token->s[0] == '1') + o->cmr = 1; + else if (token->len == 2 && token->s[0] == '-' && token->s[1] == '1') + o->cmr = -1; + break; + case CSH_LOOKUP("br"): + evs_parse_br(&o->min_br, &o->max_br, token); + break; + case CSH_LOOKUP("br-send"): + evs_parse_br(&o->min_br_send, &o->max_br_send, token); + break; + case CSH_LOOKUP("br-recv"): + evs_parse_br(&o->min_br_recv, &o->max_br_recv, token); + break; + case CSH_LOOKUP("bw"): + evs_parse_bw(&o->min_bw, &o->max_bw, token); + break; + case CSH_LOOKUP("bw-send"): + evs_parse_bw(&o->min_bw_send, &o->max_bw_send, token); + break; + case CSH_LOOKUP("bw-recv"): + evs_parse_bw(&o->min_bw_recv, &o->max_bw_recv, token); + 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 > 8) + continue; + o->mode_set |= (1 << m); + } + break; + case CSH_LOOKUP("mode-change-period"): + o->mode_change_period = str_to_i(token, 0); + break; + case CSH_LOOKUP("mode-change-neighbor"): + if (token->len == 1 && token->s[0] == '1') + o->mode_change_neighbor = 1; + break; + } +} +static bool evs_format_parse(struct rtp_codec_format *f, const str *fmtp) { + // initialise + f->parsed.evs.max_bw = EVS_BW_UNSPEC; + f->parsed.evs.min_bw = EVS_BW_UNSPEC; + f->parsed.evs.max_bw_send = EVS_BW_UNSPEC; + f->parsed.evs.min_bw_send = EVS_BW_UNSPEC; + f->parsed.evs.max_bw_recv = EVS_BW_UNSPEC; + f->parsed.evs.min_bw_recv = EVS_BW_UNSPEC; + + codeclib_key_value_parse(fmtp, true, evs_parse_format_cb, &f->parsed); + return true; +} +static void evs_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.evs; + + // swap send/recv + + __auto_type t1 = f->max_br_recv; + f->max_br_recv = f->max_br_send; + f->max_br_send = t1; + + t1 = f->min_br_recv; + f->min_br_recv = f->min_br_send; + f->min_br_send = t1; + + __auto_type t2 = f->max_bw_recv; + f->max_bw_recv = f->max_bw_send; + f->max_bw_send = t2; + + t2 = f->min_bw_recv; + f->min_bw_recv = f->min_bw_send; + f->min_bw_send = t2; +} + +// duplicated from AMR code +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 evs_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.evs; + __auto_type b = &B->format.parsed.evs; + + // reject what is incompatible + if (a->amr_io != b->amr_io) + return -1; + if (a->hf_only != b->hf_only) + return -1; + + // determine whether we are compatible + int compat = 0; + +#define FEATURE_CMP(field, compat_op, undefined_val) \ + if (a->field != undefined_val && b->field != undefined_val) { \ + if (a->field == b->field) \ + ; \ + else if (a->field compat_op b->field) \ + compat++; \ + else \ + return -1; \ + } \ + else if (a->field == undefined_val && b->field != undefined_val) /* `a` is broader than `b` */ \ + compat++; \ + else if (a->field != undefined_val && b->field == undefined_val) \ + return -1; + + if (!a->amr_io) { + // EVS + FEATURE_CMP(max_br, >, 0) + FEATURE_CMP(min_br, <, 0) + FEATURE_CMP(max_br_recv, >, 0) + FEATURE_CMP(min_br_recv, <, 0) + FEATURE_CMP(max_br_send, >, 0) + FEATURE_CMP(min_br_send, <, 0) + + FEATURE_CMP(max_bw, >, EVS_BW_UNSPEC) + FEATURE_CMP(min_bw, <, EVS_BW_UNSPEC) + FEATURE_CMP(max_bw_recv, >, EVS_BW_UNSPEC) + FEATURE_CMP(min_bw_recv, <, EVS_BW_UNSPEC) + FEATURE_CMP(max_bw_send, >, EVS_BW_UNSPEC) + FEATURE_CMP(min_bw_send, <, EVS_BW_UNSPEC) + } + else { + // AMR + int match = amr_mode_set_cmp(a->mode_set, b->mode_set); + if (match == 1) + compat++; + else if (match == -1) + return -1; + } + +#undef FEATURE_CMP + + return (compat == 0) ? 0 : 1; +} +// EVS RTP always runs at 16 kHz +static void evs_select_encoder_format(encoder_t *enc, format_t *req_format, const format_t *f, + const struct rtp_codec_format *fmtp) +{ + if (req_format->clockrate != 16000) + return; // bail - encoder will fail to initialise + + // check against natively supported rates first + switch (f->clockrate) { + case 48000: + case 32000: + case 16000: + enc->clockrate_fact = (struct fraction) {48000 / f->clockrate, 1}; + break; + case 8000: + enc->clockrate_fact = (struct fraction) {1, 16000 / f->clockrate}; + break; + default: + // resample to next best rate + if (f->clockrate > 32000) + enc->clockrate_fact = (struct fraction) {3,1}; + else if (f->clockrate > 16000) + enc->clockrate_fact = (struct fraction) {2,1}; + else if (f->clockrate > 8000) + enc->clockrate_fact = (struct fraction) {1,1}; + else + enc->clockrate_fact = (struct fraction) {1,2}; + break; + } +} + + + +static const char *evs_decoder_init(decoder_t *dec, const str *extra_opts) { + dec->evs = g_malloc0(evs_decoder_size); + if (dec->in_format.clockrate != 48000) + ilog(LOG_WARN, "EVS: invalid decoder clock rate (%i) requested", + fraction_div(dec->in_format.clockrate, &dec->clockrate_fact)); + if (dec->in_format.channels != 1) + ilog(LOG_WARN, "EVS: %i-channel EVS is not supported", + dec->in_format.channels); + dec->in_format.clockrate = 48000; + evs_set_decoder_Fs(dec->evs, dec->in_format.clockrate); + evs_init_decoder(dec->evs); + return NULL; +} +static void evs_decoder_close(decoder_t *dec) { + evs_destroy_decoder(dec->evs); + g_free(dec->evs); +} + + + +static int32_t evs_mode_from_bitrate(int bitrate) { + int bytes_per_frame = ((bitrate / 50) + 7) / 8; + if (bytes_per_frame >= 7) + return evs_mode_from_bytes(bytes_per_frame); + return -1; +} + +static int evs_bitrate_mode(int bitrate) { + switch (bitrate) { + // EVS + case 2800: + case 5900: + case 7200: + case 8000: + case 13200: + case 32000: + case 64000: + // AMR + case 6600: + case 8850: + case 12650: + case 14250: + case 15850: + case 18250: + case 19850: + case 23050: + case 23850: + return 1; + // EVS + case 9600: + case 16400: + case 24400: + case 48000: + case 96000: + case 128000: + return 2; + } + return 0; +} + +static const int evs_mode_bitrates[2][16] = { + // EVS + { + 5900, // 0 (VBR) + 7200, // 1 + 8000, // 2 + 9600, // 3 + 13200, // 4 + 16400, // 5 + 24400, // 6 + 32000, // 7 + 48800, // 8 + 64000, // 9 + 96000, // 10 + 128000, // 11 + 0, // 12 SID + 0, // 13 invalid + 0, // 14 invalid + 0, // 15 invalid + }, + // AMR + { + 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 + 0, // invalid // 14 + 0, // invalid // 15 + }, +}; +static const uint8_t evs_min_max_modes_by_bw[__EVS_BW_MAX][2] = { + { 0, 6 }, // NB + { 0, 11 }, // WB + { 3, 11 }, // SWB + { 5, 11 }, // FB +}; +static uint8_t evs_clamp_mode_by_bw(const uint8_t mode, const enum evs_bw bw) { + if (mode < evs_min_max_modes_by_bw[bw][0]) + return evs_min_max_modes_by_bw[bw][0]; + else if (mode > evs_min_max_modes_by_bw[bw][1]) + return evs_min_max_modes_by_bw[bw][1]; + return mode; +} + +static int evs_match_bitrate(int orig_br, unsigned int amr) { + // is it already a valid bitrate? + int32_t mode = evs_mode_from_bitrate(orig_br); + if (mode >= 0) { + int bits = (mode >> 8) & 0xffff; + if (mode > 0 && (mode >> 24) == amr && bits * 50 == orig_br) + return orig_br; + } + + // find closest match + int max_mode = amr ? 8 : 11; + int test_mode = max_mode / 2; + int mode_off = (max_mode + 1) / 2; + bool last = false; + while (1) { + int new_br = evs_mode_bitrates[amr][test_mode]; + int new_off = (mode_off + 1) / 2; + if (new_br > orig_br) { + if (test_mode == 0 || last) + return new_br; + test_mode -= new_off; + } + else { // new_br < orig_br + if (test_mode == max_mode) + return new_br; + test_mode += new_off; + } + if (mode_off == 1) + last = true; + mode_off = new_off; + } +} + + + +static const char *evs_encoder_init(encoder_t *enc, const str *extra_opts) { + enc->evs.ctx = g_malloc0(evs_encoder_size); + enc->evs.ind_list = g_malloc(evs_encoder_ind_list_size); + if (enc->requested_format.channels != 1) + ilog(LOG_WARN, "EVS: %i-channel EVS is not supported", + enc->requested_format.channels); + enc->actual_format = enc->requested_format; + enc->actual_format.format = AV_SAMPLE_FMT_S16; + enc->samples_per_frame = enc->actual_format.clockrate * 20 / 1000; + + __auto_type o = &enc->format_options.evs; + + // determine max BW + if (o->max_bw_send != EVS_BW_UNSPEC) + enc->codec_options.evs.max_bw = o->max_bw_send; + else if (o->max_bw != EVS_BW_UNSPEC) + enc->codec_options.evs.max_bw = o->max_bw; + else + enc->codec_options.evs.max_bw = EVS_BW_WB; + assert(enc->codec_options.evs.max_bw >= 0 && enc->codec_options.evs.max_bw < __EVS_BW_MAX); + + switch (enc->requested_format.clockrate) { + case 48000: + case 32000: + if (enc->codec_options.evs.max_bw > EVS_BW_SWB) + enc->codec_options.evs.max_bw = EVS_BW_SWB; + break; + case 16000: + if (enc->codec_options.evs.max_bw > EVS_BW_WB) + enc->codec_options.evs.max_bw = EVS_BW_WB; + break; + case 8000: + enc->codec_options.evs.max_bw = EVS_BW_NB; + break; + default: + ilog(LOG_WARN, "EVS: invalid encoder clock rate (%i) requested", + fraction_div(enc->requested_format.clockrate, &enc->clockrate_fact)); + } + evs_set_encoder_opts(enc->evs.ctx, enc->actual_format.clockrate, enc->evs.ind_list); + + // limit bitrate to given range + if (!o->amr_io) { + // EVS + if (o->max_br && enc->bitrate > o->max_br) + enc->bitrate = o->max_br; + if (o->min_br && enc->bitrate < o->max_br) + enc->bitrate = o->min_br; + + // verify bitrate + int bitrate = evs_match_bitrate(enc->bitrate, 0); + if (bitrate != enc->bitrate) { + ilog(LOG_INFO, "EVS: Using bitrate %i instead of %i", bitrate, enc->bitrate); + enc->bitrate = bitrate; + } + + // limit max bitrate to one supported by the selected BW + int32_t mode = evs_mode_from_bitrate(enc->bitrate); + if (mode == -1) + ilog(LOG_WARN, "EVS: ended up with unknown bitrate %i", enc->bitrate); + else { + mode &= 0xff; + mode = evs_clamp_mode_by_bw(mode, enc->codec_options.evs.max_bw); + bitrate = evs_mode_bitrates[0][mode]; + ilog(LOG_INFO, "EVS: using bitrate %i instead of %i as restricted by BW %i", + bitrate, enc->bitrate, enc->codec_options.evs.max_bw); + enc->bitrate = bitrate; + } + } + else { + // AMR + int32_t mode = evs_mode_from_bitrate(enc->bitrate); + if (mode != -1) { + if (mode >> 24 != 1) + mode = -1; // EVS bitrate + else if (o->mode_set) { + if ((o->mode_set & (1 << (mode & 0xff))) == 0) + mode = -1; // not part of the mode-set + } + } + if (mode == -1) { + // find closest match bitrate + int bitrate = evs_match_bitrate(enc->bitrate, 1); + mode = evs_mode_from_bitrate(bitrate); + if (mode == -1 || (mode >> 24 != 1)) + ilog(LOG_WARN, "EVS: ended up with unknown bitrate %i", bitrate); + else { + mode &= 0xff; + // restrict by mode-set if there is one + if (o->mode_set) { + if ((o->mode_set & (1 << (mode & 0xff))) == 0) { + // pick next higher mode if possible, otherwise go lower: + // clear lower unwanted modes from mode-set + unsigned int mode_set = o->mode_set & (0xfe << mode); + if (mode_set) { + // got a higher mode: which one? + mode = __builtin_ffs(mode_set) - 1; + } + else { + // no higher mode, get next lower one + mode = sizeof(int) * 8 - __builtin_clz(o->mode_set) - 1; + } + } + } + bitrate = evs_mode_bitrates[1][mode]; + ilog(LOG_INFO, "EVS: using bitrate %i instead of %i as restricted by mode-set", + bitrate, enc->bitrate); + enc->bitrate = bitrate; + } + } + } + + evs_set_encoder_brate(enc->evs.ctx, enc->bitrate, enc->codec_options.evs.max_bw, + evs_bitrate_mode(enc->bitrate), o->amr_io); + evs_init_encoder(enc->evs.ctx); + + return NULL; +} +static void evs_encoder_close(encoder_t *enc) { + evs_destroy_encoder(enc->evs.ctx); + g_free(enc->evs.ctx); + g_free(enc->evs.ind_list); +} + + + + +static void evs_handle_cmr(encoder_t *enc) { + if ((enc->callback.evs.cmr_in & 0x80) == 0) + return; + if (enc->callback.evs.cmr_in_ts == enc->evs.cmr_in_ts) + return; + + enc->evs.cmr_in_ts = enc->callback.evs.cmr_in_ts; // XXX should use a queue or something instead + + __auto_type f = &enc->format_options.evs; + __auto_type o = &enc->codec_options.evs; + unsigned char type = (enc->callback.evs.cmr_in >> 4) & 0x7; + unsigned char req = enc->callback.evs.cmr_in & 0xf; + int bitrate; + + if (type == 1) { + // AMR + if (!f->amr_io) + goto err; + if (req > 8) + goto err; + bitrate = evs_mode_bitrates[1][req]; + } + else if (type <= 4) { + // EVS modes + if (f->amr_io) + goto err; + if (req > 11) + goto err; + int bw = type; + if (bw >= 2) + bw--; // 0..3 + // ignore min BW + // instead of ignoring invalid request, clamp them to what is allowed by BW + if (o->max_bw != EVS_BW_UNSPEC && o->max_bw < bw) + bw = o->max_bw; + req = evs_clamp_mode_by_bw(req, bw); + bitrate = evs_mode_bitrates[0][req]; + } + else + goto err; + + enc->bitrate = bitrate; + evs_set_encoder_brate(enc->evs.ctx, bitrate, o->max_bw, + evs_bitrate_mode(bitrate), f->amr_io); + + return; + +err: + if (f->amr_io) + ilog(LOG_WARN | LOG_FLAG_LIMIT, "EVS: received invalid CMR (type %u, " + "request %u) in AMR mode", type, req); + else + ilog(LOG_WARN | LOG_FLAG_LIMIT, "EVS: received invalid CMR (type %u, " + "request %u) with BW <= %i", type, req, o->max_bw); +} + +static int evs_encoder_input(encoder_t *enc, AVFrame **frame) { + if (!*frame) + return 0; + + if ((*frame)->nb_samples != enc->actual_format.clockrate * 20 / 1000) { + ilog(LOG_ERR | LOG_FLAG_LIMIT, "EVS: input %u samples instead of %i", (*frame)->nb_samples, + enc->actual_format.clockrate * 20 / 1000); + return -1; + } + + evs_handle_cmr(enc); + + if (!enc->format_options.evs.amr_io) + evs_enc_in(enc->evs.ctx, (void *) (*frame)->extended_data[0], (*frame)->nb_samples); + else + evs_amr_enc_in(enc->evs.ctx, (void *) (*frame)->extended_data[0], (*frame)->nb_samples); + + // max output: 320 bytes, plus some overhead + av_new_packet(enc->avpkt, 340); + + unsigned char *out = enc->avpkt->data; + unsigned char *cmr = NULL; + + if (!enc->format_options.evs.amr_io) { + // EVS + if (enc->format_options.evs.cmr == 1) { + cmr = out; + *cmr = 0xff; // no CMR + out++; + } + } + else { + // AMR IO + if (!enc->format_options.evs.hf_only) { + // compact + cmr = out; + *cmr = 0xe0; // no CMR + out++; // to be shuffled below + } + else { + // header-full + if (enc->format_options.evs.cmr == 1) { + cmr = out; + *cmr = 0xff; // no CMR + out++; + } + } + } + + // TOC byte + unsigned char *toc = NULL; + if (enc->format_options.evs.hf_only) { + // header-full always has TOC + toc = out; + out++; + } + else { + // compact + if (cmr && !enc->format_options.evs.amr_io) { + // EVS with CMR is also header-full with TOC + toc = out; + out++; + } + } + + uint16_t bits = 0; + evs_enc_out(enc->evs.ctx, out, &bits); + uint16_t bytes = (bits + 7) / 8; + int32_t mode = evs_mode_from_bytes(bytes); + if (mode < 0) { + ilog(LOG_ERR | LOG_FLAG_LIMIT, "EVS: invalid encoding received from codec " + "(%i bits per frame)", bits); + av_packet_unref(enc->avpkt); + return -1; + } + evs_reset_enc_ind(enc->evs.ctx); + + if (toc) { + *toc = (mode & 0xff); + if (enc->format_options.evs.amr_io) + *toc |= 0x30; + } + + if (enc->format_options.evs.amr_io && !enc->format_options.evs.hf_only) { + // how many output bytes (frame minus CMR bits) total? + bytes = (bits - 5 + 7) / 8; + // bit-shuffle payload + unsigned char first = out[0]; + *cmr |= (first >> 2) & 0x1f; + // XXX accelerate with larger word sizes + for (int i = 0; i < bytes; i++) { + out[i] <<= 6; + out[i] |= out[i+1] >> 2; + } + // restore first bit, clear out tail end padding bits + unsigned int first_bit_shift = (bits + 2) % 8; + out[bytes-1] &= (0xff << (8 - first_bit_shift)); // clear leftovers + out[bytes-1] |= ((first & 0x80) >> first_bit_shift); // last/first bit + } + + bytes += (out - enc->avpkt->data); + assert(bytes <= enc->avpkt->size); + + if (toc && !enc->format_options.evs.amr_io && !enc->format_options.evs.hf_only) { + // hf-only=0 but HF packet, check for size collisions and zero-pad if needed + while (evs_mode_from_bytes(bytes) != -1) { + enc->avpkt->data[bytes] = '\0'; + bytes++; + } + } + + enc->avpkt->size = bytes; + enc->avpkt->pts = (*frame)->pts; + enc->avpkt->duration = (*frame)->nb_samples; + + return 0; +} + + + + + +static const codec_type_t codec_type_evs = { + .def_init = evs_def_init, + .decoder_init = evs_decoder_init, + .decoder_input = evs_decoder_input, + .decoder_close = evs_decoder_close, + .encoder_init = evs_encoder_init, + .encoder_input = evs_encoder_input, + .encoder_close = evs_encoder_close, +}; + + +static const dtx_method_t dtx_method_evs = { + .method_id = DTX_NATIVE, + .do_dtx = evs_dtx, +}; + + +static const codec_def_t evs = { + .rtpname = "EVS", + .avcodec_id = -1, + .default_clockrate_fact = {3,1}, + .default_clockrate = 16000, + .default_channels = 1, + .default_ptime = 20, + .default_bitrate = 16400, + .default_fmtp = "dtx=0;dtx-recv=0", + .format_parse = evs_format_parse, + .format_cmp = evs_format_cmp, + .format_print = evs_format_print, + .format_answer = evs_format_answer, + .select_encoder_format = evs_select_encoder_format, + .packetizer = packetizer_passthrough, + .bits_per_sample = 1, + .evs = 1, + .media_type = MT_AUDIO, + .codec_type = &codec_type_evs, + .dtx_methods = { + [DTX_NATIVE] = &dtx_method_evs, + [DTX_SILENCE] = &dtx_method_silence, + [DTX_CN] = &dtx_method_cn, + }, +}; + + +__attribute__((constructor)) +static void init(void) { + codeclib_register_codec(&evs); +} + +__attribute__((destructor)) +static void cleanup(void) { + if (evs_lib_handle) + dlclose(evs_lib_handle); +} diff --git a/lib/flags.Makefile b/lib/flags.Makefile index 52fdd154b..485523594 100644 --- a/lib/flags.Makefile +++ b/lib/flags.Makefile @@ -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 diff --git a/lib/g711.c b/lib/g711.c new file mode 100644 index 000000000..4ff3d5536 --- /dev/null +++ b/lib/g711.c @@ -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); +} diff --git a/lib/g722.c b/lib/g722.c new file mode 100644 index 000000000..1c40f080e --- /dev/null +++ b/lib/g722.c @@ -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); +} diff --git a/lib/g723.c b/lib/g723.c new file mode 100644 index 000000000..f128102ae --- /dev/null +++ b/lib/g723.c @@ -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); +} diff --git a/lib/g726.c b/lib/g726.c new file mode 100644 index 000000000..cb412e765 --- /dev/null +++ b/lib/g726.c @@ -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); +} diff --git a/lib/g729.c b/lib/g729.c new file mode 100644 index 000000000..0c93bcfb3 --- /dev/null +++ b/lib/g729.c @@ -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 +#include +#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); +} diff --git a/lib/gsm.c b/lib/gsm.c new file mode 100644 index 000000000..c729d2933 --- /dev/null +++ b/lib/gsm.c @@ -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); +} diff --git a/lib/ilbc.c b/lib/ilbc.c new file mode 100644 index 000000000..7fd13efb6 --- /dev/null +++ b/lib/ilbc.c @@ -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); +} diff --git a/lib/l16.c b/lib/l16.c new file mode 100644 index 000000000..2f95e51a4 --- /dev/null +++ b/lib/l16.c @@ -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); +} diff --git a/lib/mp3.c b/lib/mp3.c new file mode 100644 index 000000000..fa3069f3b --- /dev/null +++ b/lib/mp3.c @@ -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); +} diff --git a/lib/opus.c b/lib/opus.c new file mode 100644 index 000000000..b35dec138 --- /dev/null +++ b/lib/opus.c @@ -0,0 +1,471 @@ +#include "codecmod.h" +#include +#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); +} diff --git a/lib/pseudo.c b/lib/pseudo.c new file mode 100644 index 000000000..7065a568f --- /dev/null +++ b/lib/pseudo.c @@ -0,0 +1,225 @@ +#include "codecmod.h" +#include +#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); +} diff --git a/lib/qcelp.c b/lib/qcelp.c new file mode 100644 index 000000000..d2f57387d --- /dev/null +++ b/lib/qcelp.c @@ -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); +} diff --git a/lib/speex.c b/lib/speex.c new file mode 100644 index 000000000..5c0e48470 --- /dev/null +++ b/lib/speex.c @@ -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); +} diff --git a/lib/u8.c b/lib/u8.c new file mode 100644 index 000000000..8fe9e8a13 --- /dev/null +++ b/lib/u8.c @@ -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); +} diff --git a/lib/vorbis.c b/lib/vorbis.c new file mode 100644 index 000000000..28d31f6a2 --- /dev/null +++ b/lib/vorbis.c @@ -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); +} diff --git a/perf-tester/Makefile b/perf-tester/Makefile index 6840813fc..90a4bb5f3 100644 --- a/perf-tester/Makefile +++ b/perf-tester/Makefile @@ -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 diff --git a/recording-daemon/Makefile b/recording-daemon/Makefile index b43f10706..997f59906 100644 --- a/recording-daemon/Makefile +++ b/recording-daemon/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 diff --git a/t/Makefile b/t/Makefile index 27424a424..006cbab03 100644 --- a/t/Makefile +++ b/t/Makefile @@ -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 \