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

Anton Khirnov anton at khirnov.net
Mon Aug 17 22:41:33 CEST 2015


Quoting Alexandra Hájková (2015-08-14 11:00:23)
> ---
>  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;

There does not seem to be any need to store this value outside of this
function scope.

> +    }
> +
> +    // 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) {

The compared values are in different units.

> +            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)) {

Why not exchange it with the following block and so simplify the
condition.

> +        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;

What is this for?

>      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 */

This definition is too vague. What is 'pts in the stream' exactly?

> +    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 */

How is 'current audio frame' defined?

>      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 */

This name is misleading, since this value is only equal to actual
duration at the moment when the looping process happens.

> +    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 };

Is this initialization really needed?

-- 
Anton Khirnov


More information about the libav-devel mailing list