From patchwork Sun May 28 09:14:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 41871 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c51c:b0:10c:5e6f:955f with SMTP id gm28csp988583pzb; Sun, 28 May 2023 02:18:18 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6J5ejeYLIh77JaxD5ef4x+Ln3DRBHIrOp/8D4qLjelvMrA3A0Z8NzkbTgJ9cC5kpVKhs6Y X-Received: by 2002:aa7:d5d7:0:b0:504:9349:7901 with SMTP id d23-20020aa7d5d7000000b0050493497901mr4472521eds.38.1685265498015; Sun, 28 May 2023 02:18:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685265498; cv=none; d=google.com; s=arc-20160816; b=p5M10jdPAxuiH8eh4x3in3+wFAY2YaQYPVt7eQmXPeGRAqFI5WhIQ/qTf7xVjrXh17 pqp+/Ybw0SRQbGZENdG1I/fmiscb/A/cKP89lLCjHRSg9dC/VAwyzpyMGRlKlR2ssm9s 6IDCdcZTRRv4zws0/NRD3M4sdcXahFi8stpi1tMCldPUAqnDZjm3ITu8e3S5rrbak8WV lg/PpCQ4eSK6rFI9qjrnOTtJ0O3yRGj6BbRrnr/87OFRNXNeq2AUXldW8iE6T5jmvE0x vzORP9MXRf3WFw6VKJ5yht0wRL1i++o0ScpBmWIijQgJc2AEQob9v9+3qBiazR23oNcl +Njg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:delivered-to; bh=u6JT75+9fkuNknJi4U1awuetxeSpX7Ac/dG3/X6Nsaw=; b=GDX8LgRtK/vC+1OMixIE9It1cz5af0LKJ/9Gu4fkgPNjbPUK8dS7RhDlLwbYjJAwH8 VWkr5psrnq7+Owdw3wCPU9ntSUVKTNVBAcoundevtsy//Q2RXdSz7dCHNj4WBUaY+JBt 8cRINjzt1QaQLYuHRSD1rf1lnRcvKo/jlvhRLaGxStGEii8/Jm0C9JQZQT9QUqg12Vk1 xKC+2h5AHTyf6o0QnYQ82274NLD5UJv3uStLnwiQIyPgbUDvgH+s/EWzjV41eyV8ONTr f6szqpKmG5yVR0UuH4gKc17ffAGfXCwsIxTJbEWGYltd4GA6azLzOc64X91YrOq/F6rJ bDpQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id s10-20020aa7c54a000000b005149c0dbb07si461136edr.450.2023.05.28.02.18.17; Sun, 28 May 2023 02:18:17 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B888468C28B; Sun, 28 May 2023 12:15:17 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail0.khirnov.net (red.khirnov.net [176.97.15.12]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D248768C22C for ; Sun, 28 May 2023 12:14:54 +0300 (EEST) Received: from localhost (localhost [IPv6:::1]) by mail0.khirnov.net (Postfix) with ESMTP id 0B1CD2404EE for ; Sun, 28 May 2023 11:14:54 +0200 (CEST) Received: from mail0.khirnov.net ([IPv6:::1]) by localhost (mail0.khirnov.net [IPv6:::1]) (amavisd-new, port 10024) with ESMTP id 525e_9z0nfLK for ; Sun, 28 May 2023 11:14:51 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail0.khirnov.net (Postfix) with ESMTPS id 906562405B5 for ; Sun, 28 May 2023 11:14:47 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 61B3A3A0664 for ; Sun, 28 May 2023 11:14:41 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sun, 28 May 2023 11:14:00 +0200 Message-Id: <20230528091416.17927-8-anton@khirnov.net> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230528091416.17927-1-anton@khirnov.net> References: <20230528091416.17927-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 08/24] fftools/ffmpeg_filter: create Input/OutputFilters together with FilterGraph X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: l7C0NPUlSTaj This way the list of filtergraph inputs/outputs is always known after FilterGraph creation. This will allow treating simple and complex filtergraphs in a more uniform manner. --- fftools/ffmpeg_filter.c | 157 +++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 83 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 5169a3ca82..d74eeef52a 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -62,6 +62,10 @@ typedef struct InputFilterPriv { // used to hold submitted input AVFrame *frame; + /* for filters that are not yet bound to an input stream, + * this stores the input linklabel, if any */ + uint8_t *linklabel; + // filter data type enum AVMediaType type; // source data type: AVMEDIA_TYPE_SUBTITLE for sub2video, @@ -456,8 +460,6 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) ifp->ist = ist; ifp->type_src = ist->st->codecpar->codec_type; - ifp->type = ifp->type_src == AVMEDIA_TYPE_SUBTITLE ? - AVMEDIA_TYPE_VIDEO : ifp->type_src; return 0; } @@ -505,7 +507,7 @@ void fg_free(FilterGraph **pfg) av_frame_free(&frame); av_fifo_freep2(&ifp->frame_queue); } - if (ist->sub2video.sub_queue) { + if (ist && ist->sub2video.sub_queue) { AVSubtitle sub; while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0) avsubtitle_free(&sub); @@ -517,6 +519,7 @@ void fg_free(FilterGraph **pfg) av_frame_free(&ifp->frame); av_buffer_unref(&ifp->hw_frames_ctx); + av_freep(&ifp->linklabel); av_freep(&ifilter->name); av_freep(&fg->inputs[j]); } @@ -542,6 +545,10 @@ FilterGraph *fg_create(char *graph_desc) FilterGraphPriv *fgp = allocate_array_elem(&filtergraphs, sizeof(*fgp), &nb_filtergraphs); FilterGraph *fg = &fgp->fg; + AVFilterInOut *inputs, *outputs; + AVFilterGraph *graph; + int ret = 0; + fg->index = nb_filtergraphs - 1; fgp->graph_desc = graph_desc; @@ -549,6 +556,48 @@ FilterGraph *fg_create(char *graph_desc) if (!fgp->frame) report_and_exit(AVERROR(ENOMEM)); + /* this graph is only used for determining the kinds of inputs + * and outputs we have, and is discarded on exit from this function */ + graph = avfilter_graph_alloc(); + if (!graph) + report_and_exit(AVERROR(ENOMEM)); + graph->nb_threads = 1; + + ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL); + if (ret < 0) + goto fail; + + for (AVFilterInOut *cur = inputs; cur; cur = cur->next) { + InputFilter *const ifilter = ifilter_alloc(fg); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + ifp->linklabel = cur->name; + cur->name = NULL; + + ifp->type = avfilter_pad_get_type(cur->filter_ctx->input_pads, + cur->pad_idx); + ifilter->name = describe_filter_link(fg, cur, 1); + } + + for (AVFilterInOut *cur = outputs; cur; cur = cur->next) { + OutputFilter *const ofilter = ofilter_alloc(fg); + + ofilter->linklabel = cur->name; + cur->name = NULL; + + ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, + cur->pad_idx); + ofilter->name = describe_filter_link(fg, cur, 0); + } + +fail: + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + avfilter_graph_free(&graph); + + if (ret < 0) + report_and_exit(ret); + return fg; } @@ -557,8 +606,6 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost, { FilterGraph *fg; FilterGraphPriv *fgp; - OutputFilter *ofilter; - InputFilter *ifilter; int ret; fg = fg_create(graph_desc); @@ -568,26 +615,32 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost, fgp->is_simple = 1; - ofilter = ofilter_alloc(fg); - ofilter->ost = ost; + if (fg->nb_inputs != 1 || fg->nb_outputs != 1) { + av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " + "to have exactly 1 input and 1 output. " + "However, it had %d input(s) and %d output(s). Please adjust, " + "or use a complex filtergraph (-filter_complex) instead.\n", + graph_desc, fg->nb_inputs, fg->nb_outputs); + return AVERROR(EINVAL); + } - ost->filter = ofilter; + fg->outputs[0]->ost = ost; - ifilter = ifilter_alloc(fg); + ost->filter = fg->outputs[0]; - ret = ifilter_bind_ist(ifilter, ist); + ret = ifilter_bind_ist(fg->inputs[0], ist); if (ret < 0) return ret; return 0; } -static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) +static void init_input_filter(FilterGraph *fg, InputFilter *ifilter) { FilterGraphPriv *fgp = fgp_from_fg(fg); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); InputStream *ist = NULL; - enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); - InputFilter *ifilter; + enum AVMediaType type = ifp->type; int i, ret; // TODO: support other filter types @@ -597,11 +650,11 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) exit_program(1); } - if (in->name) { + if (ifp->linklabel) { AVFormatContext *s; AVStream *st = NULL; char *p; - int file_idx = strtol(in->name, &p, 0); + int file_idx = strtol(ifp->linklabel, &p, 0); if (file_idx < 0 || file_idx >= nb_input_files) { av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", @@ -631,63 +684,27 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) ist = ist_find_unused(type); if (!ist) { av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for " - "unlabeled input pad %d on filter %s\n", in->pad_idx, - in->filter_ctx->name); + "unlabeled input pad %s\n", ifilter->name); exit_program(1); } } av_assert0(ist); - ifilter = ifilter_alloc(fg); - ifilter->name = describe_filter_link(fg, in, 1); - ret = ifilter_bind_ist(ifilter, ist); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error binding an input stream to complex filtergraph input %s.\n", - in->name ? in->name : ""); + ifilter->name); exit_program(1); } } int init_complex_filtergraph(FilterGraph *fg) { - FilterGraphPriv *fgp = fgp_from_fg(fg); - AVFilterInOut *inputs, *outputs, *cur; - AVFilterGraph *graph; - int ret = 0; - - /* this graph is only used for determining the kinds of inputs - * and outputs we have, and is discarded on exit from this function */ - graph = avfilter_graph_alloc(); - if (!graph) - return AVERROR(ENOMEM); - graph->nb_threads = 1; - - ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL); - if (ret < 0) - goto fail; - - for (cur = inputs; cur; cur = cur->next) - init_input_filter(fg, cur); - - for (cur = outputs; cur;) { - OutputFilter *const ofilter = ofilter_alloc(fg); - - ofilter->linklabel = cur->name; - cur->name = NULL; - - ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, - cur->pad_idx); - ofilter->name = describe_filter_link(fg, cur, 0); - cur = cur->next; - } - -fail: - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - avfilter_graph_free(&graph); - return ret; + // bind filtergraph inputs to input streams + for (int i = 0; i < fg->nb_inputs; i++) + init_input_filter(fg, fg->inputs[i]); + return 0; } static int insert_trim(int64_t start_time, int64_t duration, @@ -1324,32 +1341,6 @@ int configure_filtergraph(FilterGraph *fg) if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs, hw_device)) < 0) goto fail; - if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { - const char *num_inputs; - const char *num_outputs; - if (!outputs) { - num_outputs = "0"; - } else if (outputs->next) { - num_outputs = ">1"; - } else { - num_outputs = "1"; - } - if (!inputs) { - num_inputs = "0"; - } else if (inputs->next) { - num_inputs = ">1"; - } else { - num_inputs = "1"; - } - av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " - "to have exactly 1 input and 1 output." - " However, it had %s input(s) and %s output(s)." - " Please adjust, or use a complex filtergraph (-filter_complex) instead.\n", - graph_desc, num_inputs, num_outputs); - ret = AVERROR(EINVAL); - goto fail; - } - for (cur = inputs, i = 0; cur; cur = cur->next, i++) if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { avfilter_inout_free(&inputs);