From 7b3ac41969b0437e5abfbeac1eccd70b1a328c79 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 7 Aug 2026 11:47:19 -0400 Subject: [PATCH] MT#65413 split out AVC code Change-Id: I99440992813bd24d5e92bbd6b9323c4f2e99a3dc --- lib/Makefile | 1 + lib/avc.c | 447 ++++++++++++++++++++++++++++++++++++++++++++ lib/codeclib.c | 430 +----------------------------------------- lib/codecmod.h | 3 + lib/common.Makefile | 2 +- lib/flags.Makefile | 1 + t/Makefile | 10 + 7 files changed, 464 insertions(+), 430 deletions(-) create mode 100644 lib/avc.c diff --git a/lib/Makefile b/lib/Makefile index ea880f667..80f7e1fab 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/avc.c b/lib/avc.c new file mode 100644 index 000000000..e9946ce4a --- /dev/null +++ b/lib/avc.c @@ -0,0 +1,447 @@ +#include "codecmod.h" +#include +#include +#include +#include +#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(); +} diff --git a/lib/codeclib.c b/lib/codeclib.c index e65428a36..199ca70b3 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1,8 +1,4 @@ #include "codeclib.h" -#include -#include -#include -#include #include #include #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; - } -} diff --git a/lib/codecmod.h b/lib/codecmod.h index e0cebe5ac..4002b5ba9 100644 --- a/lib/codecmod.h +++ b/lib/codecmod.h @@ -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); diff --git a/lib/common.Makefile b/lib/common.Makefile index 1879e6ff6..c744b0b2a 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.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 diff --git a/lib/flags.Makefile b/lib/flags.Makefile index 485523594..59876aaca 100644 --- a/lib/flags.Makefile +++ b/lib/flags.Makefile @@ -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 diff --git a/t/Makefile b/t/Makefile index 092e99997..acf9644b4 100644 --- a/t/Makefile +++ b/t/Makefile @@ -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