MT#65413 split out AVC code

Change-Id: I99440992813bd24d5e92bbd6b9323c4f2e99a3dc
master
Richard Fuchs 17 hours ago
parent 83b05e9a11
commit 7b3ac41969

@ -16,6 +16,7 @@ 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
SRCS += ssllib.c str.c streambuf.c uring.c
SRCS += avc.c
ASM := mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S mvr2s_x64_avx2.S mvr2s_x64_avx512.S

@ -0,0 +1,447 @@
#include "codecmod.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/opt.h>
#include "loglib.h"
#include "fix_frame_channel_layout.compat"
TYPED_GHASHTABLE(codecs_by_id_alloc, void, struct codec_def_s, g_direct_hash, g_direct_equal, NULL, g_free)
static rwlock_t generic_ffmpeg_codecs_lock = RWLOCK_STATIC_INIT;
static codecs_by_id_alloc generic_ffmpeg_codecs;
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,
};
const char *avc_decoder_init(decoder_t *dec, const str *extra_opts) {
const AVCodec *codec = dec->def->decoder;
if (!codec)
return "codec not supported";
dec->avc.avpkt = av_packet_alloc();
dec->avc.avcctx = avcodec_alloc_context3(codec);
if (!dec->avc.avcctx)
return "failed to alloc codec context";
SET_CHANNELS(dec->avc.avcctx, dec->in_format.channels);
DEF_CH_LAYOUT(&dec->avc.avcctx->CH_LAYOUT, dec->in_format.channels);
dec->avc.avcctx->sample_rate = dec->in_format.clockrate;
if (dec->def->set_dec_options)
dec->def->set_dec_options(dec, extra_opts);
int i = avcodec_open2(dec->avc.avcctx, codec, NULL);
if (i) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(i));
return "failed to open codec context";
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 19, 0)
avcodec_get_supported_config(dec->avc.avcctx, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void **) &dec->avc.sample_fmts, NULL);
#else
dec->avc.sample_fmts = codec->sample_fmts;
#endif
for (const enum AVSampleFormat *sfmt = dec->avc.sample_fmts; sfmt && *sfmt != -1; sfmt++)
ilogs(internals, LOG_DEBUG, "supported sample format for input codec %s: %s",
codec->name, av_get_sample_fmt_name(*sfmt));
return NULL;
}
void avc_decoder_close(decoder_t *dec) {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
avcodec_free_context(&dec->avc.avcctx);
#else
avcodec_close(dec->avc.avcctx);
av_free(dec->avc.avcctx);
#endif
av_packet_free(&dec->avc.avpkt);
}
int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
if (!dec->avc.avpkt)
return -1; // decoder shut down
const char *err;
int av_ret = 0;
dec->avc.avpkt->data = (unsigned char *) data->s;
dec->avc.avpkt->size = data->len;
dec->avc.avpkt->pts = dec->pts;
AVFrame *frame = NULL;
// loop until all input is consumed and all available output has been processed
int keep_going;
do {
keep_going = 0;
int got_frame = 0;
err = "failed to alloc av frame";
frame = av_frame_alloc();
if (!frame)
goto err;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (dec->avc.avpkt->size) {
av_ret = avcodec_send_packet(dec->avc.avcctx, dec->avc.avpkt);
ilogs(internals, LOG_DEBUG, "send packet ret %i", av_ret);
err = "failed to send packet to avcodec";
if (av_ret == 0) {
// consumed the packet
dec->avc.avpkt->size = 0;
keep_going = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // try again after reading output
else
goto err;
}
}
av_ret = avcodec_receive_frame(dec->avc.avcctx, frame);
ilogs(internals, LOG_DEBUG, "receive frame ret %i", av_ret);
err = "failed to receive frame from avcodec";
if (av_ret == 0) {
// got a frame
keep_going = 1;
got_frame = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // maybe needs more input now
else
goto err;
}
#else
// only do this if we have any input left
if (dec->avc.avpkt->size == 0)
break;
av_ret = avcodec_decode_audio4(dec->avc.avcctx, frame, &got_frame, dec->avc.avpkt);
ilogs(internals, LOG_DEBUG, "decode frame ret %i, got frame %i", av_ret, got_frame);
err = "failed to decode audio packet";
if (av_ret < 0)
goto err;
if (av_ret > 0) {
// consumed some input
err = "invalid return value";
if (av_ret > dec->avc.avpkt->size)
goto err;
dec->avc.avpkt->size -= av_ret;
dec->avc.avpkt->data += av_ret;
keep_going = 1;
}
if (got_frame)
keep_going = 1;
#endif
if (got_frame) {
ilogs(internals, LOG_DEBUG, "raw frame from decoder pts %llu samples %u",
(unsigned long long) frame->pts, frame->nb_samples);
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 36, 0)
frame->pts = frame->pkt_pts;
#endif
if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE))
frame->pts = dec->avc.avpkt->pts;
dec->avc.avpkt->pts += frame->nb_samples;
g_queue_push_tail(out, frame);
frame = NULL;
}
} while (keep_going);
av_frame_free(&frame);
return 0;
err:
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error decoding media packet: %s", err);
if (av_ret)
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(av_ret));
av_frame_free(&frame);
return -1;
}
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);
if (def->avcodec_name_dec)
def->decoder = avcodec_find_decoder_by_name(def->avcodec_name_dec);
if (def->avcodec_id >= 0) {
if (!def->encoder)
def->encoder = avcodec_find_encoder(def->avcodec_id);
if (!def->decoder)
def->decoder = avcodec_find_decoder(def->avcodec_id);
}
// check if we have support if we are supposed to
if (def->avcodec_name_enc || def->avcodec_id >= 0) {
if (def->encoder)
def->support_encoding = 1;
}
if (def->avcodec_name_dec || def->avcodec_id >= 0) {
if (def->decoder)
def->support_decoding = 1;
}
}
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";
enc->avc.avcctx = avcodec_alloc_context3(enc->avc.codec);
if (!enc->avc.avcctx)
return "failed to alloc codec context";
enc->actual_format = enc->requested_format;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 19, 0)
avcodec_get_supported_config(enc->avc.avcctx, enc->avc.codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void **) &enc->avc.sample_fmts, NULL);
#else
enc->avc.sample_fmts = enc->avc.codec->sample_fmts;
#endif
enc->actual_format.format = -1;
for (const enum AVSampleFormat *sfmt = enc->avc.sample_fmts; sfmt && *sfmt != -1; sfmt++) {
ilogs(internals, LOG_DEBUG, "supported sample format for output codec %s: %s",
enc->avc.codec->name, av_get_sample_fmt_name(*sfmt));
if (*sfmt == enc->requested_format.format)
enc->actual_format.format = *sfmt;
}
if (enc->actual_format.format == -1 && enc->avc.sample_fmts)
enc->actual_format.format = enc->avc.sample_fmts[0];
ilogs(internals, LOG_DEBUG, "using output sample format %s for codec %s",
av_get_sample_fmt_name(enc->actual_format.format), enc->avc.codec->name);
SET_CHANNELS(enc->avc.avcctx, enc->actual_format.channels);
DEF_CH_LAYOUT(&enc->avc.avcctx->CH_LAYOUT, enc->actual_format.channels);
enc->avc.avcctx->sample_rate = enc->actual_format.clockrate;
enc->avc.avcctx->sample_fmt = enc->actual_format.format;
enc->avc.avcctx->time_base = (AVRational){1,enc->actual_format.clockrate};
enc->avc.avcctx->bit_rate = enc->bitrate;
if (enc->def->set_enc_options)
enc->def->set_enc_options(enc, extra_opts);
int i = avcodec_open2(enc->avc.avcctx, enc->avc.codec, NULL);
if (i) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(i));
return "failed to open output context";
}
if (enc->avc.avcctx->frame_size)
enc->samples_per_frame = enc->avc.avcctx->frame_size;
else
enc->samples_per_frame = enc->actual_format.clockrate * enc->ptime / 1000;
enc->samples_per_packet = enc->samples_per_frame;
return NULL;
}
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);
#endif
avcodec_free_context(&enc->avc.avcctx);
}
enc->avc.avcctx = NULL;
enc->avc.codec = NULL;
}
int avc_encoder_input(encoder_t *enc, AVFrame **frame) {
int keep_going = 0;
int got_packet = 0;
int av_ret = 0;
if (!enc->avc.avcctx)
return -1;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (*frame) {
av_ret = avcodec_send_frame(enc->avc.avcctx, *frame);
ilogs(internals, LOG_DEBUG, "send frame ret %i", av_ret);
if (av_ret == 0) {
// consumed
*frame = NULL;
keep_going = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // check output and maybe try again
else
goto err;
}
}
av_ret = avcodec_receive_packet(enc->avc.avcctx, enc->avpkt);
ilogs(internals, LOG_DEBUG, "receive packet ret %i", av_ret);
if (av_ret == 0) {
// got some data
keep_going = 1;
got_packet = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // try again if there's still more input
else
goto err;
}
#else
if (!*frame)
return 0;
av_ret = avcodec_encode_audio2(enc->avc.avcctx, enc->avpkt, *frame, &got_packet);
ilogs(internals, LOG_DEBUG, "encode frame ret %i, got packet %i", av_ret, got_packet);
if (av_ret == 0)
*frame = NULL; // consumed
else
goto err;
if (got_packet)
keep_going = 1;
#endif
if (!got_packet)
return keep_going;
ilogs(internals, LOG_DEBUG, "output avpkt size is %i", (int) enc->avpkt->size);
ilogs(internals, LOG_DEBUG, "output pkt pts/dts is %li/%li", (long) enc->avpkt->pts,
(long) enc->avpkt->dts);
// the encoder may return frames with the same dts multiple consecutive times.
// the muxer may not like this, so ensure monotonically increasing dts.
if (enc->mux_dts > enc->avpkt->dts)
enc->avpkt->dts = enc->mux_dts;
if (enc->avpkt->pts < enc->avpkt->dts)
enc->avpkt->pts = enc->avpkt->dts;
return keep_going;
err:
if (av_ret)
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(av_ret));
return -1;
}
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);
int ret = av_opt_set_int(enc->avc.avcctx, opt, val, AV_OPT_SEARCH_CHILDREN);
if (!ret)
return 0;
ilog(LOG_WARN, "Failed to set ffmpeg '%s' option for codec '%s' to %" PRId64 ": %s",
opt, enc->def->rtpname, val, av_error(ret));
return -1;
}
codec_def_t *codec_def_make_generic_av(enum AVCodecID id) {
{
RWLOCK_R(&generic_ffmpeg_codecs_lock);
struct codec_def_s *ret = t_hash_table_lookup(generic_ffmpeg_codecs, GINT_TO_POINTER(id));
if (ret)
return ret;
}
{
RWLOCK_W(&generic_ffmpeg_codecs_lock);
struct codec_def_s *ret = t_hash_table_lookup(generic_ffmpeg_codecs, GINT_TO_POINTER(id));
if (ret)
return ret;
const AVCodec *codec = avcodec_find_decoder(id);
if (!codec)
return NULL;
ret = g_new(__typeof(*ret), 1);
*ret = (__typeof(*ret)) {
.rtpname = "generic ffmpeg codec",
.rtpname_str = STR_CONST("generic ffmpeg codec"),
.avcodec_id = id,
.default_clockrate_fact = {1,1},
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.decoder = codec,
.support_decoding = 1,
.support_encoding = 1, // just pretend
};
t_hash_table_insert(generic_ffmpeg_codecs, GINT_TO_POINTER(id), ret);
return ret;
}
}
static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) {
char *msg;
if (vasprintf(&msg, fmt, ap) <= 0)
ilogs(ffmpeg, LOG_ERR | LOG_FLAG_LIMIT, "av_log message dropped");
else {
#ifdef AV_LOG_PANIC
// translate AV_LOG_ constants to LOG_ levels
if (loglevel >= AV_LOG_VERBOSE)
loglevel = LOG_DEBUG;
else if (loglevel >= AV_LOG_INFO)
loglevel = LOG_NOTICE;
else if (loglevel >= AV_LOG_WARNING)
loglevel = LOG_WARNING;
else if (loglevel >= AV_LOG_ERROR)
loglevel = LOG_ERROR;
else if (loglevel >= AV_LOG_FATAL)
loglevel = LOG_CRIT;
else
loglevel = LOG_ALERT;
#else
// defuse avlog log levels to be either DEBUG or ERR
if (loglevel <= LOG_ERR)
loglevel = LOG_ERR;
else
loglevel = LOG_DEBUG;
#endif
ilogs(ffmpeg, loglevel | LOG_FLAG_LIMIT, "av_log: %s", msg);
free(msg);
}
}
void avc_init(void) {
avformat_network_init();
av_log_set_callback(avlog_ilog);
generic_ffmpeg_codecs = codecs_by_id_alloc_new();
}
void avc_cleanup(void) {
t_hash_table_destroy(generic_ffmpeg_codecs);
avformat_network_deinit();
}

@ -1,8 +1,4 @@
#include "codeclib.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/opt.h>
#include <glib.h>
#include <dlfcn.h>
#include "str.h"
@ -33,16 +29,6 @@ static int generic_cn_dtx(decoder_t *, GQueue *, int);
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,
};
const dtx_method_t dtx_method_silence = {
.method_id = DTX_SILENCE,
.do_dtx = generic_silence_dtx,
@ -68,13 +54,9 @@ void (*codeclib_thread_loop)(void);
TYPED_GHASHTABLE(codecs_by_name, str, struct codec_def_s, str_case_hash, str_case_equal, NULL, NULL)
TYPED_GHASHTABLE(codecs_by_id_alloc, void, struct codec_def_s, g_direct_hash, g_direct_equal, NULL, g_free)
static codecs_by_name codecs_by_name_ht;
static rwlock_t generic_ffmpeg_codecs_lock = RWLOCK_STATIC_INIT;
static codecs_by_id_alloc generic_ffmpeg_codecs;
codec_def_t *codec_find(const str *name, enum media_type type) {
@ -91,46 +73,6 @@ codec_def_t *codec_get_pcm16(void) {
}
const char *avc_decoder_init(decoder_t *dec, const str *extra_opts) {
const AVCodec *codec = dec->def->decoder;
if (!codec)
return "codec not supported";
dec->avc.avpkt = av_packet_alloc();
dec->avc.avcctx = avcodec_alloc_context3(codec);
if (!dec->avc.avcctx)
return "failed to alloc codec context";
SET_CHANNELS(dec->avc.avcctx, dec->in_format.channels);
DEF_CH_LAYOUT(&dec->avc.avcctx->CH_LAYOUT, dec->in_format.channels);
dec->avc.avcctx->sample_rate = dec->in_format.clockrate;
if (dec->def->set_dec_options)
dec->def->set_dec_options(dec, extra_opts);
int i = avcodec_open2(dec->avc.avcctx, codec, NULL);
if (i) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(i));
return "failed to open codec context";
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 19, 0)
avcodec_get_supported_config(dec->avc.avcctx, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void **) &dec->avc.sample_fmts, NULL);
#else
dec->avc.sample_fmts = codec->sample_fmts;
#endif
for (const enum AVSampleFormat *sfmt = dec->avc.sample_fmts; sfmt && *sfmt != -1; sfmt++)
cdbg("supported sample format for input codec %s: %s",
codec->name, av_get_sample_fmt_name(*sfmt));
return NULL;
}
decoder_t *decoder_new_fmt(codec_def_t *def, int clockrate, int channels, int ptime,
const format_t *resample_fmt)
{
@ -272,17 +214,6 @@ gboolean decoder_has_dtx(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
avcodec_close(dec->avc.avcctx);
av_free(dec->avc.avcctx);
#endif
av_packet_free(&dec->avc.avpkt);
}
void decoder_close(decoder_t *dec) {
if (!dec)
return;
@ -298,111 +229,6 @@ void decoder_close(decoder_t *dec) {
}
int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
if (!dec->avc.avpkt)
return -1; // decoder shut down
const char *err;
int av_ret = 0;
dec->avc.avpkt->data = (unsigned char *) data->s;
dec->avc.avpkt->size = data->len;
dec->avc.avpkt->pts = dec->pts;
AVFrame *frame = NULL;
// loop until all input is consumed and all available output has been processed
int keep_going;
do {
keep_going = 0;
int got_frame = 0;
err = "failed to alloc av frame";
frame = av_frame_alloc();
if (!frame)
goto err;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (dec->avc.avpkt->size) {
av_ret = avcodec_send_packet(dec->avc.avcctx, dec->avc.avpkt);
cdbg("send packet ret %i", av_ret);
err = "failed to send packet to avcodec";
if (av_ret == 0) {
// consumed the packet
dec->avc.avpkt->size = 0;
keep_going = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // try again after reading output
else
goto err;
}
}
av_ret = avcodec_receive_frame(dec->avc.avcctx, frame);
cdbg("receive frame ret %i", av_ret);
err = "failed to receive frame from avcodec";
if (av_ret == 0) {
// got a frame
keep_going = 1;
got_frame = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // maybe needs more input now
else
goto err;
}
#else
// only do this if we have any input left
if (dec->avc.avpkt->size == 0)
break;
av_ret = avcodec_decode_audio4(dec->avc.avcctx, frame, &got_frame, dec->avc.avpkt);
cdbg("decode frame ret %i, got frame %i", av_ret, got_frame);
err = "failed to decode audio packet";
if (av_ret < 0)
goto err;
if (av_ret > 0) {
// consumed some input
err = "invalid return value";
if (av_ret > dec->avc.avpkt->size)
goto err;
dec->avc.avpkt->size -= av_ret;
dec->avc.avpkt->data += av_ret;
keep_going = 1;
}
if (got_frame)
keep_going = 1;
#endif
if (got_frame) {
cdbg("raw frame from decoder pts %llu samples %u",
(unsigned long long) frame->pts, frame->nb_samples);
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 36, 0)
frame->pts = frame->pkt_pts;
#endif
if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE))
frame->pts = dec->avc.avpkt->pts;
dec->avc.avpkt->pts += frame->nb_samples;
g_queue_push_tail(out, frame);
frame = NULL;
}
} while (keep_going);
av_frame_free(&frame);
return 0;
err:
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error decoding media packet: %s", err);
if (av_ret)
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(av_ret));
av_frame_free(&frame);
return -1;
}
static int __decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, int *ptime,
int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2)
{
@ -487,66 +313,8 @@ int decoder_dtx(decoder_t *dec, unsigned long ts, int ptime,
}
static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) {
char *msg;
if (vasprintf(&msg, fmt, ap) <= 0)
ilogs(ffmpeg, LOG_ERR | LOG_FLAG_LIMIT, "av_log message dropped");
else {
#ifdef AV_LOG_PANIC
// translate AV_LOG_ constants to LOG_ levels
if (loglevel >= AV_LOG_VERBOSE)
loglevel = LOG_DEBUG;
else if (loglevel >= AV_LOG_INFO)
loglevel = LOG_NOTICE;
else if (loglevel >= AV_LOG_WARNING)
loglevel = LOG_WARNING;
else if (loglevel >= AV_LOG_ERROR)
loglevel = LOG_ERROR;
else if (loglevel >= AV_LOG_FATAL)
loglevel = LOG_CRIT;
else
loglevel = LOG_ALERT;
#else
// defuse avlog log levels to be either DEBUG or ERR
if (loglevel <= LOG_ERR)
loglevel = LOG_ERR;
else
loglevel = LOG_DEBUG;
#endif
ilogs(ffmpeg, loglevel | LOG_FLAG_LIMIT, "av_log: %s", msg);
free(msg);
}
}
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);
if (def->avcodec_name_dec)
def->decoder = avcodec_find_decoder_by_name(def->avcodec_name_dec);
if (def->avcodec_id >= 0) {
if (!def->encoder)
def->encoder = avcodec_find_encoder(def->avcodec_id);
if (!def->decoder)
def->decoder = avcodec_find_decoder(def->avcodec_id);
}
// check if we have support if we are supposed to
if (def->avcodec_name_enc || def->avcodec_id >= 0) {
if (def->encoder)
def->support_encoding = 1;
}
if (def->avcodec_name_dec || def->avcodec_id >= 0) {
if (def->decoder)
def->support_decoding = 1;
}
}
void codeclib_free(void) {
t_hash_table_destroy(codecs_by_name_ht);
t_hash_table_destroy(generic_ffmpeg_codecs);
avformat_network_deinit();
cc_cleanup();
}
@ -618,12 +386,9 @@ void codeclib_init(int print) {
avcodec_register_all();
avfilter_register_all();
#endif
avformat_network_init();
av_log_set_callback(avlog_ilog);
codecs_by_name_ht = codecs_by_name_new();
generic_ffmpeg_codecs = codecs_by_id_alloc_new();
avc_init();
cc_init();
for (unsigned int i = 0; i < __num_codec_defs; i++) {
@ -876,60 +641,6 @@ encoder_t *encoder_new(void) {
return ret;
}
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";
enc->avc.avcctx = avcodec_alloc_context3(enc->avc.codec);
if (!enc->avc.avcctx)
return "failed to alloc codec context";
enc->actual_format = enc->requested_format;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 19, 0)
avcodec_get_supported_config(enc->avc.avcctx, enc->avc.codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void **) &enc->avc.sample_fmts, NULL);
#else
enc->avc.sample_fmts = enc->avc.codec->sample_fmts;
#endif
enc->actual_format.format = -1;
for (const enum AVSampleFormat *sfmt = enc->avc.sample_fmts; sfmt && *sfmt != -1; sfmt++) {
cdbg("supported sample format for output codec %s: %s",
enc->avc.codec->name, av_get_sample_fmt_name(*sfmt));
if (*sfmt == enc->requested_format.format)
enc->actual_format.format = *sfmt;
}
if (enc->actual_format.format == -1 && enc->avc.sample_fmts)
enc->actual_format.format = enc->avc.sample_fmts[0];
cdbg("using output sample format %s for codec %s",
av_get_sample_fmt_name(enc->actual_format.format), enc->avc.codec->name);
SET_CHANNELS(enc->avc.avcctx, enc->actual_format.channels);
DEF_CH_LAYOUT(&enc->avc.avcctx->CH_LAYOUT, enc->actual_format.channels);
enc->avc.avcctx->sample_rate = enc->actual_format.clockrate;
enc->avc.avcctx->sample_fmt = enc->actual_format.format;
enc->avc.avcctx->time_base = (AVRational){1,enc->actual_format.clockrate};
enc->avc.avcctx->bit_rate = enc->bitrate;
if (enc->def->set_enc_options)
enc->def->set_enc_options(enc, extra_opts);
int i = avcodec_open2(enc->avc.avcctx, enc->avc.codec, NULL);
if (i) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(i));
return "failed to open output context";
}
if (enc->avc.avcctx->frame_size)
enc->samples_per_frame = enc->avc.avcctx->frame_size;
else
enc->samples_per_frame = enc->actual_format.clockrate * enc->ptime / 1000;
enc->samples_per_packet = enc->samples_per_frame;
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)
@ -1021,16 +732,6 @@ err:
return -1;
}
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);
#endif
avcodec_free_context(&enc->avc.avcctx);
}
enc->avc.avcctx = NULL;
enc->avc.codec = NULL;
}
void encoder_close(encoder_t *enc) {
if (!enc)
@ -1052,79 +753,6 @@ void encoder_free(encoder_t *enc) {
g_free(enc);
}
int avc_encoder_input(encoder_t *enc, AVFrame **frame) {
int keep_going = 0;
int got_packet = 0;
int av_ret = 0;
if (!enc->avc.avcctx)
return -1;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (*frame) {
av_ret = avcodec_send_frame(enc->avc.avcctx, *frame);
cdbg("send frame ret %i", av_ret);
if (av_ret == 0) {
// consumed
*frame = NULL;
keep_going = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // check output and maybe try again
else
goto err;
}
}
av_ret = avcodec_receive_packet(enc->avc.avcctx, enc->avpkt);
cdbg("receive packet ret %i", av_ret);
if (av_ret == 0) {
// got some data
keep_going = 1;
got_packet = 1;
}
else {
if (av_ret == AVERROR(EAGAIN))
; // try again if there's still more input
else
goto err;
}
#else
if (!*frame)
return 0;
av_ret = avcodec_encode_audio2(enc->avc.avcctx, enc->avpkt, *frame, &got_packet);
cdbg("encode frame ret %i, got packet %i", av_ret, got_packet);
if (av_ret == 0)
*frame = NULL; // consumed
else
goto err;
if (got_packet)
keep_going = 1;
#endif
if (!got_packet)
return keep_going;
cdbg("output avpkt size is %i", (int) enc->avpkt->size);
cdbg("output pkt pts/dts is %li/%li", (long) enc->avpkt->pts,
(long) enc->avpkt->dts);
// the encoder may return frames with the same dts multiple consecutive times.
// the muxer may not like this, so ensure monotonically increasing dts.
if (enc->mux_dts > enc->avpkt->dts)
enc->avpkt->dts = enc->mux_dts;
if (enc->avpkt->pts < enc->avpkt->dts)
enc->avpkt->pts = enc->avpkt->dts;
return keep_going;
err:
if (av_ret)
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Error returned from libav: %s", av_error(av_ret));
return -1;
}
int encoder_input_data(encoder_t *enc, AVFrame *frame,
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2)
@ -1260,21 +888,6 @@ int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output, size
}
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);
int ret = av_opt_set_int(enc->avc.avcctx, opt, val, AV_OPT_SEARCH_CHILDREN);
if (!ret)
return 0;
ilog(LOG_WARN, "Failed to set ffmpeg '%s' option for codec '%s' to %" PRId64 ": %s",
opt, enc->def->rtpname, val, av_error(ret));
return -1;
}
void codeclib_key_value_parse(const str *instr, bool need_value,
void (*cb)(str *key, str *value, void *data), void *data)
{
@ -1421,44 +1034,3 @@ void frame_fill_dtmf_samples(enum AVSampleFormat fmt, void *samples, unsigned in
break;
}
}
codec_def_t *codec_def_make_generic_av(enum AVCodecID id) {
{
RWLOCK_R(&generic_ffmpeg_codecs_lock);
struct codec_def_s *ret = t_hash_table_lookup(generic_ffmpeg_codecs, GINT_TO_POINTER(id));
if (ret)
return ret;
}
{
RWLOCK_W(&generic_ffmpeg_codecs_lock);
struct codec_def_s *ret = t_hash_table_lookup(generic_ffmpeg_codecs, GINT_TO_POINTER(id));
if (ret)
return ret;
const AVCodec *codec = avcodec_find_decoder(id);
if (!codec)
return NULL;
ret = g_new(__typeof(*ret), 1);
*ret = (__typeof(*ret)) {
.rtpname = "generic ffmpeg codec",
.rtpname_str = STR_CONST("generic ffmpeg codec"),
.avcodec_id = id,
.default_clockrate_fact = {1,1},
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.decoder = codec,
.support_decoding = 1,
.support_encoding = 1, // just pretend
};
t_hash_table_insert(generic_ffmpeg_codecs, GINT_TO_POINTER(id), ret);
return ret;
}
}

@ -15,6 +15,9 @@ packetizer_f packetizer_samplestream; // flat stream of samples
int format_cmp_ignore(const struct rtp_payload_type *, const struct rtp_payload_type *);
void avc_init(void);
void avc_cleanup(void);
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);

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

@ -19,6 +19,7 @@ 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
CODEC_SRCS += avc.c
endif
LDFLAGS += -pie

@ -294,6 +294,7 @@ test-mix-buffer: test-mix-buffer.o \
../daemon/helpers.o \
../daemon/rtp.o \
../daemon/ssrc.o \
../lib/avc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/cchain.o \
@ -323,6 +324,7 @@ 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/avc.o \
../lib/bencode.o \
../lib/cchain.o \
../lib/codeclib.o \
@ -335,6 +337,7 @@ test-amr-decode: test-amr-decode.o \
test-amr-encode: test-amr-encode.o \
$(COMMONOBJS) \
../lib/amr.strhash.o \
../lib/avc.o \
../lib/bencode.o \
../lib/cchain.o \
../lib/codeclib.o \
@ -395,6 +398,7 @@ test-stats: test-stats.o \
../daemon/timerthread.o \
../daemon/udp_listener.o \
../daemon/websocket.o \
../lib/avc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/cchain.o \
@ -456,6 +460,7 @@ test-transcode: test-transcode.o \
../daemon/udp_listener.o \
../daemon/websocket.o \
../lib/amr.strhash.o \
../lib/avc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/cchain.o \
@ -481,6 +486,7 @@ test-transcode: test-transcode.o \
test-sequencer: test-sequencer.o \
$(COMMONOBJS) \
../lib/avc.o \
../lib/bencode.o \
../lib/cchain.o \
../lib/codeclib.o \
@ -490,6 +496,7 @@ test-sequencer: test-sequencer.o \
test-resample: test-resample.o \
$(COMMONOBJS) \
../lib/avc.o \
../lib/bencode.o \
../lib/cchain.o \
../lib/codeclib.o \
@ -505,6 +512,7 @@ test-payload-tracker: test-payload-tracker.o \
../daemon/helpers.o \
../daemon/rtp.o \
../daemon/ssrc.o \
../lib/avc.o \
../lib/bencode.o \
../lib/bufferpool.o \
../lib/cchain.o \
@ -540,3 +548,5 @@ spandsp_recv_fax_t38.c: ../lib/spandsp_logging.compat
spandsp_recv_fax_pcm.c: ../lib/spandsp_logging.compat
../lib/codeclib.o: ../lib/fix_frame_channel_layout.compat
../lib/avc.o: ../lib/fix_frame_channel_layout.compat

Loading…
Cancel
Save