diff mbox series

[FFmpeg-devel,4/6] avfilter/avfilter: Allow to free non-static pads generically

Message ID AM7PR03MB6660568B4F4BD2E325944A8E8FFE9@AM7PR03MB6660.eurprd03.prod.outlook.com
State Superseded
Headers show
Series [FFmpeg-devel,1/6] avfilter/internal: Replace AVFilterPad.needs_writable by flags | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Andreas Rheinhardt Aug. 17, 2021, 1:53 a.m. UTC
This can be enabled/disabled on a per-filter basis by setting
the new internal flags FF_FILTER_FLAG_FREE_(IN|OUT)PADS and
on a per-pad basis by setting the AVFILTERPAD_FLAG_FREE_NAME flag.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
---
I decided to combine both approaches: It has the advantage that
the marginal extra code for a filter all of whose inputs'/outputs'
names need to be freed is zero while making it easy to handle filters
that have some inputs/outputs whose names need to be freed.

 libavfilter/avfilter.c | 15 ++++++++++++++-
 libavfilter/internal.h | 20 ++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

Comments

Nicolas George Aug. 17, 2021, 7:52 a.m. UTC | #1
Andreas Rheinhardt (12021-08-17):
> This can be enabled/disabled on a per-filter basis by setting
> the new internal flags FF_FILTER_FLAG_FREE_(IN|OUT)PADS and
> on a per-pad basis by setting the AVFILTERPAD_FLAG_FREE_NAME flag.
> 
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
> ---
> I decided to combine both approaches: It has the advantage that
> the marginal extra code for a filter all of whose inputs'/outputs'
> names need to be freed is zero while making it easy to handle filters
> that have some inputs/outputs whose names need to be freed.

It had the drawback that the information about the nature of the pads
ends up at two places of the code that are not close to each other. It
is not good for maintenance and readability.

I think a boolean flag to ff_append_...pad() would do the job much more
elegantly. What do you think about it?

Regards,
Andreas Rheinhardt Aug. 17, 2021, 3:31 p.m. UTC | #2
Nicolas George:
> Andreas Rheinhardt (12021-08-17):
>> This can be enabled/disabled on a per-filter basis by setting
>> the new internal flags FF_FILTER_FLAG_FREE_(IN|OUT)PADS and
>> on a per-pad basis by setting the AVFILTERPAD_FLAG_FREE_NAME flag.
>>
>> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
>> ---
>> I decided to combine both approaches: It has the advantage that
>> the marginal extra code for a filter all of whose inputs'/outputs'
>> names need to be freed is zero while making it easy to handle filters
>> that have some inputs/outputs whose names need to be freed.
> 
> It had the drawback that the information about the nature of the pads
> ends up at two places of the code that are not close to each other. It
> is not good for maintenance and readability.
> 
> I think a boolean flag to ff_append_...pad() would do the job much more
> elegantly. What do you think about it?
> 
Well, this has the problem that it adds code in many places, whereas
static flags for the filter just need code in two places (with zero
marginal cost for making another filter use this feature, whereas a flag
to ff_append_... has a nonzero cost even to users not making use of this
feature). I also like that it uses a static flag for what is essentially
a static property.

- Andreas
Nicolas George Aug. 18, 2021, 8:33 a.m. UTC | #3
Andreas Rheinhardt (12021-08-17):
> Well, this has the problem that it adds code in many places, whereas
> static flags for the filter just need code in two places (with zero
> marginal cost for making another filter use this feature, whereas a flag
> to ff_append_... has a nonzero cost even to users not making use of this
> feature). I also like that it uses a static flag for what is essentially
> a static property.

I do not understand your reasoning. If you have one case that is the
majority by a wide margin, then it makes sense to make it the default
and require the other case to be explicit. But that is not what is
happening here.

A quick count indicate that among filters adding pads dynamically, there
are two using allocated names for one using static names. Two-to-one is
not a huge majority, it is one of the cases where it makes more sense to
write it explicitly on both cases.

Furthermore, it is two-to-one for dynamically allocated, which means the
default should be to free(), and the flags should be
FF_FILTER_FLAG_DO_NOT_FREE_(IN|OUT)PADS, which would make the logic
quite ugly.

And honestly, we are talking adding ", 1" or ", 0" to each
ff_insert_(in|out)pad() call. Just the name of the flag requires eight
times as much characters in the source code.

Regards,
Andreas Rheinhardt Aug. 21, 2021, 7:33 a.m. UTC | #4
Nicolas George:
> Andreas Rheinhardt (12021-08-17):
>> Well, this has the problem that it adds code in many places, whereas
>> static flags for the filter just need code in two places (with zero
>> marginal cost for making another filter use this feature, whereas a flag
>> to ff_append_... has a nonzero cost even to users not making use of this
>> feature). I also like that it uses a static flag for what is essentially
>> a static property.
> 
> I do not understand your reasoning. If you have one case that is the
> majority by a wide margin, then it makes sense to make it the default
> and require the other case to be explicit. But that is not what is
> happening here.
> 
> A quick count indicate that among filters adding pads dynamically, there
> are two using allocated names for one using static names. Two-to-one is
> not a huge majority, it is one of the cases where it makes more sense to
> write it explicitly on both cases.
> 
> Furthermore, it is two-to-one for dynamically allocated, which means the
> default should be to free(), and the flags should be
> FF_FILTER_FLAG_DO_NOT_FREE_(IN|OUT)PADS, which would make the logic
> quite ugly.
> 
> And honestly, we are talking adding ", 1" or ", 0" to each
> ff_insert_(in|out)pad() call. Just the name of the flag requires eight
> times as much characters in the source code.
> 

You are distinguishing between filters with allocated input/output names
and those with static input/output names among those filters with
dynamic inputs/outputs and don't see a wide margin between allocated and
static; I am distinguishing between those filters that have both
allocated and static input (or output) names and those that don't have
this mix (among inputs or among outputs) and I see an overwhelming
majority of the latter, as the former group has only two elements
(actually, one could make the former group empty, by making the inpads
using static names static). So the easiest is to use a static flag for
the common default case and to treat the others by other means (either
completely separately as in the first version of this patch or by
setting the flags manually for them).

- Andreas
Nicolas George Aug. 21, 2021, 9:53 a.m. UTC | #5
Andreas Rheinhardt (12021-08-21):
> You are distinguishing between filters with allocated input/output names
> and those with static input/output names among those filters with
> dynamic inputs/outputs and don't see a wide margin between allocated and
> static; I am distinguishing between those filters that have both
> allocated and static input (or output) names and those that don't have
> this mix (among inputs or among outputs) and I see an overwhelming
> majority of the latter, as the former group has only two elements
> (actually, one could make the former group empty, by making the inpads
> using static names static). So the easiest is to use a static flag for
> the common default case and to treat the others by other means (either
> completely separately as in the first version of this patch or by
> setting the flags manually for them).

I am sorry, but the fact that you need a whole paragraph full of nested
conditions to explain your point makes it rather unconvincing.

You are looking at it from the point of view of the person doing the
refactoring. That means you have the whole issue very much in mind. You
have to consider the points of view of somebody who writes new code and
somebody who reads the existing code or a proposed patch.

For somebody who writes new code, explaining "the third argument is 1 if
the name is dynamically allocated, 0 if not" is much simpler than
explaining flags that set other flags.

For somebody who reviews the code, the fact that the information is at
the same place is a huge benefit. Otherwise, we must stop reading the
code linearly to go check if the flag was properly set.

And in this instance, even the framework is simpler if you just add a
third argument to append_in/out_pad().

If you are not convinced, we can try the experiment with an innocent
bystander: pick somebody neutral, explain your proposal from scratch, I
explain my proposal from scratch, and we see what they think.

Regards,
diff mbox series

Patch

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index c3382036d2..48727201f6 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -123,8 +123,11 @@  static int insert_pad(unsigned *count, AVFilterPad **pads,
         *pads  = newpads;
     if (newlinks)
         *links = newlinks;
-    if (!newpads || !newlinks)
+    if (!newpads || !newlinks) {
+        if (newpad->flags & AVFILTERPAD_FLAG_FREE_NAME)
+            av_freep(&newpad->name);
         return AVERROR(ENOMEM);
+    }
 
     memcpy(*pads + idx, newpad, sizeof(AVFilterPad));
     (*links)[idx] = NULL;
@@ -136,11 +139,17 @@  static int insert_pad(unsigned *count, AVFilterPad **pads,
 
 int ff_insert_inpad(AVFilterContext *f, AVFilterPad *p)
 {
+    if (f->filter->flags_internal & FF_FILTER_FLAG_FREE_INPADS)
+        p->flags |= AVFILTERPAD_FLAG_FREE_NAME;
+
     return insert_pad(&f->nb_inputs, &f->input_pads, &f->inputs, p);
 }
 
 int ff_insert_outpad(AVFilterContext *f, AVFilterPad *p)
 {
+    if (f->filter->flags_internal & FF_FILTER_FLAG_FREE_OUTPADS)
+        p->flags |= AVFILTERPAD_FLAG_FREE_NAME;
+
     return insert_pad(&f->nb_outputs, &f->output_pads, &f->outputs, p);
 }
 
@@ -741,9 +750,13 @@  void avfilter_free(AVFilterContext *filter)
 
     for (i = 0; i < filter->nb_inputs; i++) {
         free_link(filter->inputs[i]);
+        if (filter->input_pads[i].flags  & AVFILTERPAD_FLAG_FREE_NAME)
+            av_freep(&filter->input_pads[i].name);
     }
     for (i = 0; i < filter->nb_outputs; i++) {
         free_link(filter->outputs[i]);
+        if (filter->output_pads[i].flags & AVFILTERPAD_FLAG_FREE_NAME)
+            av_freep(&filter->output_pads[i].name);
     }
 
     if (filter->filter->priv_class)
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 8fe17a5433..0d0335bd1c 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -68,6 +68,11 @@  struct AVFilterPad {
      */
 #define AVFILTERPAD_FLAG_NEEDS_WRITABLE                  (1 << 0)
 
+    /**
+     * The pad's name is allocated and should be freed generically.
+     */
+#define AVFILTERPAD_FLAG_FREE_NAME                       (1 << 1)
+
     /**
      * A combination of AVFILTERPAD_FLAG_* flags.
      */
@@ -227,6 +232,11 @@  void ff_tlog_link(void *ctx, AVFilterLink *link, int end);
 
 /**
  * Append a new input/output pad to the filter's list of such pads.
+ *
+ * If the underlying AVFilter has the FF_FILTER_FLAG_FREE_INPADS
+ * set, the AVFILTERPAD_FLAG_FREE_NAME flag will be set for new inpads,
+ * ensuring that it will be freed generically (even on insertion error).
+ * Similarly for outpads.
  */
 int ff_insert_inpad (AVFilterContext *f, AVFilterPad *p);
 int ff_insert_outpad(AVFilterContext *f, AVFilterPad *p);
@@ -317,6 +327,16 @@  void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter
  */
 #define FF_FILTER_FLAG_HWFRAME_AWARE (1 << 0)
 
+/**
+ * The names of all input pads are allocated and should be freed generically.
+ */
+ #define FF_FILTER_FLAG_FREE_INPADS  (1 << 1)
+
+/**
+ * The names of all output pads are allocated and should be freed generically.
+ */
+ #define FF_FILTER_FLAG_FREE_OUTPADS (1 << 2)
+
 /**
  * Run one round of processing on a filter graph.
  */