mirror of https://git.ffmpeg.org/ffmpeg.git
avconv: Add loop option.
Signed-off-by: Anton Khirnov <anton@khirnov.net>
This commit is contained in:
parent
11c5f438ff
commit
16b0c92962
107
avconv.c
107
avconv.c
|
@ -1185,6 +1185,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
|
||||||
decoded_frame->pts = av_rescale_q(decoded_frame->pts,
|
decoded_frame->pts = av_rescale_q(decoded_frame->pts,
|
||||||
ist->st->time_base,
|
ist->st->time_base,
|
||||||
(AVRational){1, avctx->sample_rate});
|
(AVRational){1, avctx->sample_rate});
|
||||||
|
ist->nb_samples = decoded_frame->nb_samples;
|
||||||
for (i = 0; i < ist->nb_filters; i++) {
|
for (i = 0; i < ist->nb_filters; i++) {
|
||||||
if (i < ist->nb_filters - 1) {
|
if (i < ist->nb_filters - 1) {
|
||||||
f = ist->filter_frame;
|
f = ist->filter_frame;
|
||||||
|
@ -1323,7 +1324,7 @@ static int send_filter_eof(InputStream *ist)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pkt = NULL means EOF (needed to flush decoder buffers) */
|
/* pkt = NULL means EOF (needed to flush decoder buffers) */
|
||||||
static void process_input_packet(InputStream *ist, const AVPacket *pkt)
|
static void process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int got_output;
|
int got_output;
|
||||||
|
@ -1402,7 +1403,8 @@ static void process_input_packet(InputStream *ist, const AVPacket *pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* after flushing, send an EOF on all the filter inputs attached to the stream */
|
/* after flushing, send an EOF on all the filter inputs attached to the stream */
|
||||||
if (!pkt && ist->decoding_needed) {
|
/* except when looping we need to flush but not to send an EOF */
|
||||||
|
if (!pkt && ist->decoding_needed && !no_eof) {
|
||||||
int ret = send_filter_eof(ist);
|
int ret = send_filter_eof(ist);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n");
|
av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n");
|
||||||
|
@ -2270,6 +2272,86 @@ static void reset_eagain(void)
|
||||||
input_files[i]->eagain = 0;
|
input_files[i]->eagain = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set duration to max(tmp, duration) in a proper time base and return duration's time_base
|
||||||
|
static AVRational duration_max(int64_t tmp, int64_t *duration, AVRational tmp_time_base,
|
||||||
|
AVRational time_base)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!*duration) {
|
||||||
|
*duration = tmp;
|
||||||
|
return tmp_time_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = av_compare_ts(*duration, time_base, tmp, tmp_time_base);
|
||||||
|
if (ret < 0) {
|
||||||
|
*duration = tmp;
|
||||||
|
return tmp_time_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
return time_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seek_to_start(InputFile *ifile, AVFormatContext *is)
|
||||||
|
{
|
||||||
|
InputStream *ist;
|
||||||
|
AVCodecContext *avctx;
|
||||||
|
int i, ret, has_audio = 0;
|
||||||
|
int64_t duration = 0;
|
||||||
|
|
||||||
|
ret = av_seek_frame(is, -1, is->start_time, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < ifile->nb_streams; i++) {
|
||||||
|
ist = input_streams[ifile->ist_index + i];
|
||||||
|
avctx = ist->dec_ctx;
|
||||||
|
|
||||||
|
// flush decoders
|
||||||
|
if (ist->decoding_needed) {
|
||||||
|
process_input_packet(ist, NULL, 1);
|
||||||
|
avcodec_flush_buffers(avctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* duration is the length of the last frame in a stream
|
||||||
|
* when audio stream is present we don't care about
|
||||||
|
* last video frame length because it's not defined exactly */
|
||||||
|
if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && ist->nb_samples)
|
||||||
|
has_audio = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ifile->nb_streams; i++) {
|
||||||
|
ist = input_streams[ifile->ist_index + i];
|
||||||
|
avctx = ist->dec_ctx;
|
||||||
|
|
||||||
|
if (has_audio) {
|
||||||
|
if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && ist->nb_samples) {
|
||||||
|
AVRational sample_rate = {1, avctx->sample_rate};
|
||||||
|
|
||||||
|
duration = av_rescale_q(ist->nb_samples, sample_rate, ist->st->time_base);
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (ist->framerate.num) {
|
||||||
|
duration = av_rescale_q(1, ist->framerate, ist->st->time_base);
|
||||||
|
} else if (ist->st->avg_frame_rate.num) {
|
||||||
|
duration = av_rescale_q(1, ist->st->avg_frame_rate, ist->st->time_base);
|
||||||
|
} else duration = 1;
|
||||||
|
}
|
||||||
|
if (!ifile->duration)
|
||||||
|
ifile->time_base = ist->st->time_base;
|
||||||
|
/* the total duration of the stream, max_pts - min_pts is
|
||||||
|
* the duration of the stream without the last frame */
|
||||||
|
duration += ist->max_pts - ist->min_pts;
|
||||||
|
ifile->time_base = duration_max(duration, &ifile->duration, ist->st->time_base,
|
||||||
|
ifile->time_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
ifile->loop--;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read one packet from an input file and send it for
|
* Read one packet from an input file and send it for
|
||||||
* - decoding -> lavfi (audio/video)
|
* - decoding -> lavfi (audio/video)
|
||||||
|
@ -2289,6 +2371,7 @@ static int process_input(void)
|
||||||
InputStream *ist;
|
InputStream *ist;
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
int ret, i, j;
|
int ret, i, j;
|
||||||
|
int64_t duration;
|
||||||
|
|
||||||
/* select the stream that we must read now */
|
/* select the stream that we must read now */
|
||||||
ifile = select_input_file();
|
ifile = select_input_file();
|
||||||
|
@ -2310,6 +2393,11 @@ static int process_input(void)
|
||||||
ifile->eagain = 1;
|
ifile->eagain = 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if ((ret < 0) && (ifile->loop > 1)) {
|
||||||
|
if ((ret = seek_to_start(ifile, is)) < 0)
|
||||||
|
return ret;
|
||||||
|
ret = get_input_packet(ifile, &pkt);
|
||||||
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != AVERROR_EOF) {
|
if (ret != AVERROR_EOF) {
|
||||||
print_error(is->filename, ret);
|
print_error(is->filename, ret);
|
||||||
|
@ -2321,7 +2409,7 @@ static int process_input(void)
|
||||||
for (i = 0; i < ifile->nb_streams; i++) {
|
for (i = 0; i < ifile->nb_streams; i++) {
|
||||||
ist = input_streams[ifile->ist_index + i];
|
ist = input_streams[ifile->ist_index + i];
|
||||||
if (ist->decoding_needed)
|
if (ist->decoding_needed)
|
||||||
process_input_packet(ist, NULL);
|
process_input_packet(ist, NULL, 0);
|
||||||
|
|
||||||
/* mark all outputs that don't go through lavfi as finished */
|
/* mark all outputs that don't go through lavfi as finished */
|
||||||
for (j = 0; j < nb_output_streams; j++) {
|
for (j = 0; j < nb_output_streams; j++) {
|
||||||
|
@ -2400,8 +2488,17 @@ static int process_input(void)
|
||||||
pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
|
pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
duration = av_rescale_q(ifile->duration, ifile->time_base, ist->st->time_base);
|
||||||
|
if (pkt.pts != AV_NOPTS_VALUE) {
|
||||||
|
pkt.pts += duration;
|
||||||
|
ist->max_pts = FFMAX(pkt.pts, ist->max_pts);
|
||||||
|
ist->min_pts = FFMIN(pkt.pts, ist->min_pts);
|
||||||
|
}
|
||||||
|
|
||||||
process_input_packet(ist, &pkt);
|
if (pkt.dts != AV_NOPTS_VALUE)
|
||||||
|
pkt.dts += duration;
|
||||||
|
|
||||||
|
process_input_packet(ist, &pkt, 0);
|
||||||
|
|
||||||
discard_packet:
|
discard_packet:
|
||||||
av_free_packet(&pkt);
|
av_free_packet(&pkt);
|
||||||
|
@ -2472,7 +2569,7 @@ static int transcode(void)
|
||||||
for (i = 0; i < nb_input_streams; i++) {
|
for (i = 0; i < nb_input_streams; i++) {
|
||||||
ist = input_streams[i];
|
ist = input_streams[i];
|
||||||
if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
|
if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
|
||||||
process_input_packet(ist, NULL);
|
process_input_packet(ist, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
poll_filters();
|
poll_filters();
|
||||||
|
|
8
avconv.h
8
avconv.h
|
@ -102,6 +102,7 @@ typedef struct OptionsContext {
|
||||||
|
|
||||||
/* input options */
|
/* input options */
|
||||||
int64_t input_ts_offset;
|
int64_t input_ts_offset;
|
||||||
|
int loop;
|
||||||
int rate_emu;
|
int rate_emu;
|
||||||
int accurate_seek;
|
int accurate_seek;
|
||||||
|
|
||||||
|
@ -233,6 +234,9 @@ typedef struct InputStream {
|
||||||
int64_t next_dts;
|
int64_t next_dts;
|
||||||
/* dts of the last packet read for this stream */
|
/* dts of the last packet read for this stream */
|
||||||
int64_t last_dts;
|
int64_t last_dts;
|
||||||
|
int64_t min_pts; /* pts with the smallest value in a current stream */
|
||||||
|
int64_t max_pts; /* pts with the higher value in a current stream */
|
||||||
|
int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */
|
||||||
PtsCorrectionContext pts_ctx;
|
PtsCorrectionContext pts_ctx;
|
||||||
double ts_scale;
|
double ts_scale;
|
||||||
int showed_multi_packet_warning;
|
int showed_multi_packet_warning;
|
||||||
|
@ -282,6 +286,10 @@ typedef struct InputFile {
|
||||||
int eof_reached; /* true if eof reached */
|
int eof_reached; /* true if eof reached */
|
||||||
int eagain; /* true if last read attempt returned EAGAIN */
|
int eagain; /* true if last read attempt returned EAGAIN */
|
||||||
int ist_index; /* index of first stream in ist_table */
|
int ist_index; /* index of first stream in ist_table */
|
||||||
|
int loop; /* set number of times input stream should be looped */
|
||||||
|
int64_t duration; /* actual duration of the longest stream in a file
|
||||||
|
at the moment when looping happens */
|
||||||
|
AVRational time_base; /* time base of the duration */
|
||||||
int64_t ts_offset;
|
int64_t ts_offset;
|
||||||
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
|
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
|
||||||
int64_t recording_time;
|
int64_t recording_time;
|
||||||
|
|
|
@ -498,6 +498,9 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
|
||||||
ist->file_index = nb_input_files;
|
ist->file_index = nb_input_files;
|
||||||
ist->discard = 1;
|
ist->discard = 1;
|
||||||
st->discard = AVDISCARD_ALL;
|
st->discard = AVDISCARD_ALL;
|
||||||
|
ist->nb_samples = 0;
|
||||||
|
ist->min_pts = INT64_MAX;
|
||||||
|
ist->max_pts = INT64_MIN;
|
||||||
|
|
||||||
ist->ts_scale = 1.0;
|
ist->ts_scale = 1.0;
|
||||||
MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
|
MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
|
||||||
|
@ -783,6 +786,9 @@ static int open_input_file(OptionsContext *o, const char *filename)
|
||||||
f->nb_streams = ic->nb_streams;
|
f->nb_streams = ic->nb_streams;
|
||||||
f->rate_emu = o->rate_emu;
|
f->rate_emu = o->rate_emu;
|
||||||
f->accurate_seek = o->accurate_seek;
|
f->accurate_seek = o->accurate_seek;
|
||||||
|
f->loop = o->loop;
|
||||||
|
f->duration = 0;
|
||||||
|
f->time_base = (AVRational){ 1, 1 };
|
||||||
|
|
||||||
/* check if all codec options have been used */
|
/* check if all codec options have been used */
|
||||||
unused_opts = strip_specifiers(o->g->codec_opts);
|
unused_opts = strip_specifiers(o->g->codec_opts);
|
||||||
|
@ -2391,6 +2397,8 @@ const OptionDef options[] = {
|
||||||
{ "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC |
|
{ "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC |
|
||||||
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(dump_attachment) },
|
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(dump_attachment) },
|
||||||
"extract an attachment into a file", "filename" },
|
"extract an attachment into a file", "filename" },
|
||||||
|
{ "loop", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_INPUT |
|
||||||
|
OPT_OFFSET, { .off = OFFSET(loop) }, "set number of times input stream shall be looped", "loop count" },
|
||||||
|
|
||||||
/* video options */
|
/* video options */
|
||||||
{ "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames },
|
{ "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames },
|
||||||
|
|
|
@ -253,6 +253,9 @@ Overwrite output files without asking.
|
||||||
@item -n (@emph{global})
|
@item -n (@emph{global})
|
||||||
Immediately exit when output files already exist.
|
Immediately exit when output files already exist.
|
||||||
|
|
||||||
|
@item -loop @var{number} (@emph{input})
|
||||||
|
Set number of times input stream shall be looped.
|
||||||
|
|
||||||
@item -c[:@var{stream_specifier}] @var{codec} (@emph{input/output,per-stream})
|
@item -c[:@var{stream_specifier}] @var{codec} (@emph{input/output,per-stream})
|
||||||
@itemx -codec[:@var{stream_specifier}] @var{codec} (@emph{input/output,per-stream})
|
@itemx -codec[:@var{stream_specifier}] @var{codec} (@emph{input/output,per-stream})
|
||||||
Select an encoder (when used before an output file) or a decoder (when used
|
Select an encoder (when used before an output file) or a decoder (when used
|
||||||
|
|
Loading…
Reference in New Issue