lavfi/formats: put merge functions in structures.

It makes the code clearer and will allow adding new stages
of negotiation easier.
This commit is contained in:
Nicolas George 2021-07-24 17:06:53 +02:00
parent 1d8e1afc00
commit 86d3dd5627
3 changed files with 157 additions and 94 deletions

View File

@ -456,49 +456,42 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx)
for (j = 0; j < filter->nb_inputs; j++) {
AVFilterLink *link = filter->inputs[j];
const AVFilterNegotiation *neg;
unsigned neg_step;
int convert_needed = 0;
if (!link)
continue;
if (link->incfg.formats != link->outcfg.formats
&& link->incfg.formats && link->outcfg.formats)
if (!ff_can_merge_formats(link->incfg.formats, link->outcfg.formats,
link->type))
neg = ff_filter_get_negotiation(link);
av_assert0(neg);
for (neg_step = 1; neg_step < neg->nb; neg_step++) {
const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
void *a = FF_FIELD_AT(void *, m->offset, link->incfg);
void *b = FF_FIELD_AT(void *, m->offset, link->outcfg);
if (a && b && a != b && !m->can_merge(a, b)) {
convert_needed = 1;
if (link->type == AVMEDIA_TYPE_AUDIO) {
if (link->incfg.samplerates != link->outcfg.samplerates
&& link->incfg.samplerates && link->outcfg.samplerates)
if (!ff_can_merge_samplerates(link->incfg.samplerates,
link->outcfg.samplerates))
break;
}
}
for (neg_step = 0; neg_step < neg->nb; neg_step++) {
const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
void *a = FF_FIELD_AT(void *, m->offset, link->incfg);
void *b = FF_FIELD_AT(void *, m->offset, link->outcfg);
if (!(a && b)) {
count_delayed++;
} else if (a == b) {
count_already_merged++;
} else if (!convert_needed) {
count_merged++;
ret = m->merge(a, b);
if (ret < 0)
return ret;
if (!ret)
convert_needed = 1;
}
}
#define CHECKED_MERGE(field, ...) ((ret = ff_merge_ ## field(__VA_ARGS__)) <= 0)
#define MERGE_DISPATCH(field, ...) \
if (!(link->incfg.field && link->outcfg.field)) { \
count_delayed++; \
} else if (link->incfg.field == link->outcfg.field) { \
count_already_merged++; \
} else if (!convert_needed) { \
count_merged++; \
if (CHECKED_MERGE(field, __VA_ARGS__)) { \
if (ret < 0) \
return ret; \
convert_needed = 1; \
} \
}
if (link->type == AVMEDIA_TYPE_AUDIO) {
MERGE_DISPATCH(channel_layouts, link->incfg.channel_layouts,
link->outcfg.channel_layouts)
MERGE_DISPATCH(samplerates, link->incfg.samplerates,
link->outcfg.samplerates)
}
MERGE_DISPATCH(formats, link->incfg.formats,
link->outcfg.formats, link->type)
#undef MERGE_DISPATCH
if (convert_needed) {
AVFilterContext *convert;
const AVFilter *filter;
@ -570,26 +563,21 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx)
av_assert0(outlink-> incfg.channel_layouts->refcount > 0);
av_assert0(outlink->outcfg.channel_layouts->refcount > 0);
}
if (CHECKED_MERGE(formats, inlink->incfg.formats,
inlink->outcfg.formats, inlink->type) ||
CHECKED_MERGE(formats, outlink->incfg.formats,
outlink->outcfg.formats, outlink->type) ||
inlink->type == AVMEDIA_TYPE_AUDIO &&
(CHECKED_MERGE(samplerates, inlink->incfg.samplerates,
inlink->outcfg.samplerates) ||
CHECKED_MERGE(channel_layouts, inlink->incfg.channel_layouts,
inlink->outcfg.channel_layouts)) ||
outlink->type == AVMEDIA_TYPE_AUDIO &&
(CHECKED_MERGE(samplerates, outlink->incfg.samplerates,
outlink->outcfg.samplerates) ||
CHECKED_MERGE(channel_layouts, outlink->incfg.channel_layouts,
outlink->outcfg.channel_layouts))) {
if (ret < 0)
return ret;
av_log(log_ctx, AV_LOG_ERROR,
"Impossible to convert between the formats supported by the filter "
"'%s' and the filter '%s'\n", link->src->name, link->dst->name);
return AVERROR(ENOSYS);
for (neg_step = 0; neg_step < neg->nb; neg_step++) {
const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
void *ia = FF_FIELD_AT(void *, m->offset, inlink->incfg);
void *ib = FF_FIELD_AT(void *, m->offset, inlink->outcfg);
void *oa = FF_FIELD_AT(void *, m->offset, outlink->incfg);
void *ob = FF_FIELD_AT(void *, m->offset, outlink->outcfg);
if ((ret = m->merge(ia, ib)) <= 0 ||
(ret = m->merge(oa, ob)) <= 0) {
if (ret < 0)
return ret;
av_log(log_ctx, AV_LOG_ERROR,
"Impossible to convert between the formats supported by the filter "
"'%s' and the filter '%s'\n", link->src->name, link->dst->name);
return AVERROR(ENOSYS);
}
}
}
}

View File

@ -100,6 +100,8 @@ static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
int alpha1=0, alpha2=0;
int chroma1=0, chroma2=0;
av_assert2(check || (a->refcount && b->refcount));
if (a == b)
return 1;
@ -132,43 +134,86 @@ static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
return 1;
}
int ff_can_merge_formats(const AVFilterFormats *a, const AVFilterFormats *b,
enum AVMediaType type)
/**
* 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, type, 1);
(AVFilterFormats *)b, AVMEDIA_TYPE_VIDEO, 1);
}
int ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
enum AVMediaType type)
/**
* 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)
{
av_assert2(a->refcount && b->refcount);
return merge_formats_internal(a, b, type, 0);
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;
}
int ff_can_merge_samplerates(const AVFilterFormats *a, const AVFilterFormats *b)
/**
* 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);
}
int ff_merge_samplerates(AVFilterFormats *a, AVFilterFormats *b)
/**
* See merge_pix_fmts().
*/
static int merge_samplerates(void *a, void *b)
{
av_assert2(a->refcount && b->refcount);
return merge_samplerates_internal(a, b, 0);
}
int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
AVFilterChannelLayouts *b)
/**
* 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;
@ -255,6 +300,51 @@ int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
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 = FF_ARRAY_ELEMS(mergers_video),
.mergers = mergers_video,
};
static const AVFilterNegotiation negotiate_audio = {
.nb = FF_ARRAY_ELEMS(mergers_audio),
.mergers = mergers_audio,
};
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;

View File

@ -69,6 +69,19 @@ struct AVFilterFormats {
struct AVFilterFormats ***refs; ///< references to this list
};
typedef struct AVFilterFormatMerger {
unsigned offset;
int (*merge)(void *a, void *b);
int (*can_merge)(const void *a, const void *b);
} AVFilterFormatsMerger;
typedef struct AVFilterNegotiation {
unsigned nb;
const AVFilterFormatsMerger *mergers;
} AVFilterNegotiation;
const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link);
/**
* A list of supported channel layouts.
*
@ -108,34 +121,6 @@ struct AVFilterChannelLayouts {
#define FF_LAYOUT2COUNT(l) (((l) & 0x8000000000000000ULL) ? \
(int)((l) & 0x7FFFFFFF) : 0)
/**
* Check the formats/samplerates lists for compatibility for merging
* without actually merging.
*
* @return 1 if they are compatible, 0 if not.
*/
int ff_can_merge_formats(const AVFilterFormats *a, const AVFilterFormats *b,
enum AVMediaType type);
int ff_can_merge_samplerates(const AVFilterFormats *a, const AVFilterFormats *b);
/**
* Merge the formats/channel layouts/samplerates 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.
*/
int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
AVFilterChannelLayouts *b);
int ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
enum AVMediaType type);
int ff_merge_samplerates(AVFilterFormats *a, AVFilterFormats *b);
/**
* Construct an empty AVFilterChannelLayouts/AVFilterFormats struct --
* representing any channel layout (with known disposition)/sample rate.