Keep a heap of all sink links ordered by timestamps.
Nicolas George authored on 2012/04/18 18:02:22... | ... |
@@ -278,6 +278,8 @@ int avfilter_config_links(AVFilterContext *filter) |
278 | 278 |
|
279 | 279 |
if (!link) continue; |
280 | 280 |
|
281 |
+ link->current_pts = AV_NOPTS_VALUE; |
|
282 |
+ |
|
281 | 283 |
switch (link->init_state) { |
282 | 284 |
case AVLINK_INIT: |
283 | 285 |
continue; |
... | ... |
@@ -568,6 +570,15 @@ int avfilter_poll_frame(AVFilterLink *link) |
568 | 568 |
return min; |
569 | 569 |
} |
570 | 570 |
|
571 |
+static void update_link_current_pts(AVFilterLink *link) |
|
572 |
+{ |
|
573 |
+ if (link->cur_buf->pts == AV_NOPTS_VALUE) |
|
574 |
+ return; |
|
575 |
+ link->current_pts = link->cur_buf->pts; /* TODO use duration */ |
|
576 |
+ if (link->graph && link->age_index >= 0) |
|
577 |
+ ff_avfilter_graph_update_heap(link->graph, link); |
|
578 |
+} |
|
579 |
+ |
|
571 | 580 |
/* XXX: should we do the duplicating of the picture ref here, instead of |
572 | 581 |
* forcing the source filter to do it? */ |
573 | 582 |
void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) |
... | ... |
@@ -608,6 +619,7 @@ void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) |
608 | 608 |
} |
609 | 609 |
|
610 | 610 |
start_frame(link, link->cur_buf); |
611 |
+ update_link_current_pts(link); |
|
611 | 612 |
} |
612 | 613 |
|
613 | 614 |
void avfilter_end_frame(AVFilterLink *link) |
... | ... |
@@ -712,6 +724,7 @@ void avfilter_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) |
712 | 712 |
link->cur_buf = samplesref; |
713 | 713 |
|
714 | 714 |
filter_samples(link, link->cur_buf); |
715 |
+ update_link_current_pts(link); |
|
715 | 716 |
} |
716 | 717 |
|
717 | 718 |
#define MAX_REGISTERED_AVFILTERS_NB 128 |
... | ... |
@@ -696,6 +696,23 @@ struct AVFilterLink { |
696 | 696 |
*/ |
697 | 697 |
struct AVFilterGraph *graph; |
698 | 698 |
|
699 |
+ /** |
|
700 |
+ * Current timestamp of the link, as defined by the most recent |
|
701 |
+ * frame(s), in AV_TIME_BASE units. |
|
702 |
+ */ |
|
703 |
+ int64_t current_pts; |
|
704 |
+ |
|
705 |
+ /** |
|
706 |
+ * Private fields |
|
707 |
+ * |
|
708 |
+ * The following fields are for internal use only. |
|
709 |
+ * Their type, offset, number and semantic can change without notice. |
|
710 |
+ */ |
|
711 |
+ |
|
712 |
+ /** |
|
713 |
+ * Index in the age array. |
|
714 |
+ */ |
|
715 |
+ int age_index; |
|
699 | 716 |
}; |
700 | 717 |
|
701 | 718 |
/** |
... | ... |
@@ -24,6 +24,7 @@ |
24 | 24 |
#include <string.h> |
25 | 25 |
|
26 | 26 |
#include "libavutil/audioconvert.h" |
27 |
+#include "libavutil/avassert.h" |
|
27 | 28 |
#include "libavutil/pixdesc.h" |
28 | 29 |
#include "avfilter.h" |
29 | 30 |
#include "avfiltergraph.h" |
... | ... |
@@ -374,19 +375,46 @@ int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) |
374 | 374 |
return 0; |
375 | 375 |
} |
376 | 376 |
|
377 |
-static void ff_avfilter_graph_config_pointers(AVFilterGraph *graph, |
|
378 |
- AVClass *log_ctx) |
|
377 |
+static int ff_avfilter_graph_config_pointers(AVFilterGraph *graph, |
|
378 |
+ AVClass *log_ctx) |
|
379 | 379 |
{ |
380 |
- unsigned i, j;; |
|
380 |
+ unsigned i, j; |
|
381 |
+ int sink_links_count = 0, n = 0; |
|
381 | 382 |
AVFilterContext *f; |
383 |
+ AVFilterLink **sinks; |
|
382 | 384 |
|
383 | 385 |
for (i = 0; i < graph->filter_count; i++) { |
384 | 386 |
f = graph->filters[i]; |
385 |
- for (j = 0; j < f->input_count; j++) |
|
386 |
- f->inputs[j]->graph = graph; |
|
387 |
- for (j = 0; j < f->output_count; j++) |
|
388 |
- f->outputs[j]->graph = graph; |
|
387 |
+ for (j = 0; j < f->input_count; j++) { |
|
388 |
+ f->inputs[j]->graph = graph; |
|
389 |
+ f->inputs[j]->age_index = -1; |
|
390 |
+ } |
|
391 |
+ for (j = 0; j < f->output_count; j++) { |
|
392 |
+ f->outputs[j]->graph = graph; |
|
393 |
+ f->outputs[j]->age_index= -1; |
|
394 |
+ } |
|
395 |
+ if (!f->output_count) { |
|
396 |
+ if (f->input_count > INT_MAX - sink_links_count) |
|
397 |
+ return AVERROR(EINVAL); |
|
398 |
+ sink_links_count += f->input_count; |
|
399 |
+ } |
|
389 | 400 |
} |
401 |
+ sinks = av_calloc(sink_links_count, sizeof(*sinks)); |
|
402 |
+ if (!sinks) |
|
403 |
+ return AVERROR(ENOMEM); |
|
404 |
+ for (i = 0; i < graph->filter_count; i++) { |
|
405 |
+ f = graph->filters[i]; |
|
406 |
+ if (!f->output_count) { |
|
407 |
+ for (j = 0; j < f->input_count; j++) { |
|
408 |
+ sinks[n] = f->inputs[j]; |
|
409 |
+ f->inputs[j]->age_index = n++; |
|
410 |
+ } |
|
411 |
+ } |
|
412 |
+ } |
|
413 |
+ av_assert0(n == sink_links_count); |
|
414 |
+ graph->sink_links = sinks; |
|
415 |
+ graph->sink_links_count = sink_links_count; |
|
416 |
+ return 0; |
|
390 | 417 |
} |
391 | 418 |
|
392 | 419 |
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) |
... | ... |
@@ -399,7 +427,8 @@ int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) |
399 | 399 |
return ret; |
400 | 400 |
if ((ret = ff_avfilter_graph_config_links(graphctx, log_ctx))) |
401 | 401 |
return ret; |
402 |
- ff_avfilter_graph_config_pointers(graphctx, log_ctx); |
|
402 |
+ if ((ret = ff_avfilter_graph_config_pointers(graphctx, log_ctx))) |
|
403 |
+ return ret; |
|
403 | 404 |
|
404 | 405 |
return 0; |
405 | 406 |
} |
... | ... |
@@ -461,3 +490,65 @@ int avfilter_graph_queue_command(AVFilterGraph *graph, const char *target, const |
461 | 461 |
|
462 | 462 |
return 0; |
463 | 463 |
} |
464 |
+ |
|
465 |
+static void heap_bubble_up(AVFilterGraph *graph, |
|
466 |
+ AVFilterLink *link, int index) |
|
467 |
+{ |
|
468 |
+ AVFilterLink **links = graph->sink_links; |
|
469 |
+ |
|
470 |
+ while (index) { |
|
471 |
+ int parent = (index - 1) >> 1; |
|
472 |
+ if (links[parent]->current_pts >= link->current_pts) |
|
473 |
+ break; |
|
474 |
+ links[index] = links[parent]; |
|
475 |
+ links[index]->age_index = index; |
|
476 |
+ index = parent; |
|
477 |
+ } |
|
478 |
+ links[index] = link; |
|
479 |
+ link->age_index = index; |
|
480 |
+} |
|
481 |
+ |
|
482 |
+static void heap_bubble_down(AVFilterGraph *graph, |
|
483 |
+ AVFilterLink *link, int index) |
|
484 |
+{ |
|
485 |
+ AVFilterLink **links = graph->sink_links; |
|
486 |
+ |
|
487 |
+ while (1) { |
|
488 |
+ int child = 2 * index + 1; |
|
489 |
+ if (child >= graph->sink_links_count) |
|
490 |
+ break; |
|
491 |
+ if (child + 1 < graph->sink_links_count && |
|
492 |
+ links[child + 1]->current_pts < links[child]->current_pts) |
|
493 |
+ child++; |
|
494 |
+ if (link->current_pts < links[child]->current_pts) |
|
495 |
+ break; |
|
496 |
+ links[index] = links[child]; |
|
497 |
+ links[index]->age_index = index; |
|
498 |
+ index = child; |
|
499 |
+ } |
|
500 |
+ links[index] = link; |
|
501 |
+ link->age_index = index; |
|
502 |
+} |
|
503 |
+ |
|
504 |
+void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link) |
|
505 |
+{ |
|
506 |
+ heap_bubble_up (graph, link, link->age_index); |
|
507 |
+ heap_bubble_down(graph, link, link->age_index); |
|
508 |
+} |
|
509 |
+ |
|
510 |
+ |
|
511 |
+int avfilter_graph_request_oldest(AVFilterGraph *graph) |
|
512 |
+{ |
|
513 |
+ while (graph->sink_links_count) { |
|
514 |
+ AVFilterLink *oldest = graph->sink_links[0]; |
|
515 |
+ int r = avfilter_request_frame(oldest); |
|
516 |
+ if (r != AVERROR_EOF) |
|
517 |
+ return r; |
|
518 |
+ /* EOF: remove the link from the heap */ |
|
519 |
+ if (oldest->age_index < --graph->sink_links_count) |
|
520 |
+ heap_bubble_down(graph, graph->sink_links[graph->sink_links_count], |
|
521 |
+ oldest->age_index); |
|
522 |
+ oldest->age_index = -1; |
|
523 |
+ } |
|
524 |
+ return AVERROR_EOF; |
|
525 |
+} |
... | ... |
@@ -33,6 +33,16 @@ typedef struct AVFilterGraph { |
33 | 33 |
AVFilterContext **filters; |
34 | 34 |
|
35 | 35 |
char *scale_sws_opts; ///< sws options to use for the auto-inserted scale filters |
36 |
+ |
|
37 |
+ /** |
|
38 |
+ * Private fields |
|
39 |
+ * |
|
40 |
+ * The following fields are for internal use only. |
|
41 |
+ * Their type, offset, number and semantic can change without notice. |
|
42 |
+ */ |
|
43 |
+ |
|
44 |
+ AVFilterLink **sink_links; |
|
45 |
+ int sink_links_count; |
|
36 | 46 |
} AVFilterGraph; |
37 | 47 |
|
38 | 48 |
/** |
... | ... |
@@ -221,4 +231,18 @@ int avfilter_graph_queue_command(AVFilterGraph *graph, const char *target, const |
221 | 221 |
*/ |
222 | 222 |
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options); |
223 | 223 |
|
224 |
+/** |
|
225 |
+ * Request a frame on the oldest sink link. |
|
226 |
+ * |
|
227 |
+ * If the request returns AVERROR_EOF, try the next. |
|
228 |
+ * |
|
229 |
+ * Note that this function is not meant to be the sole scheduling mechanism |
|
230 |
+ * of a filtergraph, only a convenience function to help drain a filtergraph |
|
231 |
+ * in a balanced way under normal circumstances. |
|
232 |
+ * |
|
233 |
+ * @return the return value of avfilter_request_frame, |
|
234 |
+ * or AVERROR_EOF of all links returned AVERROR_EOF. |
|
235 |
+ */ |
|
236 |
+int avfilter_graph_request_oldest(AVFilterGraph *graph); |
|
237 |
+ |
|
224 | 238 |
#endif /* AVFILTER_AVFILTERGRAPH_H */ |
... | ... |
@@ -65,6 +65,11 @@ int ff_avfilter_graph_config_links(AVFilterGraph *graphctx, AVClass *log_ctx); |
65 | 65 |
*/ |
66 | 66 |
int ff_avfilter_graph_config_formats(AVFilterGraph *graphctx, AVClass *log_ctx); |
67 | 67 |
|
68 |
+/** |
|
69 |
+ * Update the position of a link in the age heap. |
|
70 |
+ */ |
|
71 |
+void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link); |
|
72 |
+ |
|
68 | 73 |
/** default handler for freeing audio/video buffer when there are no references left */ |
69 | 74 |
void ff_avfilter_default_free_buffer(AVFilterBuffer *buf); |
70 | 75 |
|