[FFmpeg-devel] [PATCH] ffmpeg: reduce differences with avconv for in/out scheduling.
Nicolas George
nicolas.george at normalesup.org
Fri Aug 17 17:25:10 CEST 2012
Rework the transcode() function and its immediate annexes to have
the same structure as in avconv, while still maintaining proper
scheduling to avoid accumulation.
Using -filter_complex without inputs now works.
Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
ffmpeg.c | 266 ++++++++++++++++++++++++++++++++------------------------------
1 file changed, 136 insertions(+), 130 deletions(-)
diff --git a/ffmpeg.c b/ffmpeg.c
index b97ad7b..2b037f0 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -947,16 +947,19 @@ static void do_video_stats(AVFormatContext *os, OutputStream *ost,
}
}
-/* check for new output on any of the filtergraphs */
-static int poll_filters(void)
+/**
+ * Check for new output on any of the filtergraphs, without causing activity.
+ *
+ * @return 0 for success, <0 for severe errors
+ */
+static int reap_filters(void)
{
AVFilterBufferRef *picref;
AVFrame *filtered_frame = NULL;
- int i, ret, ret_all;
- unsigned nb_success = 1, av_uninit(nb_eof);
+ int i;
int64_t frame_pts;
- while (1) {
+ /* TODO reindent */
/* Reap all buffers present in the buffer sinks */
for (i = 0; i < nb_output_streams; i++) {
OutputStream *ost = output_streams[i];
@@ -1026,27 +1029,8 @@ static int poll_filters(void)
avfilter_unref_buffer(picref);
}
}
- if (!nb_success) /* from last round */
- break;
- /* Request frames through all the graphs */
- ret_all = nb_success = nb_eof = 0;
- for (i = 0; i < nb_filtergraphs; i++) {
- ret = avfilter_graph_request_oldest(filtergraphs[i]->graph);
- if (!ret) {
- nb_success++;
- } else if (ret == AVERROR_EOF) {
- nb_eof++;
- } else if (ret != AVERROR(EAGAIN)) {
- char buf[256];
- av_strerror(ret, buf, sizeof(buf));
- av_log(NULL, AV_LOG_WARNING,
- "Error in request_frame(): %s\n", buf);
- ret_all = ret;
- }
- }
- /* Try again if anything succeeded */
- }
- return nb_eof == nb_filtergraphs ? AVERROR_EOF : ret_all;
+
+ return 0;
}
static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time)
@@ -2419,84 +2403,27 @@ static int need_output(void)
return 0;
}
-static int input_acceptable(InputStream *ist)
-{
- av_assert1(!ist->discard);
- return !input_files[ist->file_index]->eagain &&
- !input_files[ist->file_index]->eof_reached;
-}
-
-static int find_graph_input(FilterGraph *graph)
-{
- int i, nb_req_max = 0, file_index = -1;
-
- for (i = 0; i < graph->nb_inputs; i++) {
- int nb_req = av_buffersrc_get_nb_failed_requests(graph->inputs[i]->filter);
- if (nb_req > nb_req_max) {
- InputStream *ist = graph->inputs[i]->ist;
- if (input_acceptable(ist)) {
- nb_req_max = nb_req;
- file_index = ist->file_index;
- }
- }
- }
-
- return file_index;
-}
-
/**
- * Select the input file to read from.
+ * Select the output stream to process.
*
- * @return >=0 index of the input file to use;
- * -1 if no file is acceptable;
- * -2 to read from filters without reading from a file
+ * @return selected output stream, or NULL if none available
*/
-static int select_input_file(void)
+static OutputStream *choose_output(void)
{
- int i, ret, nb_active_out = nb_output_streams, ost_index = -1;
- int64_t opts_min;
- OutputStream *ost;
- AVFilterBufferRef *dummy;
+ int i;
+ int64_t opts_min = INT64_MAX;
+ OutputStream *ost_min = NULL;
- for (i = 0; i < nb_output_streams; i++)
- nb_active_out -= output_streams[i]->unavailable =
- output_streams[i]->finished;
- while (nb_active_out) {
- opts_min = INT64_MAX;
- ost_index = -1;
- for (i = 0; i < nb_output_streams; i++) {
- OutputStream *ost = output_streams[i];
- int64_t opts = av_rescale_q(ost->st->cur_dts, ost->st->time_base,
- AV_TIME_BASE_Q);
- if (!ost->unavailable && opts < opts_min) {
- opts_min = opts;
- ost_index = i;
- }
+ for (i = 0; i < nb_output_streams; i++) {
+ OutputStream *ost = output_streams[i];
+ int64_t opts = av_rescale_q(ost->st->cur_dts, ost->st->time_base,
+ AV_TIME_BASE_Q);
+ if (!ost->unavailable && !ost->finished && opts < opts_min) {
+ opts_min = opts;
+ ost_min = ost;
}
- if (ost_index < 0)
- return -1;
-
- ost = output_streams[ost_index];
- if (ost->source_index >= 0) {
- /* ost is directly connected to an input */
- InputStream *ist = input_streams[ost->source_index];
- if (input_acceptable(ist))
- return ist->file_index;
- } else {
- /* ost is connected to a complex filtergraph */
- av_assert1(ost->filter);
- ret = av_buffersink_get_buffer_ref(ost->filter->filter, &dummy,
- AV_BUFFERSINK_FLAG_PEEK);
- if (ret >= 0)
- return -2;
- ret = find_graph_input(ost->filter->graph);
- if (ret >= 0)
- return ret;
- }
- ost->unavailable = 1;
- nb_active_out--;
}
- return -1;
+ return ost_min;
}
static int check_keyboard_interaction(int64_t cur_time)
@@ -2714,8 +2641,8 @@ static int get_input_packet(InputFile *f, AVPacket *pkt)
static int got_eagain(void)
{
int i;
- for (i = 0; i < nb_input_files; i++)
- if (input_files[i]->eagain)
+ for (i = 0; i < nb_output_streams; i++)
+ if (output_streams[i]->unavailable)
return 1;
return 0;
}
@@ -2725,6 +2652,8 @@ static void reset_eagain(void)
int i;
for (i = 0; i < nb_input_files; i++)
input_files[i]->eagain = 0;
+ for (i = 0; i < nb_output_streams; i++)
+ output_streams[i]->unavailable = 0;
}
/**
@@ -2734,32 +2663,13 @@ static void reset_eagain(void)
* this function should be called again
* - AVERROR_EOF -- this function should not be called again
*/
-static int process_input(void)
+static int process_input(int file_index)
{
- InputFile *ifile;
+ InputFile *ifile = input_files[file_index];
AVFormatContext *is;
InputStream *ist;
AVPacket pkt;
int ret, i, j;
- int file_index;
-
- /* select the stream that we must read now */
- file_index = select_input_file();
- /* if none, if is finished */
- if (file_index == -2) {
- poll_filters() ;
- return AVERROR(EAGAIN);
- }
- if (file_index < 0) {
- if (got_eagain()) {
- reset_eagain();
- av_usleep(10000);
- return AVERROR(EAGAIN);
- }
- av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n");
- return AVERROR_EOF;
- }
- ifile = input_files[file_index];
is = ifile->ctx;
ret = get_input_packet(ifile, &pkt);
@@ -2780,7 +2690,15 @@ static int process_input(void)
ist = input_streams[ifile->ist_index + i];
if (ist->decoding_needed)
output_packet(ist, NULL);
- poll_filters();
+
+ /* mark all outputs that don't go through lavfi as finished */
+ for (j = 0; j < nb_output_streams; j++) {
+ OutputStream *ost = output_streams[j];
+
+ if (ost->source_index == ifile->ist_index + i &&
+ (ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE))
+ ost->finished= 1;
+ }
}
if (opt_shortest)
@@ -2880,16 +2798,14 @@ static int process_input(void)
sub2video_heartbeat(ist, pkt.pts);
- if ((ret = output_packet(ist, &pkt)) < 0 ||
- ((ret = poll_filters()) < 0 && ret != AVERROR_EOF)) {
+ ret = output_packet(ist, &pkt);
+ if (ret < 0) {
char buf[128];
av_strerror(ret, buf, sizeof(buf));
av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: %s\n",
ist->file_index, ist->st->index, buf);
if (exit_on_error)
exit_program(1);
- av_free_packet(&pkt);
- return AVERROR(EAGAIN);
}
discard_packet:
@@ -2898,6 +2814,98 @@ discard_packet:
return 0;
}
+/**
+ * Perform a step of transcoding for the specified filter graph.
+ *
+ * @param[in] graph filter graph to consider
+ * @param[out] best_ist input stream where a frame would allow to continue
+ * @return 0 for success, <0 for error
+ */
+static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist)
+{
+ int i, ret;
+ int nb_requests, nb_requests_max = 0;
+ InputFilter *ifilter;
+ InputStream *ist;
+
+ *best_ist = NULL;
+ ret = avfilter_graph_request_oldest(graph->graph);
+ if (ret >= 0)
+ return reap_filters();
+
+ if (ret == AVERROR_EOF) {
+ ret = reap_filters();
+ for (i = 0; i < graph->nb_outputs; i++)
+ graph->outputs[i]->ost->finished = 1;
+ return ret;
+ }
+ if (ret != AVERROR(EAGAIN))
+ return ret;
+
+ for (i = 0; i < graph->nb_inputs; i++) {
+ ifilter = graph->inputs[i];
+ ist = ifilter->ist;
+ if (input_files[ist->file_index]->eagain ||
+ input_files[ist->file_index]->eof_reached)
+ continue;
+ nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter);
+ if (nb_requests > nb_requests_max) {
+ nb_requests_max = nb_requests;
+ *best_ist = ist;
+ }
+ }
+
+ if (!*best_ist)
+ for (i = 0; i < graph->nb_outputs; i++)
+ graph->outputs[i]->ost->unavailable = 1;
+
+ return 0;
+}
+
+/**
+ * Run a single step of transcoding.
+ *
+ * @return 0 for success, <0 for error
+ */
+static int transcode_step(void)
+{
+ OutputStream *ost;
+ InputStream *ist;
+ int ret;
+
+ ost = choose_output();
+ if (!ost) {
+ if (got_eagain()) {
+ reset_eagain();
+ av_usleep(10000);
+ return 0;
+ }
+ av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n");
+ return AVERROR_EOF;
+ }
+
+ if (ost->filter) {
+ if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0)
+ return ret;
+ if (!ist)
+ return 0;
+ } else {
+ av_assert0(ost->source_index >= 0);
+ ist = input_streams[ost->source_index];
+ }
+
+ ret = process_input(ist->file_index);
+ if (ret == AVERROR(EAGAIN)) {
+ if (input_files[ist->file_index]->eagain)
+ ost->unavailable = 1;
+ return 0;
+ }
+ if (ret < 0)
+ return ret == AVERROR_EOF ? 0 : ret;
+
+ return reap_filters();
+}
+
/*
* The following code is the main loop of the file converter
*/
@@ -2938,12 +2946,11 @@ static int transcode(void)
break;
}
- ret = process_input();
+ ret = transcode_step();
if (ret < 0) {
- if (ret == AVERROR(EAGAIN))
+ if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
continue;
- if (ret == AVERROR_EOF)
- break;
+
av_log(NULL, AV_LOG_ERROR, "Error while filtering.\n");
break;
}
@@ -2962,7 +2969,6 @@ static int transcode(void)
output_packet(ist, NULL);
}
}
- poll_filters();
flush_encoders();
term_exit();
--
1.7.10.4
More information about the ffmpeg-devel
mailing list