lavfi: support unknown channel layouts.
This commit is contained in:
		
							parent
							
								
									fccd8c21c4
								
							
						
					
					
						commit
						b6afb2dde1
					
				| @ -44,7 +44,7 @@ AVFilterBufferRef *ff_default_get_audio_buffer(AVFilterLink *link, int perms, | |||||||
|     AVFilterBufferRef *samplesref = NULL; |     AVFilterBufferRef *samplesref = NULL; | ||||||
|     uint8_t **data; |     uint8_t **data; | ||||||
|     int planar      = av_sample_fmt_is_planar(link->format); |     int planar      = av_sample_fmt_is_planar(link->format); | ||||||
|     int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); |     int nb_channels = link->channels; | ||||||
|     int planes      = planar ? nb_channels : 1; |     int planes      = planar ? nb_channels : 1; | ||||||
|     int linesize; |     int linesize; | ||||||
|     int full_perms = AV_PERM_READ | AV_PERM_WRITE | AV_PERM_PRESERVE | |     int full_perms = AV_PERM_READ | AV_PERM_WRITE | AV_PERM_PRESERVE | | ||||||
|  | |||||||
| @ -96,9 +96,6 @@ AVFilterBufferRef *avfilter_get_audio_buffer_ref_from_frame(const AVFrame *frame | |||||||
|     int channels = av_frame_get_channels(frame); |     int channels = av_frame_get_channels(frame); | ||||||
|     int64_t layout = av_frame_get_channel_layout(frame); |     int64_t layout = av_frame_get_channel_layout(frame); | ||||||
| 
 | 
 | ||||||
|     if(av_frame_get_channels(frame) > 8) // libavfilter does not suport more than 8 channels FIXME, remove once libavfilter is fixed
 |  | ||||||
|         return NULL; |  | ||||||
| 
 |  | ||||||
|     if (layout && av_get_channel_layout_nb_channels(layout) != av_frame_get_channels(frame)) { |     if (layout && av_get_channel_layout_nb_channels(layout) != av_frame_get_channels(frame)) { | ||||||
|         av_log(0, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n"); |         av_log(0, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n"); | ||||||
|         return NULL; |         return NULL; | ||||||
|  | |||||||
| @ -185,9 +185,24 @@ AVFilterContext *avfilter_graph_get_filter(AVFilterGraph *graph, char *name) | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void sanitize_channel_layouts(void *log, AVFilterChannelLayouts *l) | ||||||
|  | { | ||||||
|  |     if (!l) | ||||||
|  |         return; | ||||||
|  |     if (l->nb_channel_layouts) { | ||||||
|  |         if (l->all_layouts || l->all_counts) | ||||||
|  |             av_log(log, AV_LOG_WARNING, "All layouts set on non-empty list\n"); | ||||||
|  |         l->all_layouts = l->all_counts = 0; | ||||||
|  |     } else { | ||||||
|  |         if (l->all_counts && !l->all_layouts) | ||||||
|  |             av_log(log, AV_LOG_WARNING, "All counts without all layouts\n"); | ||||||
|  |         l->all_layouts = 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int filter_query_formats(AVFilterContext *ctx) | static int filter_query_formats(AVFilterContext *ctx) | ||||||
| { | { | ||||||
|     int ret; |     int ret, i; | ||||||
|     AVFilterFormats *formats; |     AVFilterFormats *formats; | ||||||
|     AVFilterChannelLayouts *chlayouts; |     AVFilterChannelLayouts *chlayouts; | ||||||
|     AVFilterFormats *samplerates; |     AVFilterFormats *samplerates; | ||||||
| @ -201,6 +216,11 @@ static int filter_query_formats(AVFilterContext *ctx) | |||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     for (i = 0; i < ctx->nb_inputs; i++) | ||||||
|  |         sanitize_channel_layouts(ctx, ctx->inputs[i]->out_channel_layouts); | ||||||
|  |     for (i = 0; i < ctx->nb_outputs; i++) | ||||||
|  |         sanitize_channel_layouts(ctx, ctx->outputs[i]->in_channel_layouts); | ||||||
|  | 
 | ||||||
|     formats = ff_all_formats(type); |     formats = ff_all_formats(type); | ||||||
|     if (!formats) |     if (!formats) | ||||||
|         return AVERROR(ENOMEM); |         return AVERROR(ENOMEM); | ||||||
| @ -470,7 +490,7 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) | |||||||
|         link->in_samplerates->format_count = 1; |         link->in_samplerates->format_count = 1; | ||||||
|         link->sample_rate = link->in_samplerates->formats[0]; |         link->sample_rate = link->in_samplerates->formats[0]; | ||||||
| 
 | 
 | ||||||
|         if (!link->in_channel_layouts->nb_channel_layouts) { |         if (link->in_channel_layouts->all_layouts) { | ||||||
|             av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" |             av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" | ||||||
|                    "the link between filters %s and %s.\n", link->src->name, |                    "the link between filters %s and %s.\n", link->src->name, | ||||||
|                    link->dst->name); |                    link->dst->name); | ||||||
| @ -478,7 +498,10 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) | |||||||
|         } |         } | ||||||
|         link->in_channel_layouts->nb_channel_layouts = 1; |         link->in_channel_layouts->nb_channel_layouts = 1; | ||||||
|         link->channel_layout = link->in_channel_layouts->channel_layouts[0]; |         link->channel_layout = link->in_channel_layouts->channel_layouts[0]; | ||||||
|         link->channels = av_get_channel_layout_nb_channels(link->channel_layout); |         if ((link->channels = FF_LAYOUT2COUNT(link->channel_layout))) | ||||||
|  |             link->channel_layout = 0; | ||||||
|  |         else | ||||||
|  |             link->channels = av_get_channel_layout_nb_channels(link->channel_layout); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ff_formats_unref(&link->in_formats); |     ff_formats_unref(&link->in_formats); | ||||||
| @ -534,8 +557,42 @@ static int reduce_formats_on_filter(AVFilterContext *filter) | |||||||
|                    format_count, ff_add_format); |                    format_count, ff_add_format); | ||||||
|     REDUCE_FORMATS(int,      AVFilterFormats,        samplerates,     formats, |     REDUCE_FORMATS(int,      AVFilterFormats,        samplerates,     formats, | ||||||
|                    format_count, ff_add_format); |                    format_count, ff_add_format); | ||||||
|     REDUCE_FORMATS(uint64_t, AVFilterChannelLayouts, channel_layouts, | 
 | ||||||
|                    channel_layouts, nb_channel_layouts, ff_add_channel_layout); |     /* reduce channel layouts */ | ||||||
|  |     for (i = 0; i < filter->nb_inputs; i++) { | ||||||
|  |         AVFilterLink *inlink = filter->inputs[i]; | ||||||
|  |         uint64_t fmt; | ||||||
|  | 
 | ||||||
|  |         if (!inlink->out_channel_layouts || | ||||||
|  |             inlink->out_channel_layouts->nb_channel_layouts != 1) | ||||||
|  |             continue; | ||||||
|  |         fmt = inlink->out_channel_layouts->channel_layouts[0]; | ||||||
|  | 
 | ||||||
|  |         for (j = 0; j < filter->nb_outputs; j++) { | ||||||
|  |             AVFilterLink *outlink = filter->outputs[j]; | ||||||
|  |             AVFilterChannelLayouts *fmts; | ||||||
|  | 
 | ||||||
|  |             fmts = outlink->in_channel_layouts; | ||||||
|  |             if (inlink->type != outlink->type || fmts->nb_channel_layouts == 1) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             if (fmts->all_layouts) { | ||||||
|  |                 /* Turn the infinite list into a singleton */ | ||||||
|  |                 fmts->all_layouts = fmts->all_counts  = 0; | ||||||
|  |                 ff_add_channel_layout(&outlink->in_channel_layouts, fmt); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for (k = 0; k < outlink->in_channel_layouts->nb_channel_layouts; k++) { | ||||||
|  |                 if (fmts->channel_layouts[k] == fmt) { | ||||||
|  |                     fmts->channel_layouts[0]  = fmt; | ||||||
|  |                     fmts->nb_channel_layouts = 1; | ||||||
|  |                     ret = 1; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @ -663,7 +720,23 @@ static void swap_channel_layouts_on_filter(AVFilterContext *filter) | |||||||
|             int out_channels      = av_get_channel_layout_nb_channels(out_chlayout); |             int out_channels      = av_get_channel_layout_nb_channels(out_chlayout); | ||||||
|             int count_diff        = out_channels - in_channels; |             int count_diff        = out_channels - in_channels; | ||||||
|             int matched_channels, extra_channels; |             int matched_channels, extra_channels; | ||||||
|             int score = 0; |             int score = 100000; | ||||||
|  | 
 | ||||||
|  |             if (FF_LAYOUT2COUNT(in_chlayout) || FF_LAYOUT2COUNT(out_chlayout)) { | ||||||
|  |                 /* Compute score in case the input or output layout encodes
 | ||||||
|  |                    a channel count; in this case the score is not altered by | ||||||
|  |                    the computation afterwards, as in_chlayout and | ||||||
|  |                    out_chlayout have both been set to 0 */ | ||||||
|  |                 if (FF_LAYOUT2COUNT(in_chlayout)) | ||||||
|  |                     in_channels = FF_LAYOUT2COUNT(in_chlayout); | ||||||
|  |                 if (FF_LAYOUT2COUNT(out_chlayout)) | ||||||
|  |                     out_channels = FF_LAYOUT2COUNT(out_chlayout); | ||||||
|  |                 score -= 10000 + FFABS(out_channels - in_channels) + | ||||||
|  |                          (in_channels > out_channels ? 10000 : 0); | ||||||
|  |                 in_chlayout = out_chlayout = 0; | ||||||
|  |                 /* Let the remaining computation run, even if the score
 | ||||||
|  |                    value is not altered */ | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             /* channel substitution */ |             /* channel substitution */ | ||||||
|             for (k = 0; k < FF_ARRAY_ELEMS(ch_subst); k++) { |             for (k = 0; k < FF_ARRAY_ELEMS(ch_subst); k++) { | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ | |||||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include "libavutil/avassert.h" | ||||||
| #include "libavutil/channel_layout.h" | #include "libavutil/channel_layout.h" | ||||||
| #include "libavutil/common.h" | #include "libavutil/common.h" | ||||||
| #include "libavutil/eval.h" | #include "libavutil/eval.h" | ||||||
| @ -28,6 +29,8 @@ | |||||||
| #include "internal.h" | #include "internal.h" | ||||||
| #include "formats.h" | #include "formats.h" | ||||||
| 
 | 
 | ||||||
|  | #define KNOWN(l) (!FF_LAYOUT2COUNT(l)) /* for readability */ | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Add all refs from a to ret and destroy a. |  * Add all refs from a to ret and destroy a. | ||||||
|  */ |  */ | ||||||
| @ -136,21 +139,77 @@ AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a, | |||||||
|                                                  AVFilterChannelLayouts *b) |                                                  AVFilterChannelLayouts *b) | ||||||
| { | { | ||||||
|     AVFilterChannelLayouts *ret = NULL; |     AVFilterChannelLayouts *ret = NULL; | ||||||
|  |     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; | ||||||
| 
 | 
 | ||||||
|     if (a == b) return a; |     if (a == b) return a; | ||||||
| 
 | 
 | ||||||
|     if (a->nb_channel_layouts && b->nb_channel_layouts) { |     /* Put the most generic set in a, to avoid doing everything twice */ | ||||||
|         MERGE_FORMATS(ret, a, b, channel_layouts, nb_channel_layouts, |     if (a_all < b_all) { | ||||||
|                       AVFilterChannelLayouts, fail); |         FFSWAP(AVFilterChannelLayouts *, a, b); | ||||||
|     } else if (a->nb_channel_layouts) { |         FFSWAP(unsigned, a_all, b_all); | ||||||
|         MERGE_REF(a, b, channel_layouts, AVFilterChannelLayouts, fail); |     } | ||||||
|         ret = a; |     if (a_all) { | ||||||
|     } else { |         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]; | ||||||
|  |             b->nb_channel_layouts = j; | ||||||
|  |         } | ||||||
|         MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail); |         MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail); | ||||||
|         ret = b; |         return b; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     ret_max = a->nb_channel_layouts + b->nb_channel_layouts; | ||||||
|  |     if (!(ret = av_mallocz(sizeof(*ret))) || | ||||||
|  |         !(ret->channel_layouts = av_malloc(sizeof(*ret->channel_layouts) * | ||||||
|  |                                            ret_max))) | ||||||
|  |         goto fail; | ||||||
|  | 
 | ||||||
|  |     /* 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]) { | ||||||
|  |                 ret->channel_layouts[ret_nb++] = a->channel_layouts[i]; | ||||||
|  |                 a->channel_layouts[i] = b->channel_layouts[j] = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /* 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) | ||||||
|  |                     ret->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]) | ||||||
|  |                 ret->channel_layouts[ret_nb++] = a->channel_layouts[i]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ret->nb_channel_layouts = ret_nb; | ||||||
|  |     if (!ret->nb_channel_layouts) | ||||||
|  |         goto fail; | ||||||
|  |     MERGE_REF(ret, a, channel_layouts, AVFilterChannelLayouts, fail); | ||||||
|  |     MERGE_REF(ret, b, channel_layouts, AVFilterChannelLayouts, fail); | ||||||
|     return ret; |     return ret; | ||||||
|  | 
 | ||||||
| fail: | fail: | ||||||
|     if (ret) { |     if (ret) { | ||||||
|         av_freep(&ret->refs); |         av_freep(&ret->refs); | ||||||
| @ -248,17 +307,19 @@ do {                                                        \ | |||||||
|                                                             \ |                                                             \ | ||||||
|     (*f)->list = fmts;                                      \ |     (*f)->list = fmts;                                      \ | ||||||
|     (*f)->list[(*f)->nb++] = fmt;                           \ |     (*f)->list[(*f)->nb++] = fmt;                           \ | ||||||
|     return 0;                                               \ |  | ||||||
| } while (0) | } while (0) | ||||||
| 
 | 
 | ||||||
| int ff_add_format(AVFilterFormats **avff, int64_t fmt) | int ff_add_format(AVFilterFormats **avff, int64_t fmt) | ||||||
| { | { | ||||||
|     ADD_FORMAT(avff, fmt, int, formats, format_count); |     ADD_FORMAT(avff, fmt, int, formats, format_count); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) | int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) | ||||||
| { | { | ||||||
|  |     av_assert1(!(*l && (*l)->all_layouts)); | ||||||
|     ADD_FORMAT(l, channel_layout, uint64_t, channel_layouts, nb_channel_layouts); |     ADD_FORMAT(l, channel_layout, uint64_t, channel_layouts, nb_channel_layouts); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AVFilterFormats *ff_all_formats(enum AVMediaType type) | AVFilterFormats *ff_all_formats(enum AVMediaType type) | ||||||
| @ -309,6 +370,9 @@ AVFilterFormats *ff_all_samplerates(void) | |||||||
| AVFilterChannelLayouts *ff_all_channel_layouts(void) | AVFilterChannelLayouts *ff_all_channel_layouts(void) | ||||||
| { | { | ||||||
|     AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret)); |     AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret)); | ||||||
|  |     if (!ret) | ||||||
|  |         return NULL; | ||||||
|  |     ret->all_layouts = 1; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -69,14 +69,45 @@ struct AVFilterFormats { | |||||||
|     struct AVFilterFormats ***refs; ///< references to this list
 |     struct AVFilterFormats ***refs; ///< references to this list
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * A list of supported channel layouts. | ||||||
|  |  * | ||||||
|  |  * The list works the same as AVFilterFormats, except for the following | ||||||
|  |  * differences: | ||||||
|  |  * - A list with all_layouts = 1 means all channel layouts with a known | ||||||
|  |  *   disposition; nb_channel_layouts must then be 0. | ||||||
|  |  * - A list with all_counts = 1 means all channel counts, with a known or | ||||||
|  |  *   unknown disposition; nb_channel_layouts must then be 0 and all_layouts 1. | ||||||
|  |  * - The list must not contain a layout with a known disposition and a | ||||||
|  |  *   channel count with unknown disposition with the same number of channels | ||||||
|  |  *   (e.g. AV_CH_LAYOUT_STEREO and FF_COUNT2LAYOUT(2). | ||||||
|  |  */ | ||||||
| typedef struct AVFilterChannelLayouts { | typedef struct AVFilterChannelLayouts { | ||||||
|     uint64_t *channel_layouts;  ///< list of channel layouts
 |     uint64_t *channel_layouts;  ///< list of channel layouts
 | ||||||
|     int    nb_channel_layouts;  ///< number of channel layouts
 |     int    nb_channel_layouts;  ///< number of channel layouts
 | ||||||
|  |     char all_layouts;           ///< accept any known channel layout
 | ||||||
|  |     char all_counts;            ///< accept any channel layout or count
 | ||||||
| 
 | 
 | ||||||
|     unsigned refcount;          ///< number of references to this list
 |     unsigned refcount;          ///< number of references to this list
 | ||||||
|     struct AVFilterChannelLayouts ***refs; ///< references to this list
 |     struct AVFilterChannelLayouts ***refs; ///< references to this list
 | ||||||
| } AVFilterChannelLayouts; | } AVFilterChannelLayouts; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Encode a channel count as a channel layout. | ||||||
|  |  * FF_COUNT2LAYOUT(c) means any channel layout with c channels, with a known | ||||||
|  |  * or unknown disposition. | ||||||
|  |  * The result is only valid inside AVFilterChannelLayouts and immediately | ||||||
|  |  * related functions. | ||||||
|  |  */ | ||||||
|  | #define FF_COUNT2LAYOUT(c) (0x8000000000000000ULL | (c)) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Decode a channel count encoded as a channel layout. | ||||||
|  |  * Return 0 if the channel layout was a real one. | ||||||
|  |  */ | ||||||
|  | #define FF_LAYOUT2COUNT(l) (((l) & 0x8000000000000000ULL) ? \ | ||||||
|  |                            (int)((l) & 0x7FFFFFFF) : 0) | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Return a channel layouts/samplerates list which contains the intersection of |  * 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 |  * the layouts/samplerates of a and b. Also, all the references of a, all the | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user