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

226 lines
5.9 KiB

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