[FFmpeg-devel] [PATCH 02/10] lavfi: support unknown channel layouts.

Nicolas George nicolas.george at normalesup.org
Sun Dec 30 18:13:42 CET 2012


Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 libavfilter/audio.c         |    2 +-
 libavfilter/avcodec.c       |    3 --
 libavfilter/avfiltergraph.c |   73 ++++++++++++++++++++++++++++++++++-----
 libavfilter/formats.c       |   80 ++++++++++++++++++++++++++++++++++++++-----
 libavfilter/formats.h       |   24 +++++++++++++
 5 files changed, 162 insertions(+), 20 deletions(-)

diff --git a/libavfilter/audio.c b/libavfilter/audio.c
index c5ee09f..227204f 100644
--- a/libavfilter/audio.c
+++ b/libavfilter/audio.c
@@ -44,7 +44,7 @@ AVFilterBufferRef *ff_default_get_audio_buffer(AVFilterLink *link, int perms,
     AVFilterBufferRef *samplesref = NULL;
     uint8_t **data;
     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 linesize;
     int full_perms = AV_PERM_READ | AV_PERM_WRITE | AV_PERM_PRESERVE |
diff --git a/libavfilter/avcodec.c b/libavfilter/avcodec.c
index 10a2eff..512a3d4 100644
--- a/libavfilter/avcodec.c
+++ b/libavfilter/avcodec.c
@@ -96,9 +96,6 @@ AVFilterBufferRef *avfilter_get_audio_buffer_ref_from_frame(const AVFrame *frame
     int channels = av_frame_get_channels(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)) {
         av_log(0, AV_LOG_ERROR, "Layout indicates a differnt number of channels than actually present\n");
         return NULL;
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index bd772a8..0998de4 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -467,7 +467,8 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref)
         link->in_samplerates->format_count = 1;
         link->sample_rate = link->in_samplerates->formats[0];
 
-        if (!link->in_channel_layouts->nb_channel_layouts) {
+        if (!link->in_channel_layouts->nb_channel_layouts ||
+            link->in_channel_layouts->channel_layouts[0] == AV_CH_LAYOUT_UNKNOWN) {
             av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for"
                    "the link between filters %s and %s.\n", link->src->name,
                    link->dst->name);
@@ -475,7 +476,12 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref)
         }
         link->in_channel_layouts->nb_channel_layouts = 1;
         link->channel_layout = link->in_channel_layouts->channel_layouts[0];
-        link->channels = av_get_channel_layout_nb_channels(link->channel_layout);
+        if (link->channel_layout & AV_CH_LAYOUT_UNKNOWN) {
+            link->channels = link->channel_layout & ~AV_CH_LAYOUT_UNKNOWN;
+            link->channel_layout = 0;
+        } else {
+            link->channels = av_get_channel_layout_nb_channels(link->channel_layout);
+        }
     }
 
     ff_formats_unref(&link->in_formats);
@@ -531,8 +537,45 @@ static int reduce_formats_on_filter(AVFilterContext *filter)
                    format_count, ff_add_format);
     REDUCE_FORMATS(int,      AVFilterFormats,        samplerates,     formats,
                    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 *link = filter->inputs[i];
+        uint64_t fmt;
+
+        if (!link->out_channel_layouts ||
+            link->out_channel_layouts->nb_channel_layouts != 1)
+            continue;
+        fmt = link->out_channel_layouts->channel_layouts[0];
+        if (fmt == AV_CH_LAYOUT_UNKNOWN)
+            continue;
+
+        for (j = 0; j < filter->nb_outputs; j++) {
+            AVFilterLink *out_link = filter->outputs[j];
+            AVFilterChannelLayouts *fmts;
+
+            fmts = out_link->in_channel_layouts;
+            if (link->type != out_link->type ||
+                (fmts->nb_channel_layouts == 1 &&
+                 fmts->channel_layouts[0] != AV_CH_LAYOUT_UNKNOWN))
+                continue;
+
+            if (fmts->nb_channel_layouts ?
+                fmts->channel_layouts[0] == AV_CH_LAYOUT_UNKNOWN :
+                !(fmt & AV_CH_LAYOUT_UNKNOWN)) {
+                ff_add_channel_layout(&out_link->in_channel_layouts, fmt);
+                break;
+            }
+
+            for (k = 0; k < out_link->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;
 }
@@ -660,7 +703,18 @@ static void swap_channel_layouts_on_filter(AVFilterContext *filter)
             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;
+            int score = 100000;
+
+            if ((in_chlayout | out_chlayout) & AV_CH_LAYOUT_UNKNOWN) {
+                if (in_chlayout & AV_CH_LAYOUT_UNKNOWN)
+                    in_channels = in_chlayout & ~AV_CH_LAYOUT_UNKNOWN;
+                if (out_chlayout & AV_CH_LAYOUT_UNKNOWN)
+                    out_channels = out_chlayout & ~AV_CH_LAYOUT_UNKNOWN;
+                score -= 10000 + FFABS(out_channels - in_channels) +
+                         (in_channels > out_channels ? 10000 : 0);
+                /* let the remaining computation run and get 0 */
+                in_chlayout = out_chlayout = 0;
+            }
 
             /* channel substitution */
             for (k = 0; k < FF_ARRAY_ELEMS(ch_subst); k++) {
@@ -792,7 +846,8 @@ static int pick_formats(AVFilterGraph *graph)
             if (filter->nb_inputs){
                 for (j = 0; j < filter->nb_inputs; j++){
                     if(filter->inputs[j]->in_formats && filter->inputs[j]->in_formats->format_count == 1) {
-                        pick_format(filter->inputs[j], NULL);
+                        if ((ret = pick_format(filter->inputs[j], NULL)) < 0)
+                            return ret;
                         change = 1;
                     }
                 }
@@ -800,7 +855,8 @@ static int pick_formats(AVFilterGraph *graph)
             if (filter->nb_outputs){
                 for (j = 0; j < filter->nb_outputs; j++){
                     if(filter->outputs[j]->in_formats && filter->outputs[j]->in_formats->format_count == 1) {
-                        pick_format(filter->outputs[j], NULL);
+                        if ((ret = pick_format(filter->outputs[j], NULL)) < 0)
+                            return ret;
                         change = 1;
                     }
                 }
@@ -808,7 +864,8 @@ static int pick_formats(AVFilterGraph *graph)
             if (filter->nb_inputs && filter->nb_outputs && filter->inputs[0]->format>=0) {
                 for (j = 0; j < filter->nb_outputs; j++) {
                     if(filter->outputs[j]->format<0) {
-                        pick_format(filter->outputs[j], filter->inputs[0]);
+                        if ((ret = pick_format(filter->outputs[j], filter->inputs[0])) < 0)
+                            return ret;
                         change = 1;
                     }
                 }
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index ea9a184..0e4ff2b 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -132,25 +132,89 @@ fail:
     return NULL;
 }
 
+static int count_known_layouts(AVFilterChannelLayouts *l)
+{
+    int r = 0;
+
+    while (r < l->nb_channel_layouts &&
+           !(l->channel_layouts[r] & AV_CH_LAYOUT_UNKNOWN))
+        r++;
+    return r;
+}
+
 AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a,
                                                  AVFilterChannelLayouts *b)
 {
     AVFilterChannelLayouts *ret = NULL;
+    unsigned a_all = !a->nb_channel_layouts ? 1 :
+                     a->channel_layouts[0] == AV_CH_LAYOUT_UNKNOWN ? 2 : 0;
+    unsigned b_all = !b->nb_channel_layouts ? 1 :
+                     b->channel_layouts[0] == AV_CH_LAYOUT_UNKNOWN ? 2 : 0;
+    int a_known, b_known, ret_max, ret_nb = 0, i, j, ch;
 
     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 {
+    if (a_all < b_all) {
+        FFSWAP(AVFilterChannelLayouts *, a, b);
+        FFSWAP(unsigned, a_all, b_all);
+    }
+    a_known = count_known_layouts(a);
+    b_known = count_known_layouts(b);
+    if (a_all) {
+        if (a_all == 1 && !b_all) {
+            /* keep only known layouts in b; works also for b_all = 1 */
+            if (b->nb_channel_layouts && !b_known)
+                goto fail;
+            b->nb_channel_layouts = b_known;
+        }
         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] inter b[known] */
+    for (i = 0; i < a_known; i++) {
+        for (j = 0; j < b_known; 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;
+            }
+        }
+    }
+    /* a[known] inter b[generic] */
+    for (i = 0; i < a_known; i++) {
+        if (!(ch = av_get_channel_layout_nb_channels(a->channel_layouts[i])))
+            continue; /* already taken */
+        for (j = b_known; j < b->nb_channel_layouts; j++)
+            if (b->channel_layouts[j] == AV_CH_LAYOUT_UNKNOWN | ch)
+                ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
+    }
+    /* a[generic] inter b[known] */
+    for (j = 0; j < b_known; j++) {
+        if (!(ch = av_get_channel_layout_nb_channels(b->channel_layouts[j])))
+            continue; /* already taken */
+        for (i = a_known; i < a->nb_channel_layouts; i++)
+            if (a->channel_layouts[i] == AV_CH_LAYOUT_UNKNOWN | ch)
+                ret->channel_layouts[ret_nb++] = b->channel_layouts[j];
+    }
+    /* a[generic] inter b[generic] */
+    for (i = a_known; i < a->nb_channel_layouts; i++)
+        for (j = b_known; 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;
+
 fail:
     if (ret) {
         av_freep(&ret->refs);
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index c5a4e3d..23fc04c 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -69,6 +69,20 @@ struct AVFilterFormats {
     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:
+ * - an empty list means all channel layouts with known disposition;
+ * - a list consisting of only AV_CH_LAYOUT_UNKNOWN means all channel
+ *   layouts or channel counts;
+ * - all layouts with known disposition must come before all counts with
+ *   unknown disposition;
+ * - the list must not contain a layout with known disposition and a channel
+ *   count with unknown disposition with the same number of channels (e.g.
+ *   AV_CH_LAYOUT_STEREO and AV_CH_LAYOUT_UNKNOWN|2).
+ */
 typedef struct AVFilterChannelLayouts {
     uint64_t *channel_layouts;  ///< list of channel layouts
     int    nb_channel_layouts;  ///< number of channel layouts
@@ -78,6 +92,16 @@ typedef struct AVFilterChannelLayouts {
 } AVFilterChannelLayouts;
 
 /**
+ * The channel layout codes only a channel count.
+ *
+ * "AV_CH_LAYOUT_UNKNOWN | n" means a n-channels stream with any layout,
+ * known or unknown.
+ * This flag is only permitted inside the AVFilterChannelLayouts lists and
+ * immediately related functions.
+ */
+#define AV_CH_LAYOUT_UNKNOWN         0x8000000000000000ULL
+
+/**
  * 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.
-- 
1.7.10.4



More information about the ffmpeg-devel mailing list