|
|
|
@ -19,7 +19,11 @@ struct decoder_s { |
|
|
|
|
|
|
|
struct output_s { |
|
|
|
char *filename; |
|
|
|
|
|
|
|
// format params |
|
|
|
int clockrate; |
|
|
|
int channels; |
|
|
|
|
|
|
|
AVCodecContext *avcctx; |
|
|
|
AVFormatContext *fmtctx; |
|
|
|
AVStream *avst; |
|
|
|
@ -28,17 +32,21 @@ struct output_s { |
|
|
|
|
|
|
|
|
|
|
|
struct decoder_def_s { |
|
|
|
const char *name; |
|
|
|
const char *rtpname; |
|
|
|
int clockrate_mult; |
|
|
|
int avcodec_id; |
|
|
|
const char *avcodec_name; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#define DECODER_DEF_MULT(ref, id, mult) { \ |
|
|
|
.name = #ref, \ |
|
|
|
#define DECODER_DEF_MULT_NAME(ref, id, mult, name) { \ |
|
|
|
.rtpname = #ref, \ |
|
|
|
.avcodec_id = AV_CODEC_ID_ ## id, \ |
|
|
|
.clockrate_mult = mult, \ |
|
|
|
.avcodec_name = #name, \ |
|
|
|
} |
|
|
|
#define DECODER_DEF_MULT(ref, id, mult) DECODER_DEF_MULT_NAME(ref, id, mult, NULL) |
|
|
|
#define DECODER_DEF_NAME(ref, id, name) DECODER_DEF_MULT_NAME(ref, id, 1, name) |
|
|
|
#define DECODER_DEF(ref, id) DECODER_DEF_MULT(ref, id, 1) |
|
|
|
|
|
|
|
static const struct decoder_def_s decoders[] = { |
|
|
|
@ -51,19 +59,20 @@ static const struct decoder_def_s decoders[] = { |
|
|
|
DECODER_DEF(speex, SPEEX), |
|
|
|
DECODER_DEF(GSM, GSM), |
|
|
|
DECODER_DEF(iLBC, ILBC), |
|
|
|
DECODER_DEF(opus, OPUS), |
|
|
|
DECODER_DEF_NAME(opus, OPUS, libopus), |
|
|
|
}; |
|
|
|
typedef struct decoder_def_s decoder_def_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void output_shutdown(output_t *output); |
|
|
|
static int output_config(output_t *output, unsigned int clockrate, unsigned int channels); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const decoder_def_t *decoder_find(const str *name) { |
|
|
|
for (int i = 0; i < G_N_ELEMENTS(decoders); i++) { |
|
|
|
if (!str_cmp(name, decoders[i].name)) |
|
|
|
if (!str_cmp(name, decoders[i].rtpname)) |
|
|
|
return &decoders[i]; |
|
|
|
} |
|
|
|
return NULL; |
|
|
|
@ -81,6 +90,14 @@ decoder_t *decoder_new(const char *payload_str) { |
|
|
|
str_init_len(&name, (char *) payload_str, slash - payload_str); |
|
|
|
int clockrate = atoi(slash + 1); |
|
|
|
|
|
|
|
int channels = 1; |
|
|
|
slash = strchr(slash + 1, '/'); |
|
|
|
if (slash) { |
|
|
|
channels = atoi(slash + 1); |
|
|
|
if (!channels) |
|
|
|
channels = 1; |
|
|
|
} |
|
|
|
|
|
|
|
const decoder_def_t *def = decoder_find(&name); |
|
|
|
if (!def) { |
|
|
|
ilog(LOG_WARN, "No decoder for payload %s", payload_str); |
|
|
|
@ -91,11 +108,16 @@ decoder_t *decoder_new(const char *payload_str) { |
|
|
|
decoder_t *ret = g_slice_alloc0(sizeof(*ret)); |
|
|
|
|
|
|
|
// XXX error reporting |
|
|
|
AVCodec *codec = avcodec_find_decoder(def->avcodec_id); |
|
|
|
AVCodec *codec = NULL; |
|
|
|
if (def->avcodec_name) |
|
|
|
codec = avcodec_find_decoder_by_name(def->avcodec_name); |
|
|
|
if (!codec) |
|
|
|
codec = avcodec_find_decoder(def->avcodec_id); |
|
|
|
|
|
|
|
ret->avcctx = avcodec_alloc_context3(codec); |
|
|
|
if (!ret->avcctx) |
|
|
|
goto err; |
|
|
|
ret->avcctx->channels = 1; |
|
|
|
ret->avcctx->channels = channels; |
|
|
|
ret->avcctx->sample_rate = clockrate; |
|
|
|
int i = avcodec_open2(ret->avcctx, codec, NULL); |
|
|
|
if (i) |
|
|
|
@ -155,7 +177,7 @@ int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *o |
|
|
|
} |
|
|
|
else { |
|
|
|
// shift pts according to rtp ts shift |
|
|
|
dec->pts += (ts - dec->rtp_ts) * output->avst->time_base.num * 8000 / output->avst->time_base.den; |
|
|
|
dec->pts += (ts - dec->rtp_ts) /* * output->avst->time_base.num * 8000 / output->avst->time_base.den */ ; |
|
|
|
// XXX handle lost packets here if timestamps don't line up? |
|
|
|
} |
|
|
|
dec->rtp_ts = ts; |
|
|
|
@ -184,7 +206,7 @@ int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *o |
|
|
|
|
|
|
|
dec->frame->pts = dec->frame->pkt_pts; |
|
|
|
|
|
|
|
output_config(output, dec->avcctx->sample_rate); |
|
|
|
output_config(output, dec->avcctx->sample_rate, dec->avcctx->channels); |
|
|
|
output_add(output, dec->frame); |
|
|
|
|
|
|
|
return 0; |
|
|
|
@ -195,18 +217,27 @@ output_t *output_new(const char *filename) { |
|
|
|
output_t *ret = g_slice_alloc0(sizeof(*ret)); |
|
|
|
ret->filename = strdup(filename); |
|
|
|
ret->clockrate = -1; |
|
|
|
ret->channels = -1; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int output_config(output_t *output, unsigned int clockrate) { |
|
|
|
static int output_config(output_t *output, unsigned int clockrate, unsigned int channels) { |
|
|
|
// anything to do? |
|
|
|
if (output->clockrate == clockrate) |
|
|
|
return 0; |
|
|
|
if (G_UNLIKELY(output->clockrate != clockrate)) |
|
|
|
goto format_mismatch; |
|
|
|
if (G_UNLIKELY(output->channels != channels)) |
|
|
|
goto format_mismatch; |
|
|
|
|
|
|
|
// all good |
|
|
|
return 0; |
|
|
|
|
|
|
|
format_mismatch: |
|
|
|
// XXX support reset/config change |
|
|
|
|
|
|
|
// copy params |
|
|
|
output->clockrate = clockrate; |
|
|
|
output->channels = channels; |
|
|
|
|
|
|
|
// XXX error reporting |
|
|
|
output->fmtctx = avformat_alloc_context(); |
|
|
|
@ -229,7 +260,7 @@ int output_config(output_t *output, unsigned int clockrate) { |
|
|
|
output->avcctx = output->avst->codec; |
|
|
|
#endif |
|
|
|
|
|
|
|
output->avcctx->channels = 1; |
|
|
|
output->avcctx->channels = output->channels; |
|
|
|
output->avcctx->sample_rate = output->clockrate; |
|
|
|
output->avcctx->sample_fmt = AV_SAMPLE_FMT_S16; |
|
|
|
output->avcctx->time_base = (AVRational){output->clockrate,1}; |
|
|
|
|