From patchwork Wed Dec 13 19:29:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 45117 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1225:b0:181:818d:5e7f with SMTP id v37csp4501228pzf; Wed, 13 Dec 2023 11:30:55 -0800 (PST) X-Google-Smtp-Source: AGHT+IHZNuRuEzB7QrwmSpqc8Igjn0gB4o5qrId+KfG4I4/xShspdFL8UTfiHjgOMwVNq3sXLQLt X-Received: by 2002:a17:907:c003:b0:a1d:6d:1392 with SMTP id ss3-20020a170907c00300b00a1d006d1392mr9776542ejc.1.1702495855580; Wed, 13 Dec 2023 11:30:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702495855; cv=none; d=google.com; s=arc-20160816; b=QEKwYp7KNn3E/j3XqmOcv56kXa2WCmdyfqN117f+ba+ohGctxRwoMN1gSv674m8Mng WogPSgmEIkUshyTmWwoNHlSA8DOo+j7yZoneXywDW5xE+8+TI8QtL312Ji3fubWrzEh2 eZyjLRH48u1ra9FDWR864An0oWn5fGWJMot4Fl5/tobumGqClK6y719a9UYXV5jNVUHy x8rIQknHi0CIbIjqVR8uVGmrTPmrFcnRGK+n7/wtjKDYc0jlXfA7aKYawXRf6WJ4FGUA 1NQm0vPyAx94gAI/rrKRe41k81o2BPQWlKQE+zk0RlME7XWpybkL5Tj77jwV+s6o9dkz e39w== 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:message-id:date:to:from :delivered-to; bh=uVKpT2WzxMuKI/GQgzDMiEN8lEvOektOGds0jvXkOd4=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=qxMYy5ehDFC73CpWFu5awUyeLW8b1yWQ7FBe9e72FXBOLlF9DhZT6uxxwCPEduQxag UOuKlyr+Od29P11El3KDMV4N3X/4n3mAFfSBLLw16eEqEys8Qhd0b3N5QM91SqP1bniF lGmayWL2p9gxjmeO25/N7trZ/uk2pYmk/6935CLY3riFrSvbkHHUMmTAe3lHKaMHl7/B bsMIAVJIi9ctICTVdGgyU1OSxxOw7DnJ+Xpa1ndCoMMvvqxrfmFjS+Y5n6+r4V2BhoXo d20qKMiRqVFa5qYQFX6rkyyuw1HKKonrAeLWd25FhVDWiCLERxbD4RFyS/mVCjgXJBzH tCmg== 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 g25-20020a1709064e5900b00a1cd9627470si5400526ejw.341.2023.12.13.11.30.54; Wed, 13 Dec 2023 11:30:55 -0800 (PST) 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 EEAFD68D0D8; Wed, 13 Dec 2023 21:30:31 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3D9BA68CF9B for ; Wed, 13 Dec 2023 21:30:23 +0200 (EET) Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id D0F72163E for ; Wed, 13 Dec 2023 20:30:22 +0100 (CET) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id kzGIsDSAa3M8 for ; Wed, 13 Dec 2023 20:30:22 +0100 (CET) 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 mail1.khirnov.net (Postfix) with ESMTPS id 52BE71B9A for ; Wed, 13 Dec 2023 20:30:21 +0100 (CET) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 0E80E3A0582 for ; Wed, 13 Dec 2023 20:30:14 +0100 (CET) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Wed, 13 Dec 2023 20:29:56 +0100 Message-ID: <20231213193007.17471-1-anton@khirnov.net> X-Mailer: git-send-email 2.42.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 01/12] fftools/ffmpeg_filter: move FilterGraph.graph to FilterGraphThread 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: 0AAfLLraSBse The AVFilterGraph is fully owned by the filtering thread and should never be accessed outside of it. --- fftools/ffmpeg.h | 2 - fftools/ffmpeg_filter.c | 120 +++++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index ba82b7490d..69bd23f576 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -322,8 +322,6 @@ typedef struct FilterGraph { const AVClass *class; int index; - AVFilterGraph *graph; - InputFilter **inputs; int nb_inputs; OutputFilter **outputs; diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index ada235b084..3b0d457cc2 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -80,6 +80,8 @@ static const FilterGraphPriv *cfgp_from_cfg(const FilterGraph *fg) // data that is local to the filter thread and not visible outside of it typedef struct FilterGraphThread { + AVFilterGraph *graph; + AVFrame *frame; // Temporary buffer for output frames, since on filtergraph reset @@ -850,7 +852,6 @@ void fg_free(FilterGraph **pfg) return; fgp = fgp_from_fg(fg); - avfilter_graph_free(&fg->graph); for (int j = 0; j < fg->nb_inputs; j++) { InputFilter *ifilter = fg->inputs[j]; InputFilterPriv *ifp = ifp_from_ifilter(ifilter); @@ -1213,7 +1214,8 @@ static int insert_filter(AVFilterContext **last_filter, int *pad_idx, return 0; } -static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +static int configure_output_video_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) { OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; @@ -1228,7 +1230,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofp->filter, avfilter_get_by_name("buffersink"), - name, NULL, NULL, fg->graph); + name, NULL, NULL, graph); if (ret < 0) return ret; @@ -1248,7 +1250,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, snprintf(name, sizeof(name), "scaler_out_%d_%d", ost->file_index, ost->index); if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), - name, args, NULL, fg->graph)) < 0) + name, args, NULL, graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) return ret; @@ -1267,7 +1269,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("format"), - "format", pix_fmts, NULL, fg->graph); + "format", pix_fmts, NULL, graph); av_bprint_finalize(&bprint, NULL); if (ret < 0) return ret; @@ -1292,7 +1294,8 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, return 0; } -static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +static int configure_output_audio_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) { OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; @@ -1306,7 +1309,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); ret = avfilter_graph_create_filter(&ofp->filter, avfilter_get_by_name("abuffersink"), - name, NULL, NULL, fg->graph); + name, NULL, NULL, graph); if (ret < 0) return ret; if ((ret = av_opt_set_int(ofp->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) @@ -1320,7 +1323,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, \ ret = avfilter_graph_create_filter(&filt_ctx, \ avfilter_get_by_name(filter_name), \ - filter_name, arg, NULL, fg->graph); \ + filter_name, arg, NULL, graph); \ if (ret < 0) \ goto fail; \ \ @@ -1361,7 +1364,7 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, ost->file_index, ost->index); ret = avfilter_graph_create_filter(&format, avfilter_get_by_name("aformat"), - name, args.str, NULL, fg->graph); + name, args.str, NULL, graph); if (ret < 0) goto fail; @@ -1400,8 +1403,8 @@ fail: return ret; } -static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, - AVFilterInOut *out) +static int configure_output_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) { if (!ofilter->ost) { av_log(fg, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); @@ -1409,8 +1412,8 @@ static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, } switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { - case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); - case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); + case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, graph, ofilter, out); + case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, graph, ofilter, out); default: av_assert0(0); return 0; } } @@ -1444,8 +1447,8 @@ static void sub2video_prepare(InputFilterPriv *ifp) ifp->sub2video.initialize = 1; } -static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) +static int configure_input_video_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); @@ -1498,7 +1501,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, if ((ret = avfilter_graph_create_filter(&ifp->filter, buffer_filt, name, - args.str, NULL, fg->graph)) < 0) + args.str, NULL, graph)) < 0) goto fail; par->hw_frames_ctx = ifp->hw_frames_ctx; ret = av_buffersrc_parameters_set(ifp->filter, par); @@ -1574,8 +1577,8 @@ fail: return ret; } -static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) +static int configure_input_audio_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); AVFilterContext *last_filter; @@ -1610,7 +1613,7 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, if ((ret = avfilter_graph_create_filter(&ifp->filter, abuffer_filt, name, args.str, NULL, - fg->graph)) < 0) + graph)) < 0) return ret; last_filter = ifp->filter; @@ -1633,24 +1636,24 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, return 0; } -static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) +static int configure_input_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) { switch (ifp_from_ifilter(ifilter)->type) { - case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); - case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); + case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, graph, ifilter, in); + case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, graph, ifilter, in); default: av_assert0(0); return 0; } } -static void cleanup_filtergraph(FilterGraph *fg) +static void cleanup_filtergraph(FilterGraph *fg, FilterGraphThread *fgt) { int i; for (i = 0; i < fg->nb_outputs; i++) ofp_from_ofilter(fg->outputs[i])->filter = NULL; for (i = 0; i < fg->nb_inputs; i++) ifp_from_ifilter(fg->inputs[i])->filter = NULL; - avfilter_graph_free(&fg->graph); + avfilter_graph_free(&fgt->graph); } static int filter_is_buffersrc(const AVFilterContext *f) @@ -1677,9 +1680,9 @@ static int graph_is_meta(AVFilterGraph *graph) return 1; } -static int sub2video_frame(InputFilter *ifilter, AVFrame *frame); +static int sub2video_frame(InputFilter *ifilter, AVFrame *frame, int buffer); -static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) +static int configure_filtergraph(FilterGraph *fg, FilterGraphThread *fgt) { FilterGraphPriv *fgp = fgp_from_fg(fg); AVBufferRef *hw_device; @@ -1688,27 +1691,28 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) int have_input_eof = 0; const char *graph_desc = fgp->graph_desc; - cleanup_filtergraph(fg); - if (!(fg->graph = avfilter_graph_alloc())) + cleanup_filtergraph(fg, fgt); + fgt->graph = avfilter_graph_alloc(); + if (!fgt->graph) return AVERROR(ENOMEM); if (simple) { OutputStream *ost = fg->outputs[0]->ost; if (filter_nbthreads) { - ret = av_opt_set(fg->graph, "threads", filter_nbthreads, 0); + ret = av_opt_set(fgt->graph, "threads", filter_nbthreads, 0); if (ret < 0) goto fail; } else { const AVDictionaryEntry *e = NULL; e = av_dict_get(ost->encoder_opts, "threads", NULL, 0); if (e) - av_opt_set(fg->graph, "threads", e->value, 0); + av_opt_set(fgt->graph, "threads", e->value, 0); } if (av_dict_count(ost->sws_dict)) { ret = av_dict_get_string(ost->sws_dict, - &fg->graph->scale_sws_opts, + &fgt->graph->scale_sws_opts, '=', ':'); if (ret < 0) goto fail; @@ -1719,20 +1723,20 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) ret = av_dict_get_string(ost->swr_opts, &args, '=', ':'); if (ret < 0) goto fail; - av_opt_set(fg->graph, "aresample_swr_opts", args, 0); + av_opt_set(fgt->graph, "aresample_swr_opts", args, 0); av_free(args); } } else { - fg->graph->nb_threads = filter_complex_nbthreads; + fgt->graph->nb_threads = filter_complex_nbthreads; } hw_device = hw_device_for_filter(); - if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs, hw_device)) < 0) + if ((ret = graph_parse(fgt->graph, graph_desc, &inputs, &outputs, hw_device)) < 0) goto fail; for (cur = inputs, i = 0; cur; cur = cur->next, i++) - if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { + if ((ret = configure_input_filter(fg, fgt->graph, fg->inputs[i], cur)) < 0) { avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); goto fail; @@ -1740,7 +1744,7 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) avfilter_inout_free(&inputs); for (cur = outputs, i = 0; cur; cur = cur->next, i++) { - ret = configure_output_filter(fg, fg->outputs[i], cur); + ret = configure_output_filter(fg, fgt->graph, fg->outputs[i], cur); if (ret < 0) { avfilter_inout_free(&outputs); goto fail; @@ -1749,11 +1753,11 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) avfilter_inout_free(&outputs); if (fgp->disable_conversions) - avfilter_graph_set_auto_convert(fg->graph, AVFILTER_AUTO_CONVERT_NONE); - if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) + avfilter_graph_set_auto_convert(fgt->graph, AVFILTER_AUTO_CONVERT_NONE); + if ((ret = avfilter_graph_config(fgt->graph, NULL)) < 0) goto fail; - fgp->is_meta = graph_is_meta(fg->graph); + fgp->is_meta = graph_is_meta(fgt->graph); /* limit the lists of allowed formats to the ones selected, to * make sure they stay the same if the filtergraph is reconfigured later */ @@ -1791,7 +1795,7 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) AVFrame *tmp; while (av_fifo_read(ifp->frame_queue, &tmp, 1) >= 0) { if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { - sub2video_frame(&ifp->ifilter, tmp); + sub2video_frame(&ifp->ifilter, tmp, !fgt->graph); } else { ret = av_buffersrc_add_frame(ifp->filter, tmp); } @@ -1814,14 +1818,14 @@ static int configure_filtergraph(FilterGraph *fg, const FilterGraphThread *fgt) if (have_input_eof) { // make sure the EOF propagates to the end of the graph - ret = avfilter_graph_request_oldest(fg->graph); + ret = avfilter_graph_request_oldest(fgt->graph); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) goto fail; } return 0; fail: - cleanup_filtergraph(fg); + cleanup_filtergraph(fg, fgt); return ret; } @@ -1894,17 +1898,18 @@ int filtergraph_is_simple(const FilterGraph *fg) return fgp->is_simple; } -static void send_command(FilterGraph *fg, double time, const char *target, +static void send_command(FilterGraph *fg, AVFilterGraph *graph, + double time, const char *target, const char *command, const char *arg, int all_filters) { int ret; - if (!fg->graph) + if (!graph) return; if (time < 0) { char response[4096]; - ret = avfilter_graph_send_command(fg->graph, target, command, arg, + ret = avfilter_graph_send_command(graph, target, command, arg, response, sizeof(response), all_filters ? 0 : AVFILTER_CMD_FLAG_ONE); fprintf(stderr, "Command reply for stream %d: ret:%d res:\n%s", @@ -1912,7 +1917,7 @@ static void send_command(FilterGraph *fg, double time, const char *target, } else if (!all_filters) { fprintf(stderr, "Queuing commands only on filters supporting the specific command is unsupported\n"); } else { - ret = avfilter_graph_queue_command(fg->graph, target, command, arg, 0, time); + ret = avfilter_graph_queue_command(graph, target, command, arg, 0, time); if (ret < 0) fprintf(stderr, "Queuing command failed with error %s\n", av_err2str(ret)); } @@ -2396,7 +2401,7 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, int did_step = 0; // graph not configured, just select the input to request - if (!fg->graph) { + if (!fgt->graph) { for (int i = 0; i < fg->nb_inputs; i++) { InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); if (ifp->format < 0 && !fgt->eof_in[i]) { @@ -2415,7 +2420,7 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, while (fgp->nb_outputs_done < fg->nb_outputs) { int ret; - ret = avfilter_graph_request_oldest(fg->graph); + ret = avfilter_graph_request_oldest(fgt->graph); if (ret == AVERROR(EAGAIN)) { fgt->next_in = choose_input(fg, fgt); break; @@ -2473,12 +2478,12 @@ static void sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational tb sub2video_push_ref(ifp, pts2); } -static int sub2video_frame(InputFilter *ifilter, AVFrame *frame) +static int sub2video_frame(InputFilter *ifilter, AVFrame *frame, int buffer) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); int ret; - if (!ifilter->graph->graph) { + if (buffer) { AVFrame *tmp; if (!frame) @@ -2593,7 +2598,7 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, break; } - if (!ifp->ist->reinit_filters && fg->graph) + if (!ifp->ist->reinit_filters && fgt->graph) need_reinit = 0; if (!!ifp->hw_frames_ctx != !!frame->hw_frames_ctx || @@ -2614,7 +2619,7 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, } /* (re)init the graph if possible, otherwise buffer the frame and return */ - if (need_reinit || !fg->graph) { + if (need_reinit || !fgt->graph) { AVFrame *tmp = av_frame_alloc(); if (!tmp) @@ -2630,7 +2635,7 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, return ret; } - ret = fg->graph ? read_frames(fg, fgt, tmp) : 0; + ret = fgt->graph ? read_frames(fg, fgt, tmp) : 0; av_frame_free(&tmp); if (ret < 0) return ret; @@ -2691,6 +2696,8 @@ static void fg_thread_uninit(FilterGraphThread *fgt) av_freep(&fgt->eof_in); av_freep(&fgt->eof_out); + avfilter_graph_free(&fgt->graph); + memset(fgt, 0, sizeof(*fgt)); } @@ -2774,7 +2781,7 @@ static void *filter_thread(void *arg) av_assert0(o == FRAME_OPAQUE_SEND_COMMAND && fgt.frame->buf[0]); fc = (FilterCommand*)fgt.frame->buf[0]->data; - send_command(fg, fc->time, fc->target, fc->command, fc->arg, + send_command(fg, fgt.graph, fc->time, fc->target, fc->command, fc->arg, fc->all_filters); av_frame_unref(fgt.frame); continue; @@ -2786,7 +2793,8 @@ static void *filter_thread(void *arg) if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { int hb_frame = input_status >= 0 && o == FRAME_OPAQUE_SUB_HEARTBEAT; - ret = sub2video_frame(ifilter, (fgt.frame->buf[0] || hb_frame) ? fgt.frame : NULL); + ret = sub2video_frame(ifilter, (fgt.frame->buf[0] || hb_frame) ? fgt.frame : NULL, + !fgt.graph); } else if (fgt.frame->buf[0]) { ret = send_frame(fg, &fgt, ifilter, fgt.frame); } else {