diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 8a29d5e14b69d9b5615196811b5e35376e7cb346..5aa45629d4795e71fe3a85f1b20898a30047728c 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -555,9 +555,17 @@ Deprecated see -bsf @item -force_key_frames[:@var{stream_specifier}] @var{time}[,@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 times is "@code{chapters}[@var{delta}]", it is expanded into +the time of the beginning of all chapters in the file, shifted by +@var{delta}, expressed as a time in seconds. 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 key frames 0.1 second +before the beginning of every chapter: +@example +-force_key_frames 0:05:00,chapters-0.1 +@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 abfe439d95ef5fcbdf19457c5771c2250e5290fe..956040479ad64bc4d5208fad5b912e7c4241c7f9 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)) { + + 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; } static void report_new_stream(int input_index, AVPacket *pkt)