Last active
August 29, 2015 14:00
-
-
Save sasshka/11168994 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit e329bfbb3c38e803b16c3ef9015f4a65b0d05987 | |
Author: Alexandra Khirnova <[email protected]> | |
Date: Fri Apr 11 11:17:38 2014 +0200 | |
avconv: Add loop option. | |
diff --git a/avconv.c b/avconv.c | |
index 13e6778..9a1a0f6 100644 | |
--- a/avconv.c | |
+++ b/avconv.c | |
@@ -1131,6 +1131,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output) | |
decoded_frame->pts = av_rescale_q(decoded_frame->pts, | |
ist->st->time_base, | |
(AVRational){1, ist->st->codec->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; | |
@@ -2145,6 +2146,81 @@ static void reset_eagain(void) | |
input_files[i]->eagain = 0; | |
} | |
+//seek to the beggining of file | |
+static int seek_to_start(InputFile *ifile, AVFormatContext *is) | |
+{ | |
+ InputStream *ist; | |
+ int i, ret, is_audio = 0; | |
+ int64_t *duration; //duration of last frame | |
+ | |
+ ret = av_seek_frame(is, -1, is->start_time, 0); | |
+ if (ret < 0) | |
+ return ret; | |
+ duration = av_mallocz(ifile->nb_streams * sizeof(*duration)); | |
+ if (!duration) | |
+ return AVERROR(ENOMEM); | |
+ //flush decoders | |
+ for (i = 0; i < ifile->nb_streams; i++) { | |
+ int got_output; | |
+ AVFrame *frame; | |
+ AVPacket avpkt; | |
+ | |
+ ist = input_streams[ifile->ist_index + i]; | |
+ av_init_packet(&avpkt); | |
+ avpkt.data = NULL; | |
+ avpkt.size = 0; | |
+ if (!(frame = av_frame_alloc())) | |
+ return AVERROR(ENOMEM); | |
+ if (ist->decoding_needed) { | |
+ switch (ist->st->codec->codec_type) { | |
+ case AVMEDIA_TYPE_AUDIO: | |
+ avcodec_decode_audio4(ist->st->codec, frame, &got_output, &avpkt); | |
+ break; | |
+ case AVMEDIA_TYPE_VIDEO: | |
+ avcodec_decode_video2(ist->st->codec, | |
+ frame, &got_output, &avpkt); | |
+ break; | |
+ } | |
+ } | |
+ av_frame_unref(frame); | |
+ } | |
+ for (i = 0; i < ifile->nb_streams; i++) { | |
+ ist = input_streams[ifile->ist_index + i]; | |
+ avcodec_flush_buffers(ist->st->codec); | |
+ if (ist->st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
+ AVRational sample_rate = {1, ist->st->codec->sample_rate}; | |
+ | |
+ if (ist->nb_samples) { | |
+ duration[i] = av_rescale_q(ist->nb_samples, sample_rate, ist->st->time_base); | |
+ is_audio = 1; | |
+ } | |
+ } | |
+ } | |
+ for (i = 0; i < ifile->nb_streams; i++) { | |
+ ist = input_streams[ifile->ist_index + i]; | |
+ if (!is_audio) { | |
+ if (ist->framerate.num) { | |
+ duration[i] = av_rescale_q(1, ist->framerate, ist->st->time_base); | |
+ } else if (ist->st->avg_frame_rate.num) { | |
+ duration[i] = av_rescale_q(1, ist->st->avg_frame_rate, ist->st->time_base); | |
+ } else duration[i] = 1; | |
+ } | |
+ ist->duration = duration[i] + ist->max_pts - ist->min_pts; | |
+ } | |
+ //looking for the longest stream in a file | |
+ for (i = 0; i < ifile->nb_streams; i++) { | |
+ ist = input_streams[ifile->ist_index + i]; | |
+ if (ifile->duration < ist->duration) { | |
+ ifile->duration = ist->duration; | |
+ ifile->time_base = ist->st->time_base; | |
+ } | |
+ } | |
+ ifile->loop--; | |
+ av_freep(&duration); | |
+ | |
+ return ret; | |
+} | |
+ | |
/* | |
* Read one packet from an input file and send it for | |
* - decoding -> lavfi (audio/video) | |
@@ -2164,6 +2240,7 @@ static int process_input(void) | |
InputStream *ist; | |
AVPacket pkt; | |
int ret, i, j; | |
+ int64_t duration; | |
/* select the stream that we must read now */ | |
ifile = select_input_file(); | |
@@ -2181,6 +2258,12 @@ static int process_input(void) | |
is = ifile->ctx; | |
ret = get_input_packet(ifile, &pkt); | |
+ if ((ret == AVERROR_EOF) && (ifile->loop > 1)) { | |
+ if ((ret = seek_to_start(ifile, is)) < 0) | |
+ return ret; | |
+ ret = get_input_packet(ifile, &pkt); | |
+ | |
+ } | |
if (ret == AVERROR(EAGAIN)) { | |
ifile->eagain = 1; | |
return ret; | |
@@ -2251,6 +2334,15 @@ static int process_input(void) | |
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; | |
ret = output_packet(ist, &pkt); | |
if (ret < 0) { | |
diff --git a/avconv.h b/avconv.h | |
index c912fae..d794881 100644 | |
--- a/avconv.h | |
+++ b/avconv.h | |
@@ -100,6 +100,7 @@ typedef struct OptionsContext { | |
/* input options */ | |
int64_t input_ts_offset; | |
+ int loop; | |
int rate_emu; | |
int accurate_seek; | |
@@ -227,6 +228,10 @@ typedef struct InputStream { | |
int64_t next_dts; | |
/* dts of the last packet read for this stream */ | |
int64_t last_dts; | |
+ int64_t min_pts; /* smallest pts in the stream */ | |
+ int64_t max_pts; /* largest pts it the stream */ | |
+ int64_t duration; /* duration of the stream in AVStream time_base */ | |
+ int64_t nb_samples; /* number of samples in the current audio frame */ | |
PtsCorrectionContext pts_ctx; | |
double ts_scale; | |
int showed_multi_packet_warning; | |
@@ -266,6 +271,9 @@ 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 ist_table */ | |
+ int loop; /* set number of times input stream should be looped */ | |
+ int64_t duration; /* duration of the longest stream in a file in time_base */ | |
+ AVRational time_base; /* time base of the duration */ | |
int64_t ts_offset; | |
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ | |
int64_t recording_time; | |
diff --git a/avconv_opt.c b/avconv_opt.c | |
index 7bc41c9..edbf28a 100644 | |
--- a/avconv_opt.c | |
+++ b/avconv_opt.c | |
@@ -476,6 +476,10 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) | |
ist->file_index = nb_input_files; | |
ist->discard = 1; | |
st->discard = AVDISCARD_ALL; | |
+ ist->duration = 0; | |
+ 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); | |
@@ -746,6 +750,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}; | |
/* check if all codec options have been used */ | |
unused_opts = strip_specifiers(o->g->codec_opts); | |
@@ -2272,6 +2279,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" }, | |
/* video options */ | |
{ "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames }, | |
diff --git a/doc/avconv.texi b/doc/avconv.texi | |
index 0c17eb9..026158b 100644 | |
--- a/doc/avconv.texi | |
+++ b/doc/avconv.texi | |
@@ -236,6 +236,9 @@ Overwrite output files without asking. | |
@item -n (@emph{global}) | |
Immediately exit when output files already exist. | |
+@item -loop @var{number} | |
+Loops input stream <number> times. | |
+ | |
@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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment