aboutsummaryrefslogtreecommitdiffstats
path: root/fftools/ffmpeg_dec.c
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2024-02-19 10:27:44 +0100
committerAnton Khirnov <anton@khirnov.net>2024-03-13 08:01:27 +0100
commita9193f7b7d65aafa326e25571c6672636a8ee3d2 (patch)
tree3db4310d628d166d97df3972ad395776fa59d08b /fftools/ffmpeg_dec.c
parentb98af440c575c1f9a706bd57ee5f1dd8b9ff82cc (diff)
downloadffmpeg-a9193f7b7d65aafa326e25571c6672636a8ee3d2.tar.gz
fftools/ffmpeg: add loopback decoding
This allows to send an encoder's output back to decoding and feed the result into a complex filtergraph.
Diffstat (limited to 'fftools/ffmpeg_dec.c')
-rw-r--r--fftools/ffmpeg_dec.c164
1 files changed, 161 insertions, 3 deletions
diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c
index 3bf7ab4960..c41c5748e5 100644
--- a/fftools/ffmpeg_dec.c
+++ b/fftools/ffmpeg_dec.c
@@ -17,6 +17,7 @@
*/
#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavutil/error.h"
#include "libavutil/log.h"
@@ -71,9 +72,16 @@ typedef struct DecoderPriv {
Scheduler *sch;
unsigned sch_idx;
+ // this decoder's index in decoders or -1
+ int index;
void *log_parent;
char log_name[32];
char *parent_name;
+
+ struct {
+ AVDictionary *opts;
+ const AVCodec *codec;
+ } standalone_init;
} DecoderPriv;
static DecoderPriv *dp_from_dec(Decoder *d)
@@ -101,6 +109,8 @@ void dec_free(Decoder **pdec)
av_frame_free(&dp->frame);
av_packet_free(&dp->pkt);
+ av_dict_free(&dp->standalone_init.opts);
+
for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++)
av_frame_free(&dp->sub_prev[i]);
av_frame_free(&dp->sub_heartbeat);
@@ -145,6 +155,7 @@ static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts)
if (!dp->pkt)
goto fail;
+ dp->index = -1;
dp->dec.class = &dec_class;
dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE;
dp->last_frame_pts = AV_NOPTS_VALUE;
@@ -754,11 +765,56 @@ static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame)
}
}
-static void dec_thread_set_name(const DecoderPriv *dp)
+static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts,
+ const DecoderOpts *o, AVFrame *param_out);
+
+static int dec_standalone_open(DecoderPriv *dp, const AVPacket *pkt)
{
+ DecoderOpts o;
+ const FrameData *fd;
char name[16];
- snprintf(name, sizeof(name), "dec%s:%s", dp->parent_name,
- dp->dec_ctx->codec->name);
+
+ if (!pkt->opaque_ref)
+ return AVERROR_BUG;
+ fd = (FrameData *)pkt->opaque_ref->data;
+
+ if (!fd->par_enc)
+ return AVERROR_BUG;
+
+ memset(&o, 0, sizeof(o));
+
+ o.par = fd->par_enc;
+ o.time_base = pkt->time_base;
+
+ o.codec = dp->standalone_init.codec;
+ if (!o.codec)
+ o.codec = avcodec_find_decoder(o.par->codec_id);
+ if (!o.codec) {
+ const AVCodecDescriptor *desc = avcodec_descriptor_get(o.par->codec_id);
+
+ av_log(dp, AV_LOG_ERROR, "Cannot find a decoder for codec ID '%s'\n",
+ desc ? desc->name : "?");
+ return AVERROR_DECODER_NOT_FOUND;
+ }
+
+ snprintf(name, sizeof(name), "dec%d", dp->index);
+ o.name = name;
+
+ return dec_open(dp, &dp->standalone_init.opts, &o, NULL);
+}
+
+static void dec_thread_set_name(const DecoderPriv *dp)
+{
+ char name[16] = "dec";
+
+ if (dp->index >= 0)
+ av_strlcatf(name, sizeof(name), "%d", dp->index);
+ else if (dp->parent_name)
+ av_strlcat(name, dp->parent_name, sizeof(name));
+
+ if (dp->dec_ctx)
+ av_strlcatf(name, sizeof(name), ":%s", dp->dec_ctx->codec->name);
+
ff_thread_setname(name);
}
@@ -814,6 +870,22 @@ static int decoder_thread(void *arg)
av_log(dp, AV_LOG_VERBOSE, "Decoder thread received %s packet\n",
flush_buffers ? "flush" : "EOF");
+ // this is a standalone decoder that has not been initialized yet
+ if (!dp->dec_ctx) {
+ if (flush_buffers)
+ continue;
+ if (input_status < 0) {
+ av_log(dp, AV_LOG_ERROR,
+ "Cannot initialize a standalone decoder\n");
+ ret = input_status;
+ goto finish;
+ }
+
+ ret = dec_standalone_open(dp, dt.pkt);
+ if (ret < 0)
+ goto finish;
+ }
+
ret = packet_decode(dp, have_data ? dt.pkt : NULL, dt.frame);
av_packet_unref(dt.pkt);
@@ -1075,6 +1147,7 @@ static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts,
dp->flags = o->flags;
dp->log_parent = o->log_parent;
+ dp->dec.type = codec->type;
dp->framerate_in = o->framerate;
dp->hwaccel_id = o->hwaccel_id;
@@ -1188,3 +1261,88 @@ fail:
dec_free((Decoder**)&dp);
return ret;
}
+
+int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch)
+{
+ DecoderPriv *dp;
+
+ OutputFile *of;
+ OutputStream *ost;
+ int of_index, ost_index;
+ char *p;
+
+ unsigned enc_idx;
+ int ret;
+
+ ret = dec_alloc(&dp, sch, 0);
+ if (ret < 0)
+ return ret;
+
+ dp->index = nb_decoders;
+
+ ret = GROW_ARRAY(decoders, nb_decoders);
+ if (ret < 0) {
+ dec_free((Decoder **)&dp);
+ return ret;
+ }
+
+ decoders[nb_decoders - 1] = (Decoder *)dp;
+
+ of_index = strtol(arg, &p, 0);
+ if (of_index < 0 || of_index >= nb_output_files) {
+ av_log(dp, AV_LOG_ERROR, "Invalid output file index '%d' in %s\n", of_index, arg);
+ return AVERROR(EINVAL);
+ }
+ of = output_files[of_index];
+
+ ost_index = strtol(p + 1, NULL, 0);
+ if (ost_index < 0 || ost_index >= of->nb_streams) {
+ av_log(dp, AV_LOG_ERROR, "Invalid output stream index '%d' in %s\n", ost_index, arg);
+ return AVERROR(EINVAL);
+ }
+ ost = of->streams[ost_index];
+
+ if (!ost->enc) {
+ av_log(dp, AV_LOG_ERROR, "Output stream %s has no encoder\n", arg);
+ return AVERROR(EINVAL);
+ }
+
+ dp->dec.type = ost->type;
+
+ ret = enc_loopback(ost->enc);
+ if (ret < 0)
+ return ret;
+ enc_idx = ret;
+
+ ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx));
+ if (ret < 0)
+ return ret;
+
+ ret = av_dict_copy(&dp->standalone_init.opts, o->g->codec_opts, 0);
+ if (ret < 0)
+ return ret;
+
+ if (o->codec_names.nb_opt) {
+ const char *name = o->codec_names.opt[o->codec_names.nb_opt - 1].u.str;
+ dp->standalone_init.codec = avcodec_find_decoder_by_name(name);
+ if (!dp->standalone_init.codec) {
+ av_log(dp, AV_LOG_ERROR, "No such decoder: %s\n", name);
+ return AVERROR_DECODER_NOT_FOUND;
+ }
+ }
+
+ return 0;
+}
+
+int dec_filter_add(Decoder *d, InputFilter *ifilter, InputFilterOptions *opts)
+{
+ DecoderPriv *dp = dp_from_dec(d);
+ char name[16];
+
+ snprintf(name, sizeof(name), "dec%d", dp->index);
+ opts->name = av_strdup(name);
+ if (!opts->name)
+ return AVERROR(ENOMEM);
+
+ return dp->sch_idx;
+}