diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index fd996db94b..718f77df3d 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -618,6 +618,22 @@ struct AVFilterLink { * input link is assumed to be an unchangeable property. */ AVRational time_base; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavfilter and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + /** + * Lists of channel layouts and sample rates used for automatic + * negotiation. + */ + AVFilterFormats *in_samplerates; + AVFilterFormats *out_samplerates; + struct AVFilterChannelLayouts *in_channel_layouts; + struct AVFilterChannelLayouts *out_channel_layouts; }; /** diff --git a/libavfilter/formats.c b/libavfilter/formats.c index 206eff5587..06567c4d2f 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -22,58 +22,132 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" +#include "formats.h" /** * Add all refs from a to ret and destroy a. */ -static void merge_ref(AVFilterFormats *ret, AVFilterFormats *a) -{ - int i; - - for(i = 0; i < a->refcount; i ++) { - ret->refs[ret->refcount] = a->refs[i]; - *ret->refs[ret->refcount++] = ret; - } +#define MERGE_REF(ret, a, fmts, type, fail) \ +do { \ + type ***tmp; \ + int i; \ + \ + if (!(tmp = av_realloc(ret->refs, \ + sizeof(*tmp) * (ret->refcount + a->refcount)))) \ + goto fail; \ + 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) - av_free(a->refs); - av_free(a->formats); - av_free(a); -} +/** + * Add all formats common for a and b to ret, copy the refs and destroy + * a and b. + */ +#define MERGE_FORMATS(ret, a, b, fmts, nb, type, fail) \ +do { \ + int i, j, k = 0, count = FFMIN(a->nb, b->nb); \ + \ + if (!(ret = av_mallocz(sizeof(*ret)))) \ + goto fail; \ + \ + if (count) { \ + if (!(ret->fmts = av_malloc(sizeof(*ret->fmts) * count))) \ + goto fail; \ + for (i = 0; i < a->nb; i++) \ + for (j = 0; j < b->nb; j++) \ + if (a->fmts[i] == b->fmts[j]) \ + ret->fmts[k++] = a->fmts[i]; \ + \ + ret->nb = k; \ + } \ + /* check that there was at least one common format */ \ + if (!ret->nb) \ + goto fail; \ + \ + MERGE_REF(ret, a, fmts, type, fail); \ + MERGE_REF(ret, b, fmts, type, fail); \ +} while (0) AVFilterFormats *avfilter_merge_formats(AVFilterFormats *a, AVFilterFormats *b) { - AVFilterFormats *ret; - unsigned i, j, k = 0, m_count; + AVFilterFormats *ret = NULL; if (a == b) return a; - ret = av_mallocz(sizeof(*ret)); - - /* merge list of formats */ - m_count = FFMIN(a->format_count, b->format_count); - if (m_count) { - ret->formats = av_malloc(sizeof(*ret->formats) * m_count); - for(i = 0; i < a->format_count; i ++) - for(j = 0; j < b->format_count; j ++) - if(a->formats[i] == b->formats[j]) - ret->formats[k++] = a->formats[i]; + MERGE_FORMATS(ret, a, b, formats, format_count, AVFilterFormats, fail); - ret->format_count = k; + return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->formats); } - /* check that there was at least one common format */ - if(!ret->format_count) { - av_free(ret->formats); - av_free(ret); - return NULL; + av_freep(&ret); + return NULL; +} + +AVFilterFormats *ff_merge_samplerates(AVFilterFormats *a, + AVFilterFormats *b) +{ + AVFilterFormats *ret = NULL; + + if (a == b) return a; + + if (a->format_count && b->format_count) { + MERGE_FORMATS(ret, a, b, formats, format_count, AVFilterFormats, fail); + } else if (a->format_count) { + MERGE_REF(a, b, formats, AVFilterFormats, fail); + ret = a; + } else { + MERGE_REF(b, a, formats, AVFilterFormats, fail); + ret = b; } - ret->refs = av_malloc(sizeof(*ret->refs) * (a->refcount + b->refcount)); + return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->formats); + } + av_freep(&ret); + return NULL; +} - merge_ref(ret, a); - merge_ref(ret, b); +AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b) +{ + AVFilterChannelLayouts *ret = NULL; + + if (a == b) return a; + + if (a->nb_channel_layouts && b->nb_channel_layouts) { + MERGE_FORMATS(ret, a, b, channel_layouts, nb_channel_layouts, + AVFilterChannelLayouts, fail); + } else if (a->nb_channel_layouts) { + MERGE_REF(a, b, channel_layouts, AVFilterChannelLayouts, fail); + ret = a; + } else { + MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail); + ret = b; + } return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->channel_layouts); + } + av_freep(&ret); + return NULL; } int ff_fmt_is_in(int fmt, const int *fmts) @@ -104,21 +178,31 @@ AVFilterFormats *avfilter_make_format_list(const int *fmts) return formats; } +#define ADD_FORMAT(f, fmt, type, list, nb) \ +do { \ + type *fmts; \ + \ + if (!(*f) && !(*f = av_mallocz(sizeof(**f)))) \ + return AVERROR(ENOMEM); \ + \ + fmts = av_realloc((*f)->list, \ + sizeof(*(*f)->list) * ((*f)->nb + 1));\ + if (!fmts) \ + return AVERROR(ENOMEM); \ + \ + (*f)->list = fmts; \ + (*f)->list[(*f)->nb++] = fmt; \ + return 0; \ +} while (0) + int avfilter_add_format(AVFilterFormats **avff, int fmt) { - int *fmts; - - if (!(*avff) && !(*avff = av_mallocz(sizeof(**avff)))) - return AVERROR(ENOMEM); - - fmts = av_realloc((*avff)->formats, - sizeof(*(*avff)->formats) * ((*avff)->format_count+1)); - if (!fmts) - return AVERROR(ENOMEM); + ADD_FORMAT(avff, fmt, int, formats, format_count); +} - (*avff)->formats = fmts; - (*avff)->formats[(*avff)->format_count++] = fmt; - return 0; +int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) +{ + ADD_FORMAT(l, channel_layout, uint64_t, channel_layouts, nb_channel_layouts); } AVFilterFormats *avfilter_all_formats(enum AVMediaType type) @@ -136,51 +220,97 @@ AVFilterFormats *avfilter_all_formats(enum AVMediaType type) return ret; } -void avfilter_formats_ref(AVFilterFormats *f, AVFilterFormats **ref) +AVFilterFormats *ff_all_samplerates(void) { - *ref = f; - f->refs = av_realloc(f->refs, sizeof(*f->refs) * ++f->refcount); - f->refs[f->refcount-1] = ref; + AVFilterFormats *ret = av_mallocz(sizeof(*ret)); + return ret; } -static int find_ref_index(AVFilterFormats **ref) +AVFilterChannelLayouts *ff_all_channel_layouts(void) { - int i; - for(i = 0; i < (*ref)->refcount; i ++) - if((*ref)->refs[i] == ref) - return i; - return -1; + AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret)); + return ret; } -void avfilter_formats_unref(AVFilterFormats **ref) +#define FORMATS_REF(f, ref) \ +do { \ + *ref = f; \ + f->refs = av_realloc(f->refs, sizeof(*f->refs) * ++f->refcount); \ + f->refs[f->refcount-1] = ref; \ +} while (0) + +void ff_channel_layouts_ref(AVFilterChannelLayouts *f, AVFilterChannelLayouts **ref) { - int idx; + FORMATS_REF(f, ref); +} - if (!*ref) - return; +void avfilter_formats_ref(AVFilterFormats *f, AVFilterFormats **ref) +{ + FORMATS_REF(f, ref); +} - idx = find_ref_index(ref); +#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)); \ + \ + if(!--(*ref)->refcount) { \ + av_free((*ref)->list); \ + av_free((*ref)->refs); \ + av_free(*ref); \ + } \ + *ref = NULL; \ +} while (0) - if(idx >= 0) - memmove((*ref)->refs + idx, (*ref)->refs + idx + 1, - sizeof(*(*ref)->refs) * ((*ref)->refcount - idx - 1)); +void avfilter_formats_unref(AVFilterFormats **ref) +{ + FORMATS_UNREF(ref, formats); +} - if(!--(*ref)->refcount) { - av_free((*ref)->formats); - av_free((*ref)->refs); - av_free(*ref); - } - *ref = NULL; +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 avfilter_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref) { - int idx = find_ref_index(oldref); - - if(idx >= 0) { - (*oldref)->refs[idx] = newref; - *newref = *oldref; - *oldref = NULL; - } + FORMATS_CHANGEREF(oldref, newref); } diff --git a/libavfilter/formats.h b/libavfilter/formats.h new file mode 100644 index 0000000000..7e0a60114c --- /dev/null +++ b/libavfilter/formats.h @@ -0,0 +1,78 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_FORMATS_H +#define AVFILTER_FORMATS_H + +#include "avfilter.h" + +typedef struct AVFilterChannelLayouts { + uint64_t *channel_layouts; ///< list of channel layouts + int nb_channel_layouts; ///< number of channel layouts + + unsigned refcount; ///< number of references to this list + struct AVFilterChannelLayouts ***refs; ///< references to this list +} AVFilterChannelLayouts; + +/** + * Return a channel layouts/samplerates list which contains the intersection of + * the layouts/samplerates of a and b. Also, all the references of a, all the + * references of b, and a and b themselves will be deallocated. + * + * If a and b do not share any common elements, neither is modified, and NULL + * is returned. + */ +AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b); +AVFilterFormats *ff_merge_samplerates(AVFilterFormats *a, + AVFilterFormats *b); + +/** + * Construct an empty AVFilterChannelLayouts/AVFilterFormats struct -- + * representing any channel layout/sample rate. + */ +AVFilterChannelLayouts *ff_all_channel_layouts(void); +AVFilterFormats *ff_all_samplerates(void); + +/** + * A helper for query_formats() which sets all links to the same list of channel + * layouts/sample rates. If there are no links hooked to this filter, the list + * is freed. + */ +void ff_set_common_channel_layouts(AVFilterContext *ctx, + AVFilterChannelLayouts *layouts); +void ff_set_common_samplerates(AVFilterContext *ctx, + AVFilterFormats *samplerates); + +int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout); + +/** + * Add *ref as a new reference to f. + */ +void ff_channel_layouts_ref(AVFilterChannelLayouts *f, + AVFilterChannelLayouts **ref); + +/** + * Remove a reference to a channel layouts list. + */ +void ff_channel_layouts_unref(AVFilterChannelLayouts **ref); + +void ff_channel_layouts_changeref(AVFilterChannelLayouts **oldref, + AVFilterChannelLayouts **newref); + +#endif // AVFILTER_FORMATS_H