[FFmpeg-devel] [PATCH] ffmpeg: accept "chapters" as forced key frames.

Stefano Sabatini stefasab at gmail.com
Mon Jan 14 00:10:50 CET 2013


On date Sunday 2013-01-13 22:49:38 +0100, Nicolas George encoded:
> Allow to force a key frame at the beginning of each chapter.
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  doc/ffmpeg.texi |    9 ++++++++-
>  ffmpeg.c        |   52 +++++++++++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 53 insertions(+), 8 deletions(-)
> 
> 
> Updated on top of current Git HEAD and removed the risk of overflow in the
> compare function. I will push this version in a few days if nobody has
> anything to add.
> 
> 
> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
> index 0f862c1..de2e795 100644
> --- a/doc/ffmpeg.texi
> +++ b/doc/ffmpeg.texi
> @@ -555,9 +555,16 @@ Deprecated see -bsf
>  @item -force_key_frames[:@var{stream_specifier}] @var{time}[, at var{time}...] (@emph{output,per-stream})
>  Force key frames at the specified timestamps, more precisely at the first
>  frames after each specified time.
> +If one of the time is "@code{chapter}[@var{delta}]", it is expanded into the

times

@code{chapters}?

> +time of the beginning of all chapters in the file, shifted by @var{delta}.
>  This option can be useful to ensure that a seek point is present at a
>  chapter mark or any other designated place in the output file.
> -The timestamps must be specified in ascending order.
> +

> +For example, to insert a key frame at 5 minutes plus 0.1 second before the
> +beginning of each chapter:
> + at example
> +-force_key_frames 0:05:00,chapters-0.1

the description is ambiguous, it reads like a key frame is forced "5
minutes + 0.1" before the beginning of each chapter.

> + at end example
>  
>  @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream})
>  When doing stream copy, copy also non-key frames found at the
> diff --git a/ffmpeg.c b/ffmpeg.c
> index acaa523..a499f3d 100644
> --- a/ffmpeg.c
> +++ b/ffmpeg.c
> @@ -1931,19 +1931,25 @@ static InputStream *get_input_stream(OutputStream *ost)
>      return NULL;
>  }
>  
> +static int compare_int64(const void *a, const void *b)
> +{
> +    int64_t va = *(int64_t *)a, vb = *(int64_t *)b;
> +    return va < vb ? -1 : va > vb ? +1 : 0;
> +}
> +
>  static void parse_forced_key_frames(char *kf, OutputStream *ost,
>                                      AVCodecContext *avctx)
>  {
>      char *p;
> -    int n = 1, i;
> -    int64_t t;
> +    int n = 1, i, size, index = 0;
> +    int64_t t, *pts;
>  
>      for (p = kf; *p; p++)
>          if (*p == ',')
>              n++;
> -    ost->forced_kf_count = n;
> -    ost->forced_kf_pts   = av_malloc(sizeof(*ost->forced_kf_pts) * n);
> -    if (!ost->forced_kf_pts) {
> +    size = n;
> +    pts = av_malloc(sizeof(*pts) * size);
> +    if (!pts) {
>          av_log(NULL, AV_LOG_FATAL, "Could not allocate forced key frames array.\n");
>          exit(1);
>      }
> @@ -1955,11 +1961,43 @@ static void parse_forced_key_frames(char *kf, OutputStream *ost,
>          if (next)
>              *next++ = 0;
>  
> -        t = parse_time_or_die("force_key_frames", p, 1);
> -        ost->forced_kf_pts[i] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
> +        if (!memcmp(p, "chapters", 8)) {
> +

nit+++: weird empty line, here and below

> +            AVFormatContext *avf = output_files[ost->file_index]->ctx;
> +            int j;
> +
> +            if (avf->nb_chapters > INT_MAX - size ||
> +                !(pts = av_realloc_f(pts, size += avf->nb_chapters - 1,
> +                                     sizeof(*pts)))) {
> +                av_log(NULL, AV_LOG_FATAL,
> +                       "Could not allocate forced key frames array.\n");
> +                exit(1);
> +            }
> +            t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0;
> +            t = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
> +
> +            for (j = 0; j < avf->nb_chapters; j++) {
> +                AVChapter *c = avf->chapters[j];
> +                av_assert1(index < size);
> +                pts[index++] = av_rescale_q(c->start, c->time_base,
> +                                            avctx->time_base) + t;
> +            }
> +
> +        } else {
> +
> +            t = parse_time_or_die("force_key_frames", p, 1);
> +            av_assert1(index < size);
> +            pts[index++] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
> +
> +        }
>  
>          p = next;
>      }
> +
> +    av_assert0(index == size);
> +    qsort(pts, size, sizeof(*pts), compare_int64);
> +    ost->forced_kf_count = size;
> +    ost->forced_kf_pts   = pts;

Alternatively, considering my other patch: we read the chapter times,
and we store the next chapter time in a variable, so the equivalent
syntax for "chapters-0.1" would be:

expr:gte(t, NEXT_CHAPTER_T-0.1)

I wonder how we could combinate both specifications.

A possibility would be to allow to specify more than one
expression/time sequence:
-force_key_frames TIMES -force_key_frames expr:EXPR

or again we could make -force_key_frames accept one expression and one
list, and discard the corresponding previous specification.
-- 
FFmpeg = Fanciful and Frenzy Most Perennial Exciting Governor


More information about the ffmpeg-devel mailing list