ffmpeg/libavfilter/formats.c
Andreas Rheinhardt b4f5201967 avfilter: Replace query_formats callback with union of list and callback
If one looks at the many query_formats callbacks in existence,
one will immediately recognize that there is one type of default
callback for video and a slightly different default callback for
audio: It is "return ff_set_common_formats_from_list(ctx, pix_fmts);"
for video with a filter-specific pix_fmts list. For audio, it is
the same with a filter-specific sample_fmts list together with
ff_set_common_all_samplerates() and ff_set_common_all_channel_counts().

This commit allows to remove the boilerplate query_formats callbacks
by replacing said callback with a union consisting the old callback
and pointers for pixel and sample format arrays. For the not uncommon
case in which these lists only contain a single entry (besides the
sentinel) enum AVPixelFormat and enum AVSampleFormat fields are also
added to the union to store them directly in the AVFilter,
thereby avoiding a relocation.

The state of said union will be contained in a new, dedicated AVFilter
field (the nb_inputs and nb_outputs fields have been shrunk to uint8_t
in order to create a hole for this new field; this is no problem, as
the maximum of all the nb_inputs is four; for nb_outputs it is only
two).

The state's default value coincides with the earlier default of
query_formats being unset, namely that the filter accepts all formats
(and also sample rates and channel counts/layouts for audio)
provided that these properties agree coincide for all inputs and
outputs.

By using different union members for audio and video filters
the type-unsafety of using the same functions for audio and video
lists will furthermore be more confined to formats.c than before.

When the new fields are used, they will also avoid allocations:
Currently something nearly equivalent to ff_default_query_formats()
is called after every successful call to a query_formats callback;
yet in the common case that the newly allocated AVFilterFormats
are not used at all (namely if there are no free links) these newly
allocated AVFilterFormats are freed again without ever being used.
Filters no longer using the callback will not exhibit this any more.

Reviewed-by: Paul B Mahol <onemda@gmail.com>
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2021-10-05 17:48:25 +02:00

880 lines
32 KiB
C

/*
* Filter layer - format negotiation
* Copyright (c) 2007 Bobby Bingham
*
* 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
*/
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/eval.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "internal.h"
#include "formats.h"
#define KNOWN(l) (!FF_LAYOUT2COUNT(l)) /* for readability */
/**
* Add all refs from a to ret and destroy a.
*/
#define MERGE_REF(ret, a, fmts, type, fail_statement) \
do { \
type ***tmp; \
int i; \
\
if (!(tmp = av_realloc_array(ret->refs, ret->refcount + a->refcount, \
sizeof(*tmp)))) \
{ fail_statement } \
ret->refs = tmp; \
\
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)
/**
* Add all formats common to a and b to a, add b's refs to a and destroy b.
* If check is set, nothing is modified and it is only checked whether
* the formats are compatible.
* If empty_allowed is set and one of a,b->nb is zero, the lists are
* merged; otherwise, 0 (for nonmergeability) is returned.
*/
#define MERGE_FORMATS(a, b, fmts, nb, type, check, empty_allowed) \
do { \
int i, j, k = 0, skip = 0; \
\
if (empty_allowed) { \
if (!a->nb || !b->nb) { \
if (check) \
return 1; \
if (!a->nb) \
FFSWAP(type *, a, b); \
skip = 1; \
} \
} \
if (!skip) { \
for (i = 0; i < a->nb; i++) \
for (j = 0; j < b->nb; j++) \
if (a->fmts[i] == b->fmts[j]) { \
if (check) \
return 1; \
a->fmts[k++] = a->fmts[i]; \
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; \
} \
\
MERGE_REF(a, b, fmts, type, return AVERROR(ENOMEM);); \
} while (0)
static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
enum AVMediaType type, int check)
{
int i, j;
int alpha1=0, alpha2=0;
int chroma1=0, chroma2=0;
av_assert2(check || (a->refcount && b->refcount));
if (a == b)
return 1;
/* 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)
for (i = 0; i < a->nb_formats; i++) {
const AVPixFmtDescriptor *const adesc = av_pix_fmt_desc_get(a->formats[i]);
for (j = 0; j < b->nb_formats; j++) {
const AVPixFmtDescriptor *bdesc = av_pix_fmt_desc_get(b->formats[j]);
alpha2 |= adesc->flags & bdesc->flags & AV_PIX_FMT_FLAG_ALPHA;
chroma2|= adesc->nb_components > 1 && bdesc->nb_components > 1;
if (a->formats[i] == b->formats[j]) {
alpha1 |= adesc->flags & AV_PIX_FMT_FLAG_ALPHA;
chroma1|= adesc->nb_components > 1;
}
}
}
// If chroma or alpha can be lost through merging then do not merge
if (alpha2 > alpha1 || chroma2 > chroma1)
return 0;
MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 0);
return 1;
}
/**
* Check the formats lists for compatibility for merging without actually
* merging.
*
* @return 1 if they are compatible, 0 if not.
*/
static int can_merge_pix_fmts(const void *a, const void *b)
{
return merge_formats_internal((AVFilterFormats *)a,
(AVFilterFormats *)b, AVMEDIA_TYPE_VIDEO, 1);
}
/**
* Merge the formats lists if they are compatible and update all the
* references of a and b to point to the combined list and free the old
* lists as needed. The combined list usually contains the intersection of
* the lists of a and b.
*
* Both a and b must have owners (i.e. refcount > 0) for these functions.
*
* @return 1 if merging succeeded, 0 if a and b are incompatible
* and negative AVERROR code on failure.
* a and b are unmodified if 0 is returned.
*/
static int merge_pix_fmts(void *a, void *b)
{
return merge_formats_internal(a, b, AVMEDIA_TYPE_VIDEO, 0);
}
/**
* See can_merge_pix_fmts().
*/
static int can_merge_sample_fmts(const void *a, const void *b)
{
return merge_formats_internal((AVFilterFormats *)a,
(AVFilterFormats *)b, AVMEDIA_TYPE_AUDIO, 1);
}
/**
* See merge_pix_fmts().
*/
static int merge_sample_fmts(void *a, void *b)
{
return merge_formats_internal(a, b, AVMEDIA_TYPE_AUDIO, 0);
}
static int merge_samplerates_internal(AVFilterFormats *a,
AVFilterFormats *b, int check)
{
av_assert2(check || (a->refcount && b->refcount));
if (a == b) return 1;
MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 1);
return 1;
}
/**
* See can_merge_pix_fmts().
*/
static int can_merge_samplerates(const void *a, const void *b)
{
return merge_samplerates_internal((AVFilterFormats *)a, (AVFilterFormats *)b, 1);
}
/**
* See merge_pix_fmts().
*/
static int merge_samplerates(void *a, void *b)
{
return merge_samplerates_internal(a, b, 0);
}
/**
* See merge_pix_fmts().
*/
static int merge_channel_layouts(void *va, void *vb)
{
AVFilterChannelLayouts *a = va;
AVFilterChannelLayouts *b = vb;
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;
av_assert2(a->refcount && b->refcount);
if (a == b) return 1;
/* 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];
/* Not optimal: the unknown layouts of b may become known after
another merge. */
if (!j)
return 0;
b->nb_channel_layouts = j;
}
MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, return AVERROR(ENOMEM););
return 1;
}
ret_max = a->nb_channel_layouts + b->nb_channel_layouts;
if (!(channel_layouts = av_malloc_array(ret_max, sizeof(*channel_layouts))))
return AVERROR(ENOMEM);
/* 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]) {
channel_layouts[ret_nb++] = a->channel_layouts[i];
a->channel_layouts[i] = b->channel_layouts[j] = 0;
break;
}
}
}
/* 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)
channel_layouts[ret_nb++] = a->channel_layouts[i];
}
/* 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])
channel_layouts[ret_nb++] = a->channel_layouts[i];
}
if (!ret_nb) {
av_free(channel_layouts);
return 0;
}
if (a->refcount > b->refcount)
FFSWAP(AVFilterChannelLayouts *, a, b);
MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts,
{ av_free(channel_layouts); return AVERROR(ENOMEM); });
av_freep(&b->channel_layouts);
b->channel_layouts = channel_layouts;
b->nb_channel_layouts = ret_nb;
return 1;
}
static const AVFilterFormatsMerger mergers_video[] = {
{
.offset = offsetof(AVFilterFormatsConfig, formats),
.merge = merge_pix_fmts,
.can_merge = can_merge_pix_fmts,
},
};
static const AVFilterFormatsMerger mergers_audio[] = {
{
.offset = offsetof(AVFilterFormatsConfig, channel_layouts),
.merge = merge_channel_layouts,
.can_merge = NULL,
},
{
.offset = offsetof(AVFilterFormatsConfig, samplerates),
.merge = merge_samplerates,
.can_merge = can_merge_samplerates,
},
{
.offset = offsetof(AVFilterFormatsConfig, formats),
.merge = merge_sample_fmts,
.can_merge = can_merge_sample_fmts,
},
};
static const AVFilterNegotiation negotiate_video = {
.nb_mergers = FF_ARRAY_ELEMS(mergers_video),
.mergers = mergers_video,
.conversion_filter = "scale",
.conversion_opts_offset = offsetof(AVFilterGraph, scale_sws_opts),
};
static const AVFilterNegotiation negotiate_audio = {
.nb_mergers = FF_ARRAY_ELEMS(mergers_audio),
.mergers = mergers_audio,
.conversion_filter = "aresample",
.conversion_opts_offset = offsetof(AVFilterGraph, aresample_swr_opts),
};
const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link)
{
switch (link->type) {
case AVMEDIA_TYPE_VIDEO: return &negotiate_video;
case AVMEDIA_TYPE_AUDIO: return &negotiate_audio;
default: return NULL;
}
}
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;
}
#define MAKE_FORMAT_LIST(type, field, count_field) \
type *formats; \
int count = 0; \
if (fmts) \
for (count = 0; fmts[count] != -1; count++) \
; \
formats = av_mallocz(sizeof(*formats)); \
if (!formats) \
return NULL; \
formats->count_field = count; \
if (count) { \
formats->field = av_malloc_array(count, sizeof(*formats->field)); \
if (!formats->field) { \
av_freep(&formats); \
return NULL; \
} \
}
AVFilterFormats *ff_make_format_list(const int *fmts)
{
MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats);
while (count--)
formats->formats[count] = fmts[count];
return formats;
}
AVFilterChannelLayouts *ff_make_format64_list(const int64_t *fmts)
{
MAKE_FORMAT_LIST(AVFilterChannelLayouts,
channel_layouts, nb_channel_layouts);
if (count)
memcpy(formats->channel_layouts, fmts,
sizeof(*formats->channel_layouts) * count);
return formats;
}
#define ADD_FORMAT(f, fmt, unref_fn, type, list, nb) \
do { \
type *fmts; \
\
if (!(*f) && !(*f = av_mallocz(sizeof(**f)))) { \
return AVERROR(ENOMEM); \
} \
\
fmts = av_realloc_array((*f)->list, (*f)->nb + 1, \
sizeof(*(*f)->list)); \
if (!fmts) { \
unref_fn(f); \
return AVERROR(ENOMEM); \
} \
\
(*f)->list = fmts; \
(*f)->list[(*f)->nb++] = fmt; \
} while (0)
int ff_add_format(AVFilterFormats **avff, int64_t fmt)
{
ADD_FORMAT(avff, fmt, ff_formats_unref, int, formats, nb_formats);
return 0;
}
int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
{
av_assert1(!(*l && (*l)->all_layouts));
ADD_FORMAT(l, channel_layout, ff_channel_layouts_unref, uint64_t, channel_layouts, nb_channel_layouts);
return 0;
}
AVFilterFormats *ff_make_formats_list_singleton(int fmt)
{
int fmts[2] = { fmt, -1 };
return ff_make_format_list(fmts);
}
AVFilterFormats *ff_all_formats(enum AVMediaType type)
{
AVFilterFormats *ret = NULL;
if (type == AVMEDIA_TYPE_VIDEO) {
return ff_formats_pixdesc_filter(0, 0);
} else if (type == AVMEDIA_TYPE_AUDIO) {
enum AVSampleFormat fmt = 0;
while (av_get_sample_fmt_name(fmt)) {
if (ff_add_format(&ret, fmt) < 0)
return NULL;
fmt++;
}
}
return ret;
}
AVFilterFormats *ff_formats_pixdesc_filter(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);
return formats;
}
formats = av_mallocz(sizeof(*formats));
if (!formats)
return NULL;
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 NULL;
}
}
}
}
AVFilterFormats *ff_planar_sample_fmts(void)
{
AVFilterFormats *ret = NULL;
int fmt;
for (fmt = 0; av_get_bytes_per_sample(fmt)>0; fmt++)
if (av_sample_fmt_is_planar(fmt))
if (ff_add_format(&ret, fmt) < 0)
return NULL;
return ret;
}
AVFilterFormats *ff_all_samplerates(void)
{
AVFilterFormats *ret = av_mallocz(sizeof(*ret));
return ret;
}
AVFilterChannelLayouts *ff_all_channel_layouts(void)
{
AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret));
if (!ret)
return NULL;
ret->all_layouts = 1;
return ret;
}
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;
}
#define FORMATS_REF(f, ref, unref_fn) \
void *tmp; \
\
if (!f) \
return AVERROR(ENOMEM); \
\
tmp = av_realloc_array(f->refs, sizeof(*f->refs), f->refcount + 1); \
if (!tmp) { \
unref_fn(&f); \
return AVERROR(ENOMEM); \
} \
f->refs = tmp; \
f->refs[f->refcount++] = ref; \
*ref = f; \
return 0
int ff_channel_layouts_ref(AVFilterChannelLayouts *f, AVFilterChannelLayouts **ref)
{
FORMATS_REF(f, ref, ff_channel_layouts_unref);
}
int ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
{
FORMATS_REF(f, ref, ff_formats_unref);
}
#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; \
\
if (!*ref) \
return; \
\
FIND_REF_INDEX(ref, idx); \
\
if (idx >= 0) { \
memmove((*ref)->refs + idx, (*ref)->refs + idx + 1, \
sizeof(*(*ref)->refs) * ((*ref)->refcount - idx - 1)); \
--(*ref)->refcount; \
} \
if (!(*ref)->refcount) { \
av_free((*ref)->list); \
av_free((*ref)->refs); \
av_free(*ref); \
} \
*ref = NULL; \
} while (0)
void ff_formats_unref(AVFilterFormats **ref)
{
FORMATS_UNREF(ref, formats);
}
void ff_channel_layouts_unref(AVFilterChannelLayouts **ref)
{
FORMATS_UNREF(ref, channel_layouts);
}
#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);
}
void ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref)
{
FORMATS_CHANGEREF(oldref, newref);
}
#define SET_COMMON_FORMATS(ctx, fmts, media_type, ref_fn, unref_fn) \
int i; \
\
if (!fmts) \
return AVERROR(ENOMEM); \
\
for (i = 0; i < ctx->nb_inputs; i++) { \
AVFilterLink *const link = ctx->inputs[i]; \
if (link && !link->outcfg.fmts && \
(media_type == AVMEDIA_TYPE_UNKNOWN || link->type == media_type)) { \
int ret = ref_fn(fmts, &ctx->inputs[i]->outcfg.fmts); \
if (ret < 0) { \
return ret; \
} \
} \
} \
for (i = 0; i < ctx->nb_outputs; i++) { \
AVFilterLink *const link = ctx->outputs[i]; \
if (link && !link->incfg.fmts && \
(media_type == AVMEDIA_TYPE_UNKNOWN || link->type == media_type)) { \
int ret = ref_fn(fmts, &ctx->outputs[i]->incfg.fmts); \
if (ret < 0) { \
return ret; \
} \
} \
} \
\
if (!fmts->refcount) \
unref_fn(&fmts); \
\
return 0;
int ff_set_common_channel_layouts(AVFilterContext *ctx,
AVFilterChannelLayouts *channel_layouts)
{
SET_COMMON_FORMATS(ctx, channel_layouts, AVMEDIA_TYPE_AUDIO,
ff_channel_layouts_ref, ff_channel_layouts_unref);
}
int ff_set_common_channel_layouts_from_list(AVFilterContext *ctx,
const int64_t *fmts)
{
return ff_set_common_channel_layouts(ctx, ff_make_format64_list(fmts));
}
int ff_set_common_all_channel_counts(AVFilterContext *ctx)
{
return ff_set_common_channel_layouts(ctx, ff_all_channel_counts());
}
int ff_set_common_samplerates(AVFilterContext *ctx,
AVFilterFormats *samplerates)
{
SET_COMMON_FORMATS(ctx, samplerates, AVMEDIA_TYPE_AUDIO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_samplerates_from_list(AVFilterContext *ctx,
const int *samplerates)
{
return ff_set_common_samplerates(ctx, ff_make_format_list(samplerates));
}
int ff_set_common_all_samplerates(AVFilterContext *ctx)
{
return ff_set_common_samplerates(ctx, ff_all_samplerates());
}
/**
* 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.
*/
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
{
SET_COMMON_FORMATS(ctx, formats, AVMEDIA_TYPE_UNKNOWN,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts)
{
return ff_set_common_formats(ctx, ff_make_format_list(fmts));
}
int ff_default_query_formats(AVFilterContext *ctx)
{
const AVFilter *const f = ctx->filter;
AVFilterFormats *formats;
enum AVMediaType type;
int ret;
switch (f->formats_state) {
case FF_FILTER_FORMATS_PIXFMT_LIST:
type = AVMEDIA_TYPE_VIDEO;
formats = ff_make_format_list(f->formats.pixels_list);
break;
case FF_FILTER_FORMATS_SAMPLEFMTS_LIST:
type = AVMEDIA_TYPE_AUDIO;
formats = ff_make_format_list(f->formats.samples_list);
break;
case FF_FILTER_FORMATS_SINGLE_PIXFMT:
type = AVMEDIA_TYPE_VIDEO;
formats = ff_make_formats_list_singleton(f->formats.pix_fmt);
break;
case FF_FILTER_FORMATS_SINGLE_SAMPLEFMT:
type = AVMEDIA_TYPE_AUDIO;
formats = ff_make_formats_list_singleton(f->formats.sample_fmt);
break;
default:
av_assert2(!"Unreachable");
/* Intended fallthrough */
case FF_FILTER_FORMATS_PASSTHROUGH:
case FF_FILTER_FORMATS_QUERY_FUNC:
type = ctx->nb_inputs ? ctx->inputs [0]->type :
ctx->nb_outputs ? ctx->outputs[0]->type : AVMEDIA_TYPE_VIDEO;
formats = ff_all_formats(type);
break;
}
ret = ff_set_common_formats(ctx, formats);
if (ret < 0)
return ret;
if (type == AVMEDIA_TYPE_AUDIO) {
ret = ff_set_common_all_channel_counts(ctx);
if (ret < 0)
return ret;
ret = ff_set_common_all_samplerates(ctx);
if (ret < 0)
return ret;
}
return 0;
}
/* internal functions for parsing audio format arguments */
int ff_parse_pixel_format(enum AVPixelFormat *ret, const char *arg, void *log_ctx)
{
char *tail;
int pix_fmt = av_get_pix_fmt(arg);
if (pix_fmt == AV_PIX_FMT_NONE) {
pix_fmt = strtol(arg, &tail, 0);
if (*tail || !av_pix_fmt_desc_get(pix_fmt)) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid pixel format '%s'\n", arg);
return AVERROR(EINVAL);
}
}
*ret = pix_fmt;
return 0;
}
int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx)
{
char *tail;
double srate = av_strtod(arg, &tail);
if (*tail || srate < 1 || (int)srate != srate || srate > INT_MAX) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid sample rate '%s'\n", arg);
return AVERROR(EINVAL);
}
*ret = srate;
return 0;
}
int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg,
void *log_ctx)
{
int64_t chlayout;
int nb_channels;
if (av_get_extended_channel_layout(arg, &chlayout, &nb_channels) < 0) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout '%s'\n", arg);
return AVERROR(EINVAL);
}
if (!chlayout && !nret) {
av_log(log_ctx, AV_LOG_ERROR, "Unknown channel layout '%s' is not supported.\n", arg);
return AVERROR(EINVAL);
}
*ret = chlayout;
if (nret)
*nret = nb_channels;
return 0;
}
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;
}