lavfi: better channel layout negotiation
Allow substitution of channel pairs in the input for nearby channel pairs in the output in order to get a closer match. Also weigh LFE channel mismatch differently to favor matching the same layout without LFE over one less channel with LFE.
This commit is contained in:
		
							parent
							
								
									81f548de57
								
							
						
					
					
						commit
						743f07062a
					
				| @ -424,11 +424,44 @@ static void swap_samplerates(AVFilterGraph *graph) | |||||||
|         swap_samplerates_on_filter(graph->filters[i]); |         swap_samplerates_on_filter(graph->filters[i]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define CH_CENTER_PAIR (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER) | ||||||
|  | #define CH_FRONT_PAIR  (AV_CH_FRONT_LEFT           | AV_CH_FRONT_RIGHT) | ||||||
|  | #define CH_STEREO_PAIR (AV_CH_STEREO_LEFT          | AV_CH_STEREO_RIGHT) | ||||||
|  | #define CH_WIDE_PAIR   (AV_CH_WIDE_LEFT            | AV_CH_WIDE_RIGHT) | ||||||
|  | #define CH_SIDE_PAIR   (AV_CH_SIDE_LEFT            | AV_CH_SIDE_RIGHT) | ||||||
|  | #define CH_DIRECT_PAIR (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT) | ||||||
|  | #define CH_BACK_PAIR   (AV_CH_BACK_LEFT            | AV_CH_BACK_RIGHT) | ||||||
|  | 
 | ||||||
|  | /* allowable substitutions for channel pairs when comparing layouts,
 | ||||||
|  |  * ordered by priority for both values */ | ||||||
|  | static const uint64_t ch_subst[][2] = { | ||||||
|  |     { CH_FRONT_PAIR,      CH_CENTER_PAIR     }, | ||||||
|  |     { CH_FRONT_PAIR,      CH_WIDE_PAIR       }, | ||||||
|  |     { CH_FRONT_PAIR,      AV_CH_FRONT_CENTER }, | ||||||
|  |     { CH_CENTER_PAIR,     CH_FRONT_PAIR      }, | ||||||
|  |     { CH_CENTER_PAIR,     CH_WIDE_PAIR       }, | ||||||
|  |     { CH_CENTER_PAIR,     AV_CH_FRONT_CENTER }, | ||||||
|  |     { CH_WIDE_PAIR,       CH_FRONT_PAIR      }, | ||||||
|  |     { CH_WIDE_PAIR,       CH_CENTER_PAIR     }, | ||||||
|  |     { CH_WIDE_PAIR,       AV_CH_FRONT_CENTER }, | ||||||
|  |     { AV_CH_FRONT_CENTER, CH_FRONT_PAIR      }, | ||||||
|  |     { AV_CH_FRONT_CENTER, CH_CENTER_PAIR     }, | ||||||
|  |     { AV_CH_FRONT_CENTER, CH_WIDE_PAIR       }, | ||||||
|  |     { CH_SIDE_PAIR,       CH_DIRECT_PAIR     }, | ||||||
|  |     { CH_SIDE_PAIR,       CH_BACK_PAIR       }, | ||||||
|  |     { CH_SIDE_PAIR,       AV_CH_BACK_CENTER  }, | ||||||
|  |     { CH_BACK_PAIR,       CH_DIRECT_PAIR     }, | ||||||
|  |     { CH_BACK_PAIR,       CH_SIDE_PAIR       }, | ||||||
|  |     { CH_BACK_PAIR,       AV_CH_BACK_CENTER  }, | ||||||
|  |     { AV_CH_BACK_CENTER,  CH_BACK_PAIR       }, | ||||||
|  |     { AV_CH_BACK_CENTER,  CH_DIRECT_PAIR     }, | ||||||
|  |     { AV_CH_BACK_CENTER,  CH_SIDE_PAIR       }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static void swap_channel_layouts_on_filter(AVFilterContext *filter) | static void swap_channel_layouts_on_filter(AVFilterContext *filter) | ||||||
| { | { | ||||||
|     AVFilterLink *link = NULL; |     AVFilterLink *link = NULL; | ||||||
|     uint64_t chlayout; |     int i, j, k; | ||||||
|     int i, j; |  | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < filter->nb_inputs; i++) { |     for (i = 0; i < filter->nb_inputs; i++) { | ||||||
|         link = filter->inputs[i]; |         link = filter->inputs[i]; | ||||||
| @ -440,27 +473,55 @@ static void swap_channel_layouts_on_filter(AVFilterContext *filter) | |||||||
|     if (i == filter->nb_inputs) |     if (i == filter->nb_inputs) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     chlayout = link->out_channel_layouts->channel_layouts[0]; |  | ||||||
| 
 |  | ||||||
|     for (i = 0; i < filter->nb_outputs; i++) { |     for (i = 0; i < filter->nb_outputs; i++) { | ||||||
|         AVFilterLink *outlink = filter->outputs[i]; |         AVFilterLink *outlink = filter->outputs[i]; | ||||||
|         int best_idx, best_score = INT_MIN; |         int best_idx, best_score = INT_MIN, best_count_diff = INT_MAX; | ||||||
| 
 | 
 | ||||||
|         if (outlink->type != AVMEDIA_TYPE_AUDIO || |         if (outlink->type != AVMEDIA_TYPE_AUDIO || | ||||||
|             outlink->in_channel_layouts->nb_channel_layouts < 2) |             outlink->in_channel_layouts->nb_channel_layouts < 2) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
|         for (j = 0; j < outlink->in_channel_layouts->nb_channel_layouts; j++) { |         for (j = 0; j < outlink->in_channel_layouts->nb_channel_layouts; j++) { | ||||||
|  |             uint64_t  in_chlayout = link->out_channel_layouts->channel_layouts[0]; | ||||||
|             uint64_t out_chlayout = outlink->in_channel_layouts->channel_layouts[j]; |             uint64_t out_chlayout = outlink->in_channel_layouts->channel_layouts[j]; | ||||||
|             int matched_channels  = av_get_channel_layout_nb_channels(chlayout & |             int  in_channels      = av_get_channel_layout_nb_channels(in_chlayout); | ||||||
|                                                                       out_chlayout); |             int out_channels      = av_get_channel_layout_nb_channels(out_chlayout); | ||||||
|             int extra_channels     = av_get_channel_layout_nb_channels(out_chlayout & |             int count_diff        = out_channels - in_channels; | ||||||
|                                                                        (~chlayout)); |             int matched_channels, extra_channels; | ||||||
|             int score = matched_channels - extra_channels; |             int score = 0; | ||||||
| 
 | 
 | ||||||
|             if (score > best_score) { |             /* channel substitution */ | ||||||
|  |             for (k = 0; k < FF_ARRAY_ELEMS(ch_subst); k++) { | ||||||
|  |                 uint64_t cmp0 = ch_subst[k][0]; | ||||||
|  |                 uint64_t cmp1 = ch_subst[k][1]; | ||||||
|  |                 if (( in_chlayout & cmp0) && (!(out_chlayout & cmp0)) && | ||||||
|  |                     (out_chlayout & cmp1) && (!( in_chlayout & cmp1))) { | ||||||
|  |                     in_chlayout  &= ~cmp0; | ||||||
|  |                     out_chlayout &= ~cmp1; | ||||||
|  |                     /* add score for channel match, minus a deduction for
 | ||||||
|  |                        having to do the substitution */ | ||||||
|  |                     score += 10 * av_get_channel_layout_nb_channels(cmp1) - 2; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             /* no penalty for LFE channel mismatch */ | ||||||
|  |             if ( (in_chlayout & AV_CH_LOW_FREQUENCY) && | ||||||
|  |                 (out_chlayout & AV_CH_LOW_FREQUENCY)) | ||||||
|  |                 score += 10; | ||||||
|  |             in_chlayout  &= ~AV_CH_LOW_FREQUENCY; | ||||||
|  |             out_chlayout &= ~AV_CH_LOW_FREQUENCY; | ||||||
|  | 
 | ||||||
|  |             matched_channels = av_get_channel_layout_nb_channels(in_chlayout & | ||||||
|  |                                                                  out_chlayout); | ||||||
|  |             extra_channels   = av_get_channel_layout_nb_channels(out_chlayout & | ||||||
|  |                                                                  (~in_chlayout)); | ||||||
|  |             score += 10 * matched_channels - 5 * extra_channels; | ||||||
|  | 
 | ||||||
|  |             if (score > best_score || | ||||||
|  |                 (count_diff < best_count_diff && score == best_score)) { | ||||||
|                 best_score = score; |                 best_score = score; | ||||||
|                 best_idx   = j; |                 best_idx   = j; | ||||||
|  |                 best_count_diff = count_diff; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0], |         FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0], | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user