mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-01-08 13:22:53 +02:00
ffmpeg: reduce differences with avconv for in/out scheduling.
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.
This commit is contained in:
parent
af5086087e
commit
429c6cab1c
288
ffmpeg.c
288
ffmpeg.c
@ -950,16 +950,20 @@ static void do_video_stats(AVFormatContext *os, OutputStream *ost,
|
||||
}
|
||||
}
|
||||
|
||||
/* check for new output on any of the filtergraphs */
|
||||
static int poll_filters(void)
|
||||
/**
|
||||
* Get and encode new output from 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];
|
||||
@ -1029,27 +1033,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)
|
||||
@ -1429,7 +1414,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
|
||||
|
||||
if (ret >= 0 && avctx->sample_rate <= 0) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Sample rate %d invalid\n", avctx->sample_rate);
|
||||
return AVERROR_INVALIDDATA;
|
||||
ret = AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
if (!*got_output || ret < 0) {
|
||||
@ -2422,84 +2407,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)
|
||||
@ -2717,8 +2645,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;
|
||||
}
|
||||
@ -2728,6 +2656,20 @@ 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;
|
||||
}
|
||||
|
||||
static void close_output_stream(OutputStream *ost)
|
||||
{
|
||||
OutputFile *of = output_files[ost->file_index];
|
||||
|
||||
ost->finished = 1;
|
||||
if (of->shortest) {
|
||||
int i;
|
||||
for (i = 0; i < of->ctx->nb_streams; i++)
|
||||
output_streams[of->ost_index + i]->finished = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2737,32 +2679,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);
|
||||
@ -2783,19 +2706,14 @@ static int process_input(void)
|
||||
ist = input_streams[ifile->ist_index + i];
|
||||
if (ist->decoding_needed)
|
||||
output_packet(ist, NULL);
|
||||
poll_filters();
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_output_streams; i++) {
|
||||
OutputStream *ost = output_streams[i];
|
||||
OutputFile *of = output_files[ost->file_index];
|
||||
AVFormatContext *os = output_files[ost->file_index]->ctx;
|
||||
/* 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 (of->shortest) {
|
||||
int j;
|
||||
for (j = 0; j < of->ctx->nb_streams; j++)
|
||||
output_streams[of->ost_index + j]->finished = 1;
|
||||
continue;
|
||||
if (ost->source_index == ifile->ist_index + i &&
|
||||
(ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE))
|
||||
close_output_stream(ost);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2893,16 +2811,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:
|
||||
@ -2911,6 +2827,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++)
|
||||
close_output_stream(graph->outputs[i]->ost);
|
||||
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
|
||||
*/
|
||||
@ -2951,12 +2959,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;
|
||||
}
|
||||
@ -2975,7 +2982,6 @@ static int transcode(void)
|
||||
output_packet(ist, NULL);
|
||||
}
|
||||
}
|
||||
poll_filters();
|
||||
flush_encoders();
|
||||
|
||||
term_exit();
|
||||
|
Loading…
Reference in New Issue
Block a user