#include "libavutil/audioconvert.h"
#include "libavutil/avassert.h"
#include "libavutil/pixdesc.h"
+#include "libavcodec/avcodec.h" // avcodec_find_best_pix_fmt2()
#include "avfilter.h"
#include "avfiltergraph.h"
#include "formats.h"
#include "internal.h"
#include "libavutil/audioconvert.h"
+#include "libavutil/avassert.h"
+#include "libavutil/common.h"
#include "libavutil/log.h"
static const AVClass filtergraph_class = {
AVFilterGraph *ret = av_mallocz(sizeof(AVFilterGraph));
if (!ret)
return NULL;
-#if FF_API_GRAPH_AVCLASS
ret->av_class = &filtergraph_class;
-#endif
return ret;
}
if ((ret = ctx->filter->query_formats(ctx)) < 0)
return ret;
- formats = avfilter_make_all_formats(type);
+ formats = ff_all_formats(type);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
break;
case AVMEDIA_TYPE_AUDIO:
if (!(filter = avfilter_get_by_name("aresample"))) {
- av_log(log_ctx, AV_LOG_ERROR, "'resample' filter "
+ av_log(log_ctx, AV_LOG_ERROR, "'aresample' filter "
"not present, cannot convert audio formats.\n");
return AVERROR(EINVAL);
}
enum PixelFormat p = link->in_formats->formats[i];
best= avcodec_find_best_pix_fmt2(best, p, ref->format, has_alpha, NULL);
}
+ av_log(link->src,AV_LOG_DEBUG, "picking %s out of %d ref:%s alpha:%d\n",
+ av_get_pix_fmt_name(best), link->in_formats->format_count,
+ av_get_pix_fmt_name(ref->format), has_alpha);
link->in_formats->formats[0] = best;
}
}
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];
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],
for (i = 0; i < filter->nb_outputs; i++) {
AVFilterLink *outlink = filter->outputs[i];
- int best_idx, best_score = INT_MIN;
+ int best_idx = -1, best_score = INT_MIN;
if (outlink->type != AVMEDIA_TYPE_AUDIO ||
outlink->in_formats->format_count < 2)
best_idx = j;
}
}
+ av_assert0(best_idx >= 0);
FFSWAP(int, outlink->in_formats->formats[0],
outlink->in_formats->formats[best_idx]);
}
return 0;
}
+static int graph_insert_fifos(AVFilterGraph *graph, AVClass *log_ctx)
+{
+ AVFilterContext *f;
+ int i, j, ret;
+ int fifo_count = 0;
+
+ for (i = 0; i < graph->filter_count; i++) {
+ f = graph->filters[i];
+
+ for (j = 0; j < f->nb_inputs; j++) {
+ AVFilterLink *link = f->inputs[j];
+ AVFilterContext *fifo_ctx;
+ AVFilter *fifo;
+ char name[32];
+
+ if (!link->dstpad->needs_fifo)
+ continue;
+
+ fifo = f->inputs[j]->type == AVMEDIA_TYPE_VIDEO ?
+ avfilter_get_by_name("fifo") :
+ avfilter_get_by_name("afifo");
+
+ snprintf(name, sizeof(name), "auto-inserted fifo %d", fifo_count++);
+
+ ret = avfilter_graph_create_filter(&fifo_ctx, fifo, name, NULL,
+ NULL, graph);
+ if (ret < 0)
+ return ret;
+
+ ret = avfilter_insert_filter(link, fifo_ctx, 0, 0);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx)
{
int ret;
if ((ret = graph_check_validity(graphctx, log_ctx)))
return ret;
+ if ((ret = graph_insert_fifos(graphctx, log_ctx)) < 0)
+ return ret;
if ((ret = graph_config_formats(graphctx, log_ctx)))
return ret;
if ((ret = graph_config_links(graphctx, log_ctx)))
{
while (graph->sink_links_count) {
AVFilterLink *oldest = graph->sink_links[0];
- int r = avfilter_request_frame(oldest);
+ int r = ff_request_frame(oldest);
if (r != AVERROR_EOF)
return r;
+ av_log(oldest->dst, AV_LOG_DEBUG, "EOF on sink link %s:%s.\n",
+ oldest->dst ? oldest->dst->name : "unknown",
+ oldest->dstpad ? oldest->dstpad->name : "unknown");
/* EOF: remove the link from the heap */
if (oldest->age_index < --graph->sink_links_count)
heap_bubble_down(graph, graph->sink_links[graph->sink_links_count],