[FFmpeg-devel] [PATCH 22/22] avfilter/formats: Avoid allocations when merging channel layouts

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Thu Aug 13 15:44:34 EEST 2020


Nicolas George:
> Andreas Rheinhardt (12020-08-12):
>> When one merges two AVFilterChannelLayouts structs, there is no need to
>> allocate a new one. Instead one can reuse one of the two given ones.
>> If one does this, one also doesn't need to update the references of the
>> AVFilterChannelLayouts that is reused. Therefore this commit reuses the
>> structure with the higher refcount.
>>
>> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
>> ---
>> This can of course be applied independently of 7-21.
>>
>>  libavfilter/formats.c | 48 +++++++++++++++++++------------------------
>>  1 file changed, 21 insertions(+), 27 deletions(-)
>>
>> diff --git a/libavfilter/formats.c b/libavfilter/formats.c
>> index 00d050e439..2d33dd7afe 100644
>> --- a/libavfilter/formats.c
>> +++ b/libavfilter/formats.c
>> @@ -48,13 +48,13 @@ do {                                                                       \
>>      av_freep(&a);                                                          \
>>  } while (0)
>>  
>> -#define MERGE_REF(ret, a, fmts, type, fail)                                \
>> +#define MERGE_REF(ret, a, fmts, type, fail_statement)                      \
>>  do {                                                                       \
>>      type ***tmp;                                                           \
>>                                                                             \
>>      if (!(tmp = av_realloc_array(ret->refs, ret->refcount + a->refcount,   \
>>                                   sizeof(*tmp))))                           \
>> -        goto fail;                                                         \
>> +        { fail_statement }                                                 \
>>      ret->refs = tmp;                                                       \
>>      MERGE_REF_NO_ALLOC(ret, a, fmts);                                      \
>>  } while (0)
>> @@ -157,10 +157,10 @@ AVFilterFormats *ff_merge_samplerates(AVFilterFormats *a,
>>      if (a->nb_formats && b->nb_formats) {
>>          MERGE_FORMATS(ret, a, b, formats, nb_formats, AVFilterFormats, fail);
>>      } else if (a->nb_formats) {
>> -        MERGE_REF(a, b, formats, AVFilterFormats, fail);
>> +        MERGE_REF(a, b, formats, AVFilterFormats, return NULL;);
>>          ret = a;
>>      } else {
>> -        MERGE_REF(b, a, formats, AVFilterFormats, fail);
>> +        MERGE_REF(b, a, formats, AVFilterFormats, return NULL;);
>>          ret = b;
>>      }
>>  
>> @@ -177,7 +177,7 @@ fail:
>>  AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
>>                                                   AVFilterChannelLayouts *b)
>>  {
>> -    AVFilterChannelLayouts *ret = NULL;
>> +    uint64_t *channel_layouts;
>>      unsigned a_all = a->all_layouts + a->all_counts;
>>      unsigned b_all = b->all_layouts + b->all_counts;
>>      int ret_max, ret_nb = 0, i, j, round;
>> @@ -201,15 +201,13 @@ AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
>>                  return NULL;
>>              b->nb_channel_layouts = j;
>>          }
>> -        MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail);
>> +        MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, return NULL;);
>>          return b;
>>      }
>>  
>>      ret_max = a->nb_channel_layouts + b->nb_channel_layouts;
>> -    if (!(ret = av_mallocz(sizeof(*ret))) ||
>> -        !(ret->channel_layouts = av_malloc_array(ret_max,
>> -                                                 sizeof(*ret->channel_layouts))))
>> -        goto fail;
> 
>> +    if (!(channel_layouts = av_malloc_array(ret_max, sizeof(channel_layouts))))
> 
> sizeof(*channel_layouts)

True. Fixed locally.

> 
>> +        return NULL;
>>  
>>      /* a[known] intersect b[known] */
>>      for (i = 0; i < a->nb_channel_layouts; i++) {
>> @@ -217,7 +215,7 @@ AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
>>              continue;
>>          for (j = 0; j < b->nb_channel_layouts; j++) {
>>              if (a->channel_layouts[i] == b->channel_layouts[j]) {
>> -                ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
>> +                channel_layouts[ret_nb++] = a->channel_layouts[i];
>>                  a->channel_layouts[i] = b->channel_layouts[j] = 0;
>>              }
>>          }
>> @@ -232,7 +230,7 @@ AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
>>              bfmt = FF_COUNT2LAYOUT(av_get_channel_layout_nb_channels(fmt));
>>              for (j = 0; j < b->nb_channel_layouts; j++)
>>                  if (b->channel_layouts[j] == bfmt)
>> -                    ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
>> +                    channel_layouts[ret_nb++] = a->channel_layouts[i];
>>          }
>>          /* 1st round: swap to prepare 2nd round; 2nd round: put it back */
>>          FFSWAP(AVFilterChannelLayouts *, a, b);
>> @@ -243,27 +241,23 @@ AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
>>              continue;
>>          for (j = 0; j < b->nb_channel_layouts; j++)
>>              if (a->channel_layouts[i] == b->channel_layouts[j])
>> -                ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
>> +                channel_layouts[ret_nb++] = a->channel_layouts[i];
>>      }
>>  
>> -    ret->nb_channel_layouts = ret_nb;
>> -    if (!ret->nb_channel_layouts)
>> +    if (!ret_nb)
>>          goto fail;
>>  
>> -    ret->refs = av_realloc_array(NULL, a->refcount + b->refcount,
>> -                                 sizeof(*ret->refs));
>> -    if (!ret->refs)
>> -        goto fail;
>> -    MERGE_REF_NO_ALLOC(ret, a, channel_layouts);
>> -    MERGE_REF_NO_ALLOC(ret, b, channel_layouts);
>> -    return ret;
> 
>> +    if (a->refcount > b->refcount)
>> +        FFSWAP(AVFilterChannelLayouts *, a, b);
> 
> I do not think it is just as simple as that: if you swap, then b->refs
> points to the references of a and a->refs points to the references of b.
> Then the MERGE_REF() below will update a->refs, i.e. the references of
> b, which are already valid, and not b->refs, which need to.

No, I just swap the pointers, not the structures themselves. It just
ensures a->refcount <= b->refcount in the following code. (The same
swapping btw already happens at the beginning of the function.)

> 
> Better leave this optimization for later. Especially if we consider
> using a linked list as I suggested.
> 
>> +
>> +    MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, goto fail;);
>> +    av_freep(&b->channel_layouts);
>> +    b->channel_layouts    = channel_layouts;
>> +    b->nb_channel_layouts = ret_nb;
>> +    return b;
>>  
>>  fail:
>> -    if (ret) {
>> -        av_assert1(!ret->refs);
>> -        av_freep(&ret->channel_layouts);
>> -        av_freep(&ret);
>> -    }
>> +    av_free(channel_layouts);
>>      return NULL;
>>  }
> 
> LGTM apart from that.
> 
> Regards,
> 



More information about the ffmpeg-devel mailing list