[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