[libav-devel] [ibav-devel] [PATCH] avconv: Add loop option.

Alexandra Hájková alexandra.khirnova at gmail.com
Fri Aug 14 11:00:23 CEST 2015


---
 avconv.c        | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 avconv.h        |  9 ++++++
 avconv_opt.c    | 10 ++++++
 doc/avconv.texi |  3 ++
 4 files changed, 115 insertions(+), 5 deletions(-)

diff --git a/avconv.c b/avconv.c
index deaa40b..d45b01f 100644
--- a/avconv.c
+++ b/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,
                                           ist->st->time_base,
                                           (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;
@@ -1323,7 +1324,7 @@ static int send_filter_eof(InputStream *ist)
 }
 
 /* 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 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 */
-    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);
         if (ret < 0) {
             av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n");
@@ -2271,6 +2273,76 @@ static void reset_eagain(void)
         input_files[i]->eagain = 0;
 }
 
+static int seek_to_start(InputFile *ifile, AVFormatContext *is)
+{
+    InputStream *ist;
+    AVCodecContext *avctx;
+    int i, ret, has_audio = 0;
+    int64_t *duration; // duration of the 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++) {
+        ist   = input_streams[ifile->ist_index + i];
+        avctx = ist->dec_ctx;
+
+        if (ist->decoding_needed) {
+            process_input_packet(ist, NULL, 1);
+            avcodec_flush_buffers(avctx);
+        }
+    }
+
+    // determine the duration of each stream
+    for (i = 0; i < ifile->nb_streams; i++) {
+        ist   = input_streams[ifile->ist_index + i];
+        avctx = ist->dec_ctx;
+
+        if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
+            AVRational sample_rate = {1, avctx->sample_rate};
+
+            if (ist->nb_samples) {
+                duration[i] = av_rescale_q(ist->nb_samples, sample_rate, ist->st->time_base);
+                has_audio = 1;
+            }
+        }
+    }
+    /* duration[i] is the duration 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 */
+    for (i = 0; i < ifile->nb_streams; i++) {
+        ist = input_streams[ifile->ist_index + i];
+        if (!has_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;
+        }
+        /* the total duration of the stream, max_pts - min_pts is
+         * the duration of the stream without the last frame */
+        ist->duration = duration[i] + ist->max_pts - ist->min_pts;
+    }
+
+    // set input file duration to the longest stream duration
+    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)
@@ -2290,6 +2362,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();
@@ -2307,6 +2380,11 @@ static int process_input(void)
     is  = ifile->ctx;
     ret = get_input_packet(ifile, &pkt);
 
+    if ((ret < 0) && (ret != AVERROR(EAGAIN)) && (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;
@@ -2322,7 +2400,7 @@ static int process_input(void)
         for (i = 0; i < ifile->nb_streams; i++) {
             ist = input_streams[ifile->ist_index + i];
             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 */
             for (j = 0; j < nb_output_streams; j++) {
@@ -2350,6 +2428,7 @@ static int process_input(void)
 
     ist = input_streams[ifile->ist_index + pkt.stream_index];
 
+    ist->last_pkt_duration = pkt.duration;
     ist->data_size += pkt.size;
     ist->nb_packets++;
 
@@ -2401,8 +2480,17 @@ 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;
 
-    process_input_packet(ist, &pkt);
+    process_input_packet(ist, &pkt, 0);
 
 discard_packet:
     av_free_packet(&pkt);
@@ -2473,7 +2561,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);
         }
     }
     poll_filters();
diff --git a/avconv.h b/avconv.h
index 187dcdb..e96b2ad 100644
--- a/avconv.h
+++ b/avconv.h
@@ -102,6 +102,7 @@ typedef struct OptionsContext {
 
     /* input options */
     int64_t input_ts_offset;
+    int loop;
     int rate_emu;
     int accurate_seek;
 
@@ -233,6 +234,11 @@ 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 */
+    int last_pkt_duration;
+    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;
@@ -282,6 +288,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 9775416..25f37ae 100644
--- a/avconv_opt.c
+++ b/avconv_opt.c
@@ -486,6 +486,11 @@ 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->last_pkt_duration = 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);
@@ -771,6 +776,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);
@@ -2378,6 +2386,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 504b8ed..33f61e4 100644
--- a/doc/avconv.texi
+++ b/doc/avconv.texi
@@ -253,6 +253,9 @@ Overwrite output files without asking.
 @item -n (@emph{global})
 Immediately exit when output files already exist.
 
+ at item -loop @var{number}
+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
-- 
2.0.1



More information about the libav-devel mailing list