2008-02-10 20:02:11 +02:00
|
|
|
/*
|
|
|
|
* Filter layer - format negotiation
|
2010-11-28 12:22:58 +02:00
|
|
|
* Copyright (c) 2007 Bobby Bingham
|
2008-02-10 20:02:11 +02:00
|
|
|
*
|
|
|
|
* This file is part of FFmpeg.
|
|
|
|
*
|
|
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
2013-01-02 17:11:30 +03:00
|
|
|
#include "libavutil/avassert.h"
|
2012-11-13 16:16:48 +03:00
|
|
|
#include "libavutil/channel_layout.h"
|
2012-08-06 16:49:32 +03:00
|
|
|
#include "libavutil/common.h"
|
2011-08-04 12:28:14 +03:00
|
|
|
#include "libavutil/eval.h"
|
2010-01-04 02:08:11 +02:00
|
|
|
#include "libavutil/pixdesc.h"
|
2008-02-10 20:02:11 +02:00
|
|
|
#include "avfilter.h"
|
2011-06-30 10:51:17 +03:00
|
|
|
#include "internal.h"
|
2012-05-06 07:59:06 +03:00
|
|
|
#include "formats.h"
|
2008-02-10 20:02:11 +02:00
|
|
|
|
2013-01-02 17:11:30 +03:00
|
|
|
#define KNOWN(l) (!FF_LAYOUT2COUNT(l)) /* for readability */
|
|
|
|
|
2008-02-22 00:10:11 +02:00
|
|
|
/**
|
|
|
|
* Add all refs from a to ret and destroy a.
|
|
|
|
*/
|
2020-08-13 21:20:22 +02:00
|
|
|
#define MERGE_REF(ret, a, fmts, type, fail_statement) \
|
2012-05-06 07:59:06 +03:00
|
|
|
do { \
|
2020-08-13 21:20:22 +02:00
|
|
|
type ***tmp; \
|
2012-05-06 07:59:06 +03:00
|
|
|
int i; \
|
2020-08-13 21:20:22 +02:00
|
|
|
\
|
|
|
|
if (!(tmp = av_realloc_array(ret->refs, ret->refcount + a->refcount, \
|
|
|
|
sizeof(*tmp)))) \
|
|
|
|
{ fail_statement } \
|
|
|
|
ret->refs = tmp; \
|
|
|
|
\
|
2012-05-06 07:59:06 +03:00
|
|
|
for (i = 0; i < a->refcount; i ++) { \
|
|
|
|
ret->refs[ret->refcount] = a->refs[i]; \
|
|
|
|
*ret->refs[ret->refcount++] = ret; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
av_freep(&a->refs); \
|
|
|
|
av_freep(&a->fmts); \
|
|
|
|
av_freep(&a); \
|
|
|
|
} while (0)
|
2008-02-10 20:04:55 +02:00
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
/**
|
2020-08-13 21:20:22 +02:00
|
|
|
* Add all formats common to a and b to a, add b's refs to a and destroy b.
|
2020-08-14 16:47:01 +02:00
|
|
|
* If check is set, nothing is modified and it is only checked whether
|
|
|
|
* the formats are compatible.
|
2020-08-14 00:18:09 +02:00
|
|
|
* If empty_allowed is set and one of a,b->nb is zero, the lists are
|
|
|
|
* merged; otherwise, it is treated as error.
|
2012-05-06 07:59:06 +03:00
|
|
|
*/
|
2020-08-14 16:47:01 +02:00
|
|
|
#define MERGE_FORMATS(a, b, fmts, nb, type, check, empty_allowed) \
|
2020-08-14 19:21:18 +02:00
|
|
|
do { \
|
2020-08-14 00:18:09 +02:00
|
|
|
int i, j, k = 0, skip = 0; \
|
2020-08-14 19:21:18 +02:00
|
|
|
\
|
2020-08-14 00:18:09 +02:00
|
|
|
if (empty_allowed) { \
|
|
|
|
if (!a->nb || !b->nb) { \
|
2020-08-14 16:47:01 +02:00
|
|
|
if (check) \
|
|
|
|
return 1; \
|
2020-08-14 00:18:09 +02:00
|
|
|
if (!a->nb) \
|
|
|
|
FFSWAP(type *, a, b); \
|
|
|
|
skip = 1; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
if (!skip) { \
|
2020-08-14 19:21:18 +02:00
|
|
|
for (i = 0; i < a->nb; i++) \
|
|
|
|
for (j = 0; j < b->nb; j++) \
|
|
|
|
if (a->fmts[i] == b->fmts[j]) { \
|
2020-08-14 16:47:01 +02:00
|
|
|
if (check) \
|
|
|
|
return 1; \
|
2020-08-13 21:20:22 +02:00
|
|
|
a->fmts[k++] = a->fmts[i]; \
|
2020-08-14 19:21:18 +02:00
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
/* Check that there was at least one common format. \
|
|
|
|
* Notice that both a and b are unchanged if not. */ \
|
|
|
|
if (!k) \
|
|
|
|
return 0; \
|
|
|
|
av_assert2(!check); \
|
|
|
|
a->nb = k; \
|
2020-08-14 00:18:09 +02:00
|
|
|
} \
|
|
|
|
\
|
2020-08-14 16:47:01 +02:00
|
|
|
MERGE_REF(a, b, fmts, type, return AVERROR(ENOMEM);); \
|
2012-05-06 07:59:06 +03:00
|
|
|
} while (0)
|
2008-02-10 20:04:52 +02:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
|
|
|
|
enum AVMediaType type, int check)
|
2008-02-10 20:02:11 +02:00
|
|
|
{
|
2013-02-19 17:00:01 +03:00
|
|
|
int i, j;
|
|
|
|
int alpha1=0, alpha2=0;
|
|
|
|
int chroma1=0, chroma2=0;
|
2008-02-10 20:02:11 +02:00
|
|
|
|
2011-06-08 19:24:25 +03:00
|
|
|
if (a == b)
|
2020-08-14 16:47:01 +02:00
|
|
|
return 1;
|
2011-06-08 19:24:25 +03:00
|
|
|
|
2013-02-21 16:02:13 +03:00
|
|
|
/* Do not lose chroma or alpha in merging.
|
|
|
|
It happens if both lists have formats with chroma (resp. alpha), but
|
|
|
|
the only formats in common do not have it (e.g. YUV+gray vs.
|
|
|
|
RGB+gray): in that case, the merging would select the gray format,
|
|
|
|
possibly causing a lossy conversion elsewhere in the graph.
|
|
|
|
To avoid that, pretend that there are no common formats to force the
|
|
|
|
insertion of a conversion filter. */
|
|
|
|
if (type == AVMEDIA_TYPE_VIDEO)
|
2013-05-18 12:40:38 +03:00
|
|
|
for (i = 0; i < a->nb_formats; i++)
|
|
|
|
for (j = 0; j < b->nb_formats; j++) {
|
2013-02-24 14:05:06 +03:00
|
|
|
const AVPixFmtDescriptor *adesc = av_pix_fmt_desc_get(a->formats[i]);
|
|
|
|
const AVPixFmtDescriptor *bdesc = av_pix_fmt_desc_get(b->formats[j]);
|
2013-05-15 12:23:14 +03:00
|
|
|
alpha2 |= adesc->flags & bdesc->flags & AV_PIX_FMT_FLAG_ALPHA;
|
2013-02-24 14:05:06 +03:00
|
|
|
chroma2|= adesc->nb_components > 1 && bdesc->nb_components > 1;
|
|
|
|
if (a->formats[i] == b->formats[j]) {
|
2013-05-15 12:23:14 +03:00
|
|
|
alpha1 |= adesc->flags & AV_PIX_FMT_FLAG_ALPHA;
|
2013-02-24 14:05:06 +03:00
|
|
|
chroma1|= adesc->nb_components > 1;
|
|
|
|
}
|
2013-02-19 17:00:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If chroma or alpha can be lost through merging then do not merge
|
|
|
|
if (alpha2 > alpha1 || chroma2 > chroma1)
|
2020-08-14 16:47:01 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 0);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_can_merge_formats(const AVFilterFormats *a, const AVFilterFormats *b,
|
|
|
|
enum AVMediaType type)
|
|
|
|
{
|
|
|
|
return merge_formats_internal((AVFilterFormats *)a,
|
|
|
|
(AVFilterFormats *)b, type, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
|
|
|
|
enum AVMediaType type)
|
|
|
|
{
|
|
|
|
av_assert2(a->refcount && b->refcount);
|
|
|
|
return merge_formats_internal(a, b, type, 0);
|
|
|
|
}
|
2013-02-19 17:00:01 +03:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
static int merge_samplerates_internal(AVFilterFormats *a,
|
|
|
|
AVFilterFormats *b, int check)
|
|
|
|
{
|
|
|
|
if (a == b) return 1;
|
2008-02-10 20:02:11 +02:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 1);
|
|
|
|
return 1;
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
int ff_can_merge_samplerates(const AVFilterFormats *a, const AVFilterFormats *b)
|
2012-05-06 07:59:06 +03:00
|
|
|
{
|
2020-08-14 16:47:01 +02:00
|
|
|
return merge_samplerates_internal((AVFilterFormats *)a, (AVFilterFormats *)b, 1);
|
|
|
|
}
|
2008-02-10 20:04:52 +02:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
int ff_merge_samplerates(AVFilterFormats *a, AVFilterFormats *b)
|
|
|
|
{
|
|
|
|
av_assert2(a->refcount && b->refcount);
|
|
|
|
return merge_samplerates_internal(a, b, 0);
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
2008-02-10 20:04:52 +02:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
|
|
|
|
AVFilterChannelLayouts *b)
|
2012-05-06 07:59:06 +03:00
|
|
|
{
|
2020-08-12 22:31:50 +02:00
|
|
|
uint64_t *channel_layouts;
|
2013-01-02 17:11:30 +03:00
|
|
|
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;
|
2012-05-06 07:59:06 +03:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
av_assert2(a->refcount && b->refcount);
|
|
|
|
|
|
|
|
if (a == b) return 1;
|
2012-05-06 07:59:06 +03:00
|
|
|
|
2013-01-02 17:11:30 +03:00
|
|
|
/* Put the most generic set in a, to avoid doing everything twice */
|
|
|
|
if (a_all < b_all) {
|
|
|
|
FFSWAP(AVFilterChannelLayouts *, a, b);
|
|
|
|
FFSWAP(unsigned, a_all, b_all);
|
|
|
|
}
|
|
|
|
if (a_all) {
|
|
|
|
if (a_all == 1 && !b_all) {
|
|
|
|
/* keep only known layouts in b; works also for b_all = 1 */
|
|
|
|
for (i = j = 0; i < b->nb_channel_layouts; i++)
|
|
|
|
if (KNOWN(b->channel_layouts[i]))
|
|
|
|
b->channel_layouts[j++] = b->channel_layouts[i];
|
2013-03-14 23:54:48 +03:00
|
|
|
/* Not optimal: the unknown layouts of b may become known after
|
|
|
|
another merge. */
|
|
|
|
if (!j)
|
2020-08-14 16:47:01 +02:00
|
|
|
return 0;
|
2013-01-02 17:11:30 +03:00
|
|
|
b->nb_channel_layouts = j;
|
|
|
|
}
|
2020-08-14 16:47:01 +02:00
|
|
|
MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, return AVERROR(ENOMEM););
|
|
|
|
return 1;
|
2013-01-02 17:11:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ret_max = a->nb_channel_layouts + b->nb_channel_layouts;
|
2020-08-12 22:31:50 +02:00
|
|
|
if (!(channel_layouts = av_malloc_array(ret_max, sizeof(*channel_layouts))))
|
2020-08-14 16:47:01 +02:00
|
|
|
return AVERROR(ENOMEM);
|
2013-01-02 17:11:30 +03:00
|
|
|
|
|
|
|
/* a[known] intersect b[known] */
|
|
|
|
for (i = 0; i < a->nb_channel_layouts; i++) {
|
|
|
|
if (!KNOWN(a->channel_layouts[i]))
|
|
|
|
continue;
|
|
|
|
for (j = 0; j < b->nb_channel_layouts; j++) {
|
|
|
|
if (a->channel_layouts[i] == b->channel_layouts[j]) {
|
2020-08-12 22:31:50 +02:00
|
|
|
channel_layouts[ret_nb++] = a->channel_layouts[i];
|
2013-01-02 17:11:30 +03:00
|
|
|
a->channel_layouts[i] = b->channel_layouts[j] = 0;
|
avfilter/formats: Fix heap-buffer overflow when merging channel layouts
The channel layouts accepted by ff_merge_channel_layouts() are of two
types: Ordinary channel layouts and generic channel layouts. These are
layouts that match all layouts with a certain number of channels.
Therefore parsing these channel layouts is not done in one go; instead
first the intersection of the ordinary layouts of the first input
list of channel layouts with the ordinary layouts of the second list is
determined, then the intersection of the ordinary layouts of the first
one and the generic layouts of the second one etc. In order to mark the
ordinary channel layouts that have already been matched as used they are
zeroed. The inner loop that does this is as follows:
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];
a->channel_layouts[i] = b->channel_layouts[j] = 0;
}
}
(Here ret->channel_layouts is the array containing the intersection of
the two input arrays.)
Yet the problem with this code is that after a match has been found, the
loop continues the search with the new value a->channel_layouts[i].
The intention of zeroing these elements was to make sure that elements
already paired at this stage are ignored later. And while they are indeed
ignored when pairing ordinary and generic channel layouts later, it has
the exact opposite effect when pairing ordinary channel layouts.
To see this consider the channel layouts A B C D E and E D C B A. In the
first round, A and A will be paired and added to ret->channel_layouts.
In the second round, the input arrays are 0 B C D E and E D C B 0.
At first B and B will be matched and zeroed, but after doing so matching
continues, but this time it will search for 0, which will match with the
last entry of the second array. ret->channel_layouts now contains A B 0.
In the third round, C 0 0 will be added to ret->channel_layouts etc.
This gives a quadratic amount of elements, yet the amount of elements
allocated for said array is only the sum of the sizes of a and b.
This issue can e.g. be reproduced by
ffmpeg -f lavfi -i anullsrc=cl=7.1 \
-af 'aformat=cl=mono|stereo|2.1|3.0|4.0,aformat=cl=4.0|3.0|2.1|stereo|mono' \
-f null -
The fix is easy: break out of the inner loop after having found a match.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-13 04:02:26 +02:00
|
|
|
break;
|
2013-01-02 17:11:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* 1st round: a[known] intersect b[generic]
|
|
|
|
2nd round: a[generic] intersect b[known] */
|
|
|
|
for (round = 0; round < 2; round++) {
|
|
|
|
for (i = 0; i < a->nb_channel_layouts; i++) {
|
|
|
|
uint64_t fmt = a->channel_layouts[i], bfmt;
|
|
|
|
if (!fmt || !KNOWN(fmt))
|
|
|
|
continue;
|
|
|
|
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)
|
2020-08-12 22:31:50 +02:00
|
|
|
channel_layouts[ret_nb++] = a->channel_layouts[i];
|
2013-01-02 17:11:30 +03:00
|
|
|
}
|
|
|
|
/* 1st round: swap to prepare 2nd round; 2nd round: put it back */
|
|
|
|
FFSWAP(AVFilterChannelLayouts *, a, b);
|
|
|
|
}
|
|
|
|
/* a[generic] intersect b[generic] */
|
|
|
|
for (i = 0; i < a->nb_channel_layouts; i++) {
|
|
|
|
if (KNOWN(a->channel_layouts[i]))
|
|
|
|
continue;
|
|
|
|
for (j = 0; j < b->nb_channel_layouts; j++)
|
|
|
|
if (a->channel_layouts[i] == b->channel_layouts[j])
|
2020-08-12 22:31:50 +02:00
|
|
|
channel_layouts[ret_nb++] = a->channel_layouts[i];
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
2008-02-10 20:02:11 +02:00
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
if (!ret_nb) {
|
|
|
|
av_free(channel_layouts);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-08-07 03:41:18 +02:00
|
|
|
|
2020-08-12 22:31:50 +02:00
|
|
|
if (a->refcount > b->refcount)
|
|
|
|
FFSWAP(AVFilterChannelLayouts *, a, b);
|
|
|
|
|
2020-08-14 16:47:01 +02:00
|
|
|
MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts,
|
|
|
|
{ av_free(channel_layouts); return AVERROR(ENOMEM); });
|
2020-08-12 22:31:50 +02:00
|
|
|
av_freep(&b->channel_layouts);
|
|
|
|
b->channel_layouts = channel_layouts;
|
|
|
|
b->nb_channel_layouts = ret_nb;
|
2020-08-14 16:47:01 +02:00
|
|
|
return 1;
|
2008-02-10 20:02:11 +02:00
|
|
|
}
|
|
|
|
|
2011-06-30 10:51:17 +03:00
|
|
|
int ff_fmt_is_in(int fmt, const int *fmts)
|
|
|
|
{
|
|
|
|
const int *p;
|
|
|
|
|
|
|
|
for (p = fmts; *p != -1; p++) {
|
|
|
|
if (fmt == *p)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-18 13:43:31 +03:00
|
|
|
#define MAKE_FORMAT_LIST(type, field, count_field) \
|
|
|
|
type *formats; \
|
2011-06-07 21:17:23 +03:00
|
|
|
int count = 0; \
|
|
|
|
if (fmts) \
|
|
|
|
for (count = 0; fmts[count] != -1; count++) \
|
|
|
|
; \
|
2012-05-18 13:43:31 +03:00
|
|
|
formats = av_mallocz(sizeof(*formats)); \
|
2015-03-12 23:39:27 +02:00
|
|
|
if (!formats) \
|
|
|
|
return NULL; \
|
2012-05-18 13:43:31 +03:00
|
|
|
formats->count_field = count; \
|
2011-06-07 21:17:23 +03:00
|
|
|
if (count) { \
|
2014-06-14 00:15:16 +03:00
|
|
|
formats->field = av_malloc_array(count, sizeof(*formats->field)); \
|
2012-05-18 13:43:31 +03:00
|
|
|
if (!formats->field) { \
|
2015-03-12 23:39:27 +02:00
|
|
|
av_freep(&formats); \
|
2011-06-07 21:17:23 +03:00
|
|
|
return NULL; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2012-05-30 11:12:55 +03:00
|
|
|
AVFilterFormats *ff_make_format_list(const int *fmts)
|
2009-10-19 02:06:52 +03:00
|
|
|
{
|
2013-05-18 12:40:38 +03:00
|
|
|
MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats);
|
2011-06-07 21:17:23 +03:00
|
|
|
while (count--)
|
|
|
|
formats->formats[count] = fmts[count];
|
2009-10-19 02:06:52 +03:00
|
|
|
|
2011-06-07 21:17:23 +03:00
|
|
|
return formats;
|
|
|
|
}
|
2015-12-09 14:40:03 +02:00
|
|
|
|
2020-08-08 00:37:46 +02:00
|
|
|
AVFilterChannelLayouts *ff_make_format64_list(const int64_t *fmts)
|
2011-06-07 21:17:23 +03:00
|
|
|
{
|
2012-05-18 13:43:31 +03:00
|
|
|
MAKE_FORMAT_LIST(AVFilterChannelLayouts,
|
|
|
|
channel_layouts, nb_channel_layouts);
|
2011-06-07 21:17:23 +03:00
|
|
|
if (count)
|
2012-05-18 13:43:31 +03:00
|
|
|
memcpy(formats->channel_layouts, fmts,
|
|
|
|
sizeof(*formats->channel_layouts) * count);
|
2009-10-19 02:06:52 +03:00
|
|
|
|
2012-05-18 13:43:31 +03:00
|
|
|
return formats;
|
2009-10-19 02:06:52 +03:00
|
|
|
}
|
|
|
|
|
2020-08-08 00:37:46 +02:00
|
|
|
#if LIBAVFILTER_VERSION_MAJOR < 8
|
|
|
|
AVFilterChannelLayouts *avfilter_make_format64_list(const int64_t *fmts)
|
|
|
|
{
|
|
|
|
return ff_make_format64_list(fmts);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-10-05 05:39:25 +02:00
|
|
|
#define ADD_FORMAT(f, fmt, unref_fn, type, list, nb) \
|
2012-05-06 07:59:06 +03:00
|
|
|
do { \
|
|
|
|
type *fmts; \
|
|
|
|
\
|
2015-10-05 05:39:25 +02:00
|
|
|
if (!(*f) && !(*f = av_mallocz(sizeof(**f)))) { \
|
2012-05-06 07:59:06 +03:00
|
|
|
return AVERROR(ENOMEM); \
|
2015-10-05 05:39:25 +02:00
|
|
|
} \
|
2012-05-06 07:59:06 +03:00
|
|
|
\
|
2015-03-14 22:17:27 +02:00
|
|
|
fmts = av_realloc_array((*f)->list, (*f)->nb + 1, \
|
|
|
|
sizeof(*(*f)->list)); \
|
2014-11-17 01:22:20 +02:00
|
|
|
if (!fmts) { \
|
2015-10-05 05:39:25 +02:00
|
|
|
unref_fn(f); \
|
2012-05-06 07:59:06 +03:00
|
|
|
return AVERROR(ENOMEM); \
|
2014-11-17 01:22:20 +02:00
|
|
|
} \
|
2012-05-06 07:59:06 +03:00
|
|
|
\
|
|
|
|
(*f)->list = fmts; \
|
|
|
|
(*f)->list[(*f)->nb++] = fmt; \
|
|
|
|
} while (0)
|
|
|
|
|
2012-06-05 23:43:44 +03:00
|
|
|
int ff_add_format(AVFilterFormats **avff, int64_t fmt)
|
2010-01-04 00:26:59 +02:00
|
|
|
{
|
2015-10-05 05:39:25 +02:00
|
|
|
ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
|
2013-01-02 17:11:30 +03:00
|
|
|
return 0;
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
2010-01-04 00:26:59 +02:00
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
|
|
|
|
{
|
2013-01-02 17:11:30 +03:00
|
|
|
av_assert1(!(*l && (*l)->all_layouts));
|
2015-10-05 05:39:25 +02:00
|
|
|
ADD_FORMAT(l, channel_layout, ff_channel_layouts_unref, uint64_t, channel_layouts, nb_channel_layouts);
|
2013-01-02 17:11:30 +03:00
|
|
|
return 0;
|
2010-01-04 00:26:59 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 11:12:55 +03:00
|
|
|
AVFilterFormats *ff_all_formats(enum AVMediaType type)
|
2008-02-10 20:02:11 +02:00
|
|
|
{
|
2010-01-06 18:19:13 +02:00
|
|
|
AVFilterFormats *ret = NULL;
|
2008-02-10 20:04:49 +02:00
|
|
|
|
2014-05-24 12:15:15 +03:00
|
|
|
if (type == AVMEDIA_TYPE_VIDEO) {
|
|
|
|
const AVPixFmtDescriptor *desc = NULL;
|
|
|
|
while ((desc = av_pix_fmt_desc_next(desc))) {
|
2015-10-05 05:39:25 +02:00
|
|
|
if (ff_add_format(&ret, av_pix_fmt_desc_get_id(desc)) < 0)
|
|
|
|
return NULL;
|
2014-05-24 12:15:15 +03:00
|
|
|
}
|
|
|
|
} else if (type == AVMEDIA_TYPE_AUDIO) {
|
|
|
|
enum AVSampleFormat fmt = 0;
|
|
|
|
while (av_get_sample_fmt_name(fmt)) {
|
2015-10-05 05:39:25 +02:00
|
|
|
if (ff_add_format(&ret, fmt) < 0)
|
|
|
|
return NULL;
|
2014-05-24 12:15:15 +03:00
|
|
|
fmt++;
|
|
|
|
}
|
2012-10-06 14:29:37 +03:00
|
|
|
}
|
2008-02-10 20:04:49 +02:00
|
|
|
|
|
|
|
return ret;
|
2008-02-10 20:02:11 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 16:40:40 +02:00
|
|
|
int ff_formats_pixdesc_filter(AVFilterFormats **rfmts, unsigned want, unsigned rej)
|
|
|
|
{
|
|
|
|
unsigned nb_formats, fmt, flags;
|
|
|
|
AVFilterFormats *formats = NULL;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
nb_formats = 0;
|
|
|
|
for (fmt = 0;; fmt++) {
|
|
|
|
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
|
|
|
|
if (!desc)
|
|
|
|
break;
|
|
|
|
flags = desc->flags;
|
|
|
|
if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL) &&
|
|
|
|
!(desc->flags & AV_PIX_FMT_FLAG_PLANAR) &&
|
|
|
|
(desc->log2_chroma_w || desc->log2_chroma_h))
|
|
|
|
flags |= FF_PIX_FMT_FLAG_SW_FLAT_SUB;
|
|
|
|
if ((flags & (want | rej)) != want)
|
|
|
|
continue;
|
|
|
|
if (formats)
|
|
|
|
formats->formats[nb_formats] = fmt;
|
|
|
|
nb_formats++;
|
|
|
|
}
|
|
|
|
if (formats) {
|
|
|
|
av_assert0(formats->nb_formats == nb_formats);
|
|
|
|
*rfmts = formats;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
formats = av_mallocz(sizeof(*formats));
|
|
|
|
if (!formats)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
formats->nb_formats = nb_formats;
|
|
|
|
if (nb_formats) {
|
|
|
|
formats->formats = av_malloc_array(nb_formats, sizeof(*formats->formats));
|
|
|
|
if (!formats->formats) {
|
|
|
|
av_freep(&formats);
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-30 14:59:30 +03:00
|
|
|
AVFilterFormats *ff_planar_sample_fmts(void)
|
|
|
|
{
|
|
|
|
AVFilterFormats *ret = NULL;
|
|
|
|
int fmt;
|
|
|
|
|
2014-05-27 19:22:09 +03:00
|
|
|
for (fmt = 0; av_get_bytes_per_sample(fmt)>0; fmt++)
|
2012-05-30 14:59:30 +03:00
|
|
|
if (av_sample_fmt_is_planar(fmt))
|
2015-10-05 05:39:25 +02:00
|
|
|
if (ff_add_format(&ret, fmt) < 0)
|
|
|
|
return NULL;
|
2012-05-30 14:59:30 +03:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
AVFilterFormats *ff_all_samplerates(void)
|
2008-02-10 20:02:11 +02:00
|
|
|
{
|
2012-05-06 07:59:06 +03:00
|
|
|
AVFilterFormats *ret = av_mallocz(sizeof(*ret));
|
|
|
|
return ret;
|
2008-02-10 20:02:11 +02:00
|
|
|
}
|
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
AVFilterChannelLayouts *ff_all_channel_layouts(void)
|
2008-02-10 20:03:18 +02:00
|
|
|
{
|
2012-05-06 07:59:06 +03:00
|
|
|
AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret));
|
2013-01-02 17:11:30 +03:00
|
|
|
if (!ret)
|
|
|
|
return NULL;
|
|
|
|
ret->all_layouts = 1;
|
2012-05-06 07:59:06 +03:00
|
|
|
return ret;
|
2008-02-10 20:03:18 +02:00
|
|
|
}
|
|
|
|
|
2012-12-26 18:46:43 +03:00
|
|
|
AVFilterChannelLayouts *ff_all_channel_counts(void)
|
|
|
|
{
|
|
|
|
AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret));
|
|
|
|
if (!ret)
|
|
|
|
return NULL;
|
|
|
|
ret->all_layouts = ret->all_counts = 1;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-10-05 05:39:25 +02:00
|
|
|
#define FORMATS_REF(f, ref, unref_fn) \
|
2015-03-15 14:24:22 +02:00
|
|
|
void *tmp; \
|
|
|
|
\
|
2015-10-05 05:39:25 +02:00
|
|
|
if (!f || !ref) \
|
|
|
|
return AVERROR(ENOMEM); \
|
2015-03-15 14:24:22 +02:00
|
|
|
\
|
|
|
|
tmp = av_realloc_array(f->refs, sizeof(*f->refs), f->refcount + 1); \
|
2015-10-05 05:39:25 +02:00
|
|
|
if (!tmp) { \
|
|
|
|
unref_fn(&f); \
|
2015-03-14 22:21:49 +02:00
|
|
|
return AVERROR(ENOMEM); \
|
2015-10-05 05:39:25 +02:00
|
|
|
} \
|
2015-03-14 22:21:49 +02:00
|
|
|
f->refs = tmp; \
|
|
|
|
f->refs[f->refcount++] = ref; \
|
|
|
|
*ref = f; \
|
|
|
|
return 0
|
|
|
|
|
|
|
|
int ff_channel_layouts_ref(AVFilterChannelLayouts *f, AVFilterChannelLayouts **ref)
|
2008-02-10 20:02:11 +02:00
|
|
|
{
|
2015-10-05 05:39:25 +02:00
|
|
|
FORMATS_REF(f, ref, ff_channel_layouts_unref);
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
2010-01-08 10:19:18 +02:00
|
|
|
|
2015-03-14 22:21:49 +02:00
|
|
|
int ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
|
2008-02-10 20:02:11 +02:00
|
|
|
{
|
2015-10-05 05:39:25 +02:00
|
|
|
FORMATS_REF(f, ref, ff_formats_unref);
|
2012-05-06 07:59:06 +03:00
|
|
|
}
|
2010-01-08 10:19:18 +02:00
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
#define FIND_REF_INDEX(ref, idx) \
|
|
|
|
do { \
|
|
|
|
int i; \
|
|
|
|
for (i = 0; i < (*ref)->refcount; i ++) \
|
|
|
|
if((*ref)->refs[i] == ref) { \
|
|
|
|
idx = i; \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define FORMATS_UNREF(ref, list) \
|
|
|
|
do { \
|
|
|
|
int idx = -1; \
|
|
|
|
\
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
if (!ref || !*ref) \
|
2012-05-06 07:59:06 +03:00
|
|
|
return; \
|
|
|
|
\
|
|
|
|
FIND_REF_INDEX(ref, idx); \
|
|
|
|
\
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
if (idx >= 0) { \
|
2012-05-06 07:59:06 +03:00
|
|
|
memmove((*ref)->refs + idx, (*ref)->refs + idx + 1, \
|
|
|
|
sizeof(*(*ref)->refs) * ((*ref)->refcount - idx - 1)); \
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
--(*ref)->refcount; \
|
|
|
|
} \
|
|
|
|
if (!(*ref)->refcount) { \
|
2012-05-06 07:59:06 +03:00
|
|
|
av_free((*ref)->list); \
|
|
|
|
av_free((*ref)->refs); \
|
|
|
|
av_free(*ref); \
|
|
|
|
} \
|
|
|
|
*ref = NULL; \
|
|
|
|
} while (0)
|
2010-01-08 00:59:22 +02:00
|
|
|
|
2012-05-30 11:12:55 +03:00
|
|
|
void ff_formats_unref(AVFilterFormats **ref)
|
2012-05-06 07:59:06 +03:00
|
|
|
{
|
|
|
|
FORMATS_UNREF(ref, formats);
|
|
|
|
}
|
2008-02-10 20:03:18 +02:00
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
void ff_channel_layouts_unref(AVFilterChannelLayouts **ref)
|
|
|
|
{
|
|
|
|
FORMATS_UNREF(ref, channel_layouts);
|
|
|
|
}
|
2008-02-10 20:03:18 +02:00
|
|
|
|
2012-05-06 07:59:06 +03:00
|
|
|
#define FORMATS_CHANGEREF(oldref, newref) \
|
|
|
|
do { \
|
|
|
|
int idx = -1; \
|
|
|
|
\
|
|
|
|
FIND_REF_INDEX(oldref, idx); \
|
|
|
|
\
|
|
|
|
if (idx >= 0) { \
|
|
|
|
(*oldref)->refs[idx] = newref; \
|
|
|
|
*newref = *oldref; \
|
|
|
|
*oldref = NULL; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
void ff_channel_layouts_changeref(AVFilterChannelLayouts **oldref,
|
|
|
|
AVFilterChannelLayouts **newref)
|
|
|
|
{
|
|
|
|
FORMATS_CHANGEREF(oldref, newref);
|
2008-02-10 20:02:11 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 11:12:55 +03:00
|
|
|
void ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref)
|
2008-02-10 20:04:32 +02:00
|
|
|
{
|
2012-05-06 07:59:06 +03:00
|
|
|
FORMATS_CHANGEREF(oldref, newref);
|
2008-02-10 20:04:32 +02:00
|
|
|
}
|
2012-05-10 08:41:16 +03:00
|
|
|
|
2020-08-13 11:59:19 +02:00
|
|
|
#define SET_COMMON_FORMATS(ctx, fmts, ref_fn, unref_fn) \
|
2012-05-10 08:41:16 +03:00
|
|
|
int count = 0, i; \
|
|
|
|
\
|
2015-03-15 14:24:22 +02:00
|
|
|
if (!fmts) \
|
2015-10-05 05:39:25 +02:00
|
|
|
return AVERROR(ENOMEM); \
|
2015-03-15 14:24:22 +02:00
|
|
|
\
|
2012-06-12 22:25:10 +03:00
|
|
|
for (i = 0; i < ctx->nb_inputs; i++) { \
|
2020-08-13 11:59:19 +02:00
|
|
|
if (ctx->inputs[i] && !ctx->inputs[i]->outcfg.fmts) { \
|
|
|
|
int ret = ref_fn(fmts, &ctx->inputs[i]->outcfg.fmts); \
|
2015-10-05 05:39:25 +02:00
|
|
|
if (ret < 0) { \
|
2015-03-15 14:24:22 +02:00
|
|
|
return ret; \
|
2015-10-05 05:39:25 +02:00
|
|
|
} \
|
2012-05-10 08:41:16 +03:00
|
|
|
count++; \
|
|
|
|
} \
|
|
|
|
} \
|
2012-06-12 22:25:10 +03:00
|
|
|
for (i = 0; i < ctx->nb_outputs; i++) { \
|
2020-08-13 11:59:19 +02:00
|
|
|
if (ctx->outputs[i] && !ctx->outputs[i]->incfg.fmts) { \
|
|
|
|
int ret = ref_fn(fmts, &ctx->outputs[i]->incfg.fmts); \
|
2015-10-05 05:39:25 +02:00
|
|
|
if (ret < 0) { \
|
2015-03-15 14:24:22 +02:00
|
|
|
return ret; \
|
2015-10-05 05:39:25 +02:00
|
|
|
} \
|
2012-05-10 08:41:16 +03:00
|
|
|
count++; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
if (!count) { \
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
unref_fn(&fmts); \
|
2012-05-10 08:41:16 +03:00
|
|
|
} \
|
2015-03-15 14:24:22 +02:00
|
|
|
\
|
|
|
|
return 0;
|
2012-05-10 08:41:16 +03:00
|
|
|
|
2015-03-15 14:24:22 +02:00
|
|
|
int ff_set_common_channel_layouts(AVFilterContext *ctx,
|
2020-08-13 11:59:19 +02:00
|
|
|
AVFilterChannelLayouts *channel_layouts)
|
2012-05-10 08:41:16 +03:00
|
|
|
{
|
2020-08-13 11:59:19 +02:00
|
|
|
SET_COMMON_FORMATS(ctx, channel_layouts,
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
ff_channel_layouts_ref, ff_channel_layouts_unref);
|
2012-05-10 08:41:16 +03:00
|
|
|
}
|
|
|
|
|
2015-03-15 14:24:22 +02:00
|
|
|
int ff_set_common_samplerates(AVFilterContext *ctx,
|
|
|
|
AVFilterFormats *samplerates)
|
2012-05-10 08:41:16 +03:00
|
|
|
{
|
2020-08-13 11:59:19 +02:00
|
|
|
SET_COMMON_FORMATS(ctx, samplerates,
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
ff_formats_ref, ff_formats_unref);
|
2012-05-10 08:41:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A helper for query_formats() which sets all links to the same list of
|
|
|
|
* formats. If there are no links hooked to this filter, the list of formats is
|
|
|
|
* freed.
|
|
|
|
*/
|
2015-03-15 14:24:22 +02:00
|
|
|
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
|
2012-05-10 08:41:16 +03:00
|
|
|
{
|
2020-08-13 11:59:19 +02:00
|
|
|
SET_COMMON_FORMATS(ctx, formats,
|
avfilter/formats: Fix double frees and memleaks on error
The formats API deals with lists of channel layouts, sample rates,
pixel formats and sample formats. These lists are refcounted in a way in
which the list structure itself contains pointers to all of its owners.
Furthermore, it is possible for a list to be not owned by anyone yet;
this status is temporary until the list has been attached to an owner.
Adding an owner to a list involves reallocating the list's list of
owners and can therefore fail.
In order to reduce the amount of checks and cleanup code for the users
of this API, the API is supposed to be lenient when faced with input
lists that are NULL and it is supposed to clean up if adding an owner
to a list fails, so that a simple use case like
list = ff_make_format_list(foo_fmts);
if ((ret = ff_formats_ref(list, &ctx->inputs[0]->out_formats)) < 0)
return ret;
needn't check whether list could be successfully allocated
(ff_formats_ref() return AVERROR(ENOMEM) if it couldn't) and it also
needn't free list if ff_formats_ref() couldn't add an owner for it.
But the cleaning up after itself was broken. The root cause was that
the refcount was decremented during unreferencing whether or not the
element to be unreferenced was actually an owner of the list or not.
This means that if the above sample code is continued by
if ((ret = ff_formats_ref(list, &ctx->inputs[1]->out_formats)) < 0)
return ret;
and that if an error happens at the second ff_formats_ref() call, the
automatic cleaning of list will decrement the refcount from 1 (the sole
owner of list at this moment is ctx->input[0]->out_formats) to 0 and so
the list will be freed; yet ctx->input[0]->out_formats still points to
the list and this will lead to a double free/use-after-free when
ctx->input[0] is freed later.
Presumably in order to work around such an issue, commit
93afb338a405eac0f9e7b092bc26603378bfcca6 restricted unreferencing to
lists with owners. This does not solve the root cause (the above example
is not fixed by this) at all, but it solves some crashs.
This commit fixes the API: The list's refcount is only decremented if
an owner is removed from the list of owners and not if the
unref-function is called with a pointer that is not among the owners of
the list. Furtermore, the requirement for the list to have owners is
dropped.
This implies that if the first call to ff_formats_ref() in the above
example fails, the refcount which is initially zero during unreferencing
is not modified, so that the list will be freed automatically in said
call to ff_formats_ref() as every list whose refcount reaches zero is.
If on the other hand, the second call to ff_formats_ref() is the first
to fail, the refcount would stay at one during the automatic
unreferencing in ff_formats_ref(). The list would later be freed when
its last (and in this case sole) owner (namely
ctx->inputs[0]->out_formats) gets unreferenced.
The issues described here for ff_formats_ref() also affected the other
functions of this API. E.g. ff_add_format() failed to clean up after
itself if adding an entry to an already existing list failed (the case
of a freshly allocated list was handled specially and this commit also
removes said code). E.g. ff_all_formats() inherited the flaw.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
2020-08-07 19:43:20 +02:00
|
|
|
ff_formats_ref, ff_formats_unref);
|
2012-05-10 08:41:16 +03:00
|
|
|
}
|
2008-02-10 20:04:32 +02:00
|
|
|
|
2020-08-21 15:40:01 +02:00
|
|
|
int ff_default_query_formats(AVFilterContext *ctx)
|
2012-05-10 08:41:16 +03:00
|
|
|
{
|
2015-03-15 14:24:22 +02:00
|
|
|
int ret;
|
2012-05-10 08:41:16 +03:00
|
|
|
enum AVMediaType type = ctx->inputs && ctx->inputs [0] ? ctx->inputs [0]->type :
|
|
|
|
ctx->outputs && ctx->outputs[0] ? ctx->outputs[0]->type :
|
|
|
|
AVMEDIA_TYPE_VIDEO;
|
|
|
|
|
2015-03-15 14:24:22 +02:00
|
|
|
ret = ff_set_common_formats(ctx, ff_all_formats(type));
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2012-05-10 08:41:16 +03:00
|
|
|
if (type == AVMEDIA_TYPE_AUDIO) {
|
2020-08-21 15:40:01 +02:00
|
|
|
ret = ff_set_common_channel_layouts(ctx, ff_all_channel_counts());
|
2015-03-15 14:24:22 +02:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
ret = ff_set_common_samplerates(ctx, ff_all_samplerates());
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2008-02-10 20:04:32 +02:00
|
|
|
}
|
2012-05-10 08:41:16 +03:00
|
|
|
|
|
|
|
return 0;
|
2008-02-10 20:04:32 +02:00
|
|
|
}
|
|
|
|
|
2011-08-04 12:28:14 +03:00
|
|
|
/* internal functions for parsing audio format arguments */
|
|
|
|
|
2012-10-08 21:54:00 +03:00
|
|
|
int ff_parse_pixel_format(enum AVPixelFormat *ret, const char *arg, void *log_ctx)
|
2011-08-22 15:56:23 +03:00
|
|
|
{
|
|
|
|
char *tail;
|
|
|
|
int pix_fmt = av_get_pix_fmt(arg);
|
2012-10-08 21:54:00 +03:00
|
|
|
if (pix_fmt == AV_PIX_FMT_NONE) {
|
2011-08-22 15:56:23 +03:00
|
|
|
pix_fmt = strtol(arg, &tail, 0);
|
2014-05-27 18:43:03 +03:00
|
|
|
if (*tail || !av_pix_fmt_desc_get(pix_fmt)) {
|
2011-08-22 15:56:23 +03:00
|
|
|
av_log(log_ctx, AV_LOG_ERROR, "Invalid pixel format '%s'\n", arg);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*ret = pix_fmt;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-21 20:17:24 +03:00
|
|
|
int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx)
|
2011-08-04 12:28:14 +03:00
|
|
|
{
|
|
|
|
char *tail;
|
|
|
|
double srate = av_strtod(arg, &tail);
|
2011-08-21 20:17:24 +03:00
|
|
|
if (*tail || srate < 1 || (int)srate != srate || srate > INT_MAX) {
|
2011-08-04 12:28:14 +03:00
|
|
|
av_log(log_ctx, AV_LOG_ERROR, "Invalid sample rate '%s'\n", arg);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
*ret = srate;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-25 16:07:40 +03:00
|
|
|
int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg,
|
|
|
|
void *log_ctx)
|
2011-08-04 12:28:14 +03:00
|
|
|
{
|
2015-06-20 18:52:50 +02:00
|
|
|
int64_t chlayout;
|
2016-12-26 03:03:37 +02:00
|
|
|
int nb_channels;
|
|
|
|
|
|
|
|
if (av_get_extended_channel_layout(arg, &chlayout, &nb_channels) < 0) {
|
2018-01-06 23:04:21 +02:00
|
|
|
av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout '%s'\n", arg);
|
|
|
|
return AVERROR(EINVAL);
|
2016-12-26 03:03:37 +02:00
|
|
|
}
|
|
|
|
if (!chlayout && !nret) {
|
|
|
|
av_log(log_ctx, AV_LOG_ERROR, "Unknown channel layout '%s' is not supported.\n", arg);
|
|
|
|
return AVERROR(EINVAL);
|
2011-08-04 12:28:14 +03:00
|
|
|
}
|
|
|
|
*ret = chlayout;
|
2013-10-25 16:07:40 +03:00
|
|
|
if (nret)
|
2016-12-26 03:03:37 +02:00
|
|
|
*nret = nb_channels;
|
|
|
|
|
2011-08-04 12:28:14 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-13 13:18:15 +02:00
|
|
|
|
|
|
|
static int check_list(void *log, const char *name, const AVFilterFormats *fmts)
|
|
|
|
{
|
|
|
|
unsigned i, j;
|
|
|
|
|
|
|
|
if (!fmts)
|
|
|
|
return 0;
|
|
|
|
if (!fmts->nb_formats) {
|
|
|
|
av_log(log, AV_LOG_ERROR, "Empty %s list\n", name);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
for (i = 0; i < fmts->nb_formats; i++) {
|
|
|
|
for (j = i + 1; j < fmts->nb_formats; j++) {
|
|
|
|
if (fmts->formats[i] == fmts->formats[j]) {
|
|
|
|
av_log(log, AV_LOG_ERROR, "Duplicated %s\n", name);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_formats_check_pixel_formats(void *log, const AVFilterFormats *fmts)
|
|
|
|
{
|
|
|
|
return check_list(log, "pixel format", fmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_formats_check_sample_formats(void *log, const AVFilterFormats *fmts)
|
|
|
|
{
|
|
|
|
return check_list(log, "sample format", fmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_formats_check_sample_rates(void *log, const AVFilterFormats *fmts)
|
|
|
|
{
|
|
|
|
if (!fmts || !fmts->nb_formats)
|
|
|
|
return 0;
|
|
|
|
return check_list(log, "sample rate", fmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int layouts_compatible(uint64_t a, uint64_t b)
|
|
|
|
{
|
|
|
|
return a == b ||
|
|
|
|
(KNOWN(a) && !KNOWN(b) && av_get_channel_layout_nb_channels(a) == FF_LAYOUT2COUNT(b)) ||
|
|
|
|
(KNOWN(b) && !KNOWN(a) && av_get_channel_layout_nb_channels(b) == FF_LAYOUT2COUNT(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
int ff_formats_check_channel_layouts(void *log, const AVFilterChannelLayouts *fmts)
|
|
|
|
{
|
|
|
|
unsigned i, j;
|
|
|
|
|
|
|
|
if (!fmts)
|
|
|
|
return 0;
|
|
|
|
if (fmts->all_layouts < fmts->all_counts) {
|
|
|
|
av_log(log, AV_LOG_ERROR, "Inconsistent generic list\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
if (!fmts->all_layouts && !fmts->nb_channel_layouts) {
|
|
|
|
av_log(log, AV_LOG_ERROR, "Empty channel layout list\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
for (i = 0; i < fmts->nb_channel_layouts; i++) {
|
|
|
|
for (j = i + 1; j < fmts->nb_channel_layouts; j++) {
|
|
|
|
if (layouts_compatible(fmts->channel_layouts[i], fmts->channel_layouts[j])) {
|
|
|
|
av_log(log, AV_LOG_ERROR, "Duplicated or redundant channel layout\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|