if (!link) continue;
+ link->current_pts = AV_NOPTS_VALUE;
+
switch (link->init_state) {
case AVLINK_INIT:
continue;
return min;
}
+static void update_link_current_pts(AVFilterLink *link)
+{
+ if (link->cur_buf->pts == AV_NOPTS_VALUE)
+ return;
+ link->current_pts = link->cur_buf->pts; /* TODO use duration */
+ if (link->graph && link->age_index >= 0)
+ ff_avfilter_graph_update_heap(link->graph, link);
+}
+
/* XXX: should we do the duplicating of the picture ref here, instead of
* forcing the source filter to do it? */
void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
}
start_frame(link, link->cur_buf);
+ update_link_current_pts(link);
}
void avfilter_end_frame(AVFilterLink *link)
link->cur_buf = samplesref;
filter_samples(link, link->cur_buf);
+ update_link_current_pts(link);
}
#define MAX_REGISTERED_AVFILTERS_NB 128
#include <string.h>
#include "libavutil/audioconvert.h"
+#include "libavutil/avassert.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "avfiltergraph.h"
return 0;
}
-static void ff_avfilter_graph_config_pointers(AVFilterGraph *graph,
- AVClass *log_ctx)
+static int ff_avfilter_graph_config_pointers(AVFilterGraph *graph,
+ AVClass *log_ctx)
{
- unsigned i, j;;
+ unsigned i, j;
+ int sink_links_count = 0, n = 0;
AVFilterContext *f;
+ AVFilterLink **sinks;
for (i = 0; i < graph->filter_count; i++) {
f = graph->filters[i];
- for (j = 0; j < f->input_count; j++)
- f->inputs[j]->graph = graph;
- for (j = 0; j < f->output_count; j++)
- f->outputs[j]->graph = graph;
+ for (j = 0; j < f->input_count; j++) {
+ f->inputs[j]->graph = graph;
+ f->inputs[j]->age_index = -1;
+ }
+ for (j = 0; j < f->output_count; j++) {
+ f->outputs[j]->graph = graph;
+ f->outputs[j]->age_index= -1;
+ }
+ if (!f->output_count) {
+ if (f->input_count > INT_MAX - sink_links_count)
+ return AVERROR(EINVAL);
+ sink_links_count += f->input_count;
+ }
}
+ sinks = av_calloc(sink_links_count, sizeof(*sinks));
+ if (!sinks)
+ return AVERROR(ENOMEM);
+ for (i = 0; i < graph->filter_count; i++) {
+ f = graph->filters[i];
+ if (!f->output_count) {
+ for (j = 0; j < f->input_count; j++) {
+ sinks[n] = f->inputs[j];
+ f->inputs[j]->age_index = n++;
+ }
+ }
+ }
+ av_assert0(n == sink_links_count);
+ graph->sink_links = sinks;
+ graph->sink_links_count = sink_links_count;
+ return 0;
}
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx)
return ret;
if ((ret = ff_avfilter_graph_config_links(graphctx, log_ctx)))
return ret;
- ff_avfilter_graph_config_pointers(graphctx, log_ctx);
+ if ((ret = ff_avfilter_graph_config_pointers(graphctx, log_ctx)))
+ return ret;
return 0;
}
return 0;
}
+
+static void heap_bubble_up(AVFilterGraph *graph,
+ AVFilterLink *link, int index)
+{
+ AVFilterLink **links = graph->sink_links;
+
+ while (index) {
+ int parent = (index - 1) >> 1;
+ if (links[parent]->current_pts >= link->current_pts)
+ break;
+ links[index] = links[parent];
+ links[index]->age_index = index;
+ index = parent;
+ }
+ links[index] = link;
+ link->age_index = index;
+}
+
+static void heap_bubble_down(AVFilterGraph *graph,
+ AVFilterLink *link, int index)
+{
+ AVFilterLink **links = graph->sink_links;
+
+ while (1) {
+ int child = 2 * index + 1;
+ if (child >= graph->sink_links_count)
+ break;
+ if (child + 1 < graph->sink_links_count &&
+ links[child + 1]->current_pts < links[child]->current_pts)
+ child++;
+ if (link->current_pts < links[child]->current_pts)
+ break;
+ links[index] = links[child];
+ links[index]->age_index = index;
+ index = child;
+ }
+ links[index] = link;
+ link->age_index = index;
+}
+
+void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link)
+{
+ heap_bubble_up (graph, link, link->age_index);
+ heap_bubble_down(graph, link, link->age_index);
+}
+
+
+int avfilter_graph_request_oldest(AVFilterGraph *graph)
+{
+ while (graph->sink_links_count) {
+ AVFilterLink *oldest = graph->sink_links[0];
+ int r = avfilter_request_frame(oldest);
+ if (r != AVERROR_EOF)
+ return r;
+ /* 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],
+ oldest->age_index);
+ oldest->age_index = -1;
+ }
+ return AVERROR_EOF;
+}
AVFilterContext **filters;
char *scale_sws_opts; ///< sws options to use for the auto-inserted scale filters
+
+ /**
+ * Private fields
+ *
+ * The following fields are for internal use only.
+ * Their type, offset, number and semantic can change without notice.
+ */
+
+ AVFilterLink **sink_links;
+ int sink_links_count;
} AVFilterGraph;
/**
*/
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options);
+/**
+ * Request a frame on the oldest sink link.
+ *
+ * If the request returns AVERROR_EOF, try the next.
+ *
+ * Note that this function is not meant to be the sole scheduling mechanism
+ * of a filtergraph, only a convenience function to help drain a filtergraph
+ * in a balanced way under normal circumstances.
+ *
+ * @return the return value of avfilter_request_frame,
+ * or AVERROR_EOF of all links returned AVERROR_EOF.
+ */
+int avfilter_graph_request_oldest(AVFilterGraph *graph);
+
#endif /* AVFILTER_AVFILTERGRAPH_H */