aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustin Ruggles <justin.ruggles@gmail.com>2012-07-13 20:22:08 -0400
committerJustin Ruggles <justin.ruggles@gmail.com>2012-07-27 14:15:33 -0400
commit743f07062a89a484e1be796da3eaa5e5047aee7d (patch)
treefa42ba76bebe41752f67cebb8cb79c8b5bb3caa8
parent81f548de572a324c771c094291378d9bcc184af3 (diff)
downloadffmpeg-743f07062a89a484e1be796da3eaa5e5047aee7d.tar.gz
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.
-rw-r--r--libavfilter/avfiltergraph.c83
1 files changed, 72 insertions, 11 deletions
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index c147c3b781..a0aa807b9c 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -424,11 +424,44 @@ static void swap_samplerates(AVFilterGraph *graph)
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)
{
AVFilterLink *link = NULL;
- uint64_t chlayout;
- int i, j;
+ int i, j, k;
for (i = 0; i < filter->nb_inputs; i++) {
link = filter->inputs[i];
@@ -440,27 +473,55 @@ static void swap_channel_layouts_on_filter(AVFilterContext *filter)
if (i == filter->nb_inputs)
return;
- chlayout = link->out_channel_layouts->channel_layouts[0];
-
for (i = 0; i < filter->nb_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 ||
outlink->in_channel_layouts->nb_channel_layouts < 2)
continue;
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];
- int matched_channels = av_get_channel_layout_nb_channels(chlayout &
- out_chlayout);
- int extra_channels = av_get_channel_layout_nb_channels(out_chlayout &
- (~chlayout));
- int score = matched_channels - extra_channels;
+ int in_channels = av_get_channel_layout_nb_channels(in_chlayout);
+ int out_channels = av_get_channel_layout_nb_channels(out_chlayout);
+ int count_diff = out_channels - in_channels;
+ int matched_channels, extra_channels;
+ int score = 0;
+
+ /* 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;
+ }
+ }
- if (score > best_score) {
+ /* 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_idx = j;
+ best_count_diff = count_diff;
}
}
FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0],