diff options
-rw-r--r-- | doc/ffmpeg.texi | 3 | ||||
-rw-r--r-- | ffmpeg.c | 108 | ||||
-rw-r--r-- | ffmpeg.h | 10 | ||||
-rw-r--r-- | ffmpeg_opt.c | 8 | ||||
-rw-r--r-- | tst.avi | bin | 0 -> 1067722 bytes |
5 files changed, 124 insertions, 5 deletions
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index f4ffc6cab9..562493026f 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -253,6 +253,9 @@ Overwrite output files without asking. Do not overwrite output files, and exit immediately if a specified output file already exists. +@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}) @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 @@ -2024,6 +2024,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output) decoded_frame->pts = av_rescale_delta(decoded_frame_tb, decoded_frame->pts, (AVRational){1, avctx->sample_rate}, decoded_frame->nb_samples, &ist->filter_in_rescale_delta_last, (AVRational){1, avctx->sample_rate}); + ist->nb_samples = decoded_frame->nb_samples; for (i = 0; i < ist->nb_filters; i++) { if (i < ist->nb_filters - 1) { f = ist->filter_frame; @@ -2261,7 +2262,7 @@ static int send_filter_eof(InputStream *ist) } /* pkt = NULL means EOF (needed to flush decoder buffers) */ -static int process_input_packet(InputStream *ist, const AVPacket *pkt) +static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof) { int ret = 0, i; int got_output = 0; @@ -2370,7 +2371,8 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt) } /* after flushing, send an EOF on all the filter inputs attached to the stream */ - if (!pkt && ist->decoding_needed && !got_output) { + /* except when looping we need to flush but not to send an EOF */ + if (!pkt && ist->decoding_needed && !got_output && !no_eof) { int ret = send_filter_eof(ist); if (ret < 0) { av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n"); @@ -3606,6 +3608,86 @@ static void reset_eagain(void) output_streams[i]->unavailable = 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; +} + /* * Return * - 0 -- one packet was read and processed @@ -3620,6 +3702,7 @@ static int process_input(int file_index) InputStream *ist; AVPacket pkt; int ret, i, j; + int64_t duration; is = ifile->ctx; ret = get_input_packet(ifile, &pkt); @@ -3628,6 +3711,11 @@ static int process_input(int file_index) ifile->eagain = 1; 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 != AVERROR_EOF) { print_error(is->filename, ret); @@ -3638,7 +3726,7 @@ static int process_input(int file_index) for (i = 0; i < ifile->nb_streams; i++) { ist = input_streams[ifile->ist_index + i]; if (ist->decoding_needed) { - ret = process_input_packet(ist, NULL); + ret = process_input_packet(ist, NULL, 0); if (ret>0) return 0; } @@ -3773,6 +3861,16 @@ static int process_input(int file_index) 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); + } + + if (pkt.dts != AV_NOPTS_VALUE) + pkt.dts += duration; if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) && @@ -3824,7 +3922,7 @@ static int process_input(int file_index) sub2video_heartbeat(ist, pkt.pts); - process_input_packet(ist, &pkt); + process_input_packet(ist, &pkt, 0); discard_packet: av_free_packet(&pkt); @@ -3989,7 +4087,7 @@ static int transcode(void) for (i = 0; i < nb_input_streams; i++) { ist = input_streams[i]; if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) { - process_input_packet(ist, NULL); + process_input_packet(ist, NULL, 0); } } flush_encoders(); @@ -112,6 +112,7 @@ typedef struct OptionsContext { /* input options */ int64_t input_ts_offset; + int loop; int rate_emu; int accurate_seek; int thread_queue_size; @@ -272,6 +273,10 @@ typedef struct InputStream { int wrap_correction_done; int64_t filter_in_rescale_delta_last; + + 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 */ double ts_scale; int saw_first_ts; @@ -342,7 +347,12 @@ typedef struct InputFile { int eof_reached; /* true if eof reached */ int eagain; /* true if last read attempt returned EAGAIN */ int ist_index; /* index of first stream in input_streams */ + 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 input_ts_offset; + int64_t ts_offset; int64_t last_ts; int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index ded7e2ea53..573da723f6 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -627,6 +627,9 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) ist->file_index = nb_input_files; ist->discard = 1; st->discard = AVDISCARD_ALL; + ist->nb_samples = 0; + ist->min_pts = INT64_MAX; + ist->max_pts = INT64_MIN; ist->ts_scale = 1.0; MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st); @@ -1005,6 +1008,9 @@ static int open_input_file(OptionsContext *o, const char *filename) f->nb_streams = ic->nb_streams; f->rate_emu = o->rate_emu; f->accurate_seek = o->accurate_seek; + f->loop = o->loop; + f->duration = 0; + f->time_base = (AVRational){ 1, 1 }; #if HAVE_PTHREADS f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8; #endif @@ -3152,6 +3158,8 @@ const OptionDef options[] = { { "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(dump_attachment) }, "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" }, { "debug_ts", OPT_BOOL | OPT_EXPERT, { &debug_ts }, "print timestamp debugging info" }, { "max_error_rate", HAS_ARG | OPT_FLOAT, { &max_error_rate }, diff --git a/tst.avi b/tst.avi Binary files differnew file mode 100644 index 0000000000..dffeab86f5 --- /dev/null +++ b/tst.avi |