diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 3daf2f7ec2..2dae6632bc 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -1665,6 +1665,7 @@ Force video tag/fourcc. This is an alias for @code{-tag:v}. @item -force_key_frames[:@var{stream_specifier}] @var{time}[,@var{time}...] (@emph{output,per-stream}) @item -force_key_frames[:@var{stream_specifier}] expr:@var{expr} (@emph{output,per-stream}) @item -force_key_frames[:@var{stream_specifier}] source (@emph{output,per-stream}) +@item -force_key_frames[:@var{stream_specifier}] scd_metadata (@emph{output,per-stream}) @var{force_key_frames} can take arguments of the following form: @@ -1728,6 +1729,14 @@ the current frame being encoded is marked as a key frame in its source. In cases where this particular source frame has to be dropped, enforce the next available frame to become a key frame instead. +@item scd_metadata +If the argument is @code{scd_metadata}, ffmpeg will force a key frame if +the current frame contains a metadata entry with the key @code{lavfi.scd.time}. +The metadata can be added by filters like @code{scdet} and @code{scdet_vulkan}. +Avoid inserting filters that duplicate frames after @code{scdet}, as this can +cause duplicate metadata for multiple frames and repeated insertion of key +frames. + @end table Note that forcing too many keyframes is very harmful for the lookahead diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index cc2ea1a56e..7720dd9c59 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -602,6 +602,8 @@ enum { #if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP KF_FORCE_SOURCE_NO_DROP = 2, #endif + // force keyframe if lavfi.scd.time metadata is set + KF_FORCE_SCD_METADATA = 3, }; typedef struct KeyframeForceCtx { diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index 8f07a10848..0f7d961472 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -768,6 +768,9 @@ static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, } } else if (kf->type == KF_FORCE_SOURCE && (frame->flags & AV_FRAME_FLAG_KEY)) { goto force_keyframe; + } else if (kf->type == KF_FORCE_SCD_METADATA && + av_dict_get(frame->metadata, "lavfi.scd.time", NULL, 0)) { + goto force_keyframe; } return AV_PICTURE_TYPE_NONE; diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index bcbbee9126..194a87875d 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -3279,6 +3279,8 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) "-force_key_frames is deprecated, use just 'source'\n"); ost->kf.type = KF_FORCE_SOURCE; #endif + } else if (!strcmp(forced_keyframes, "scd_metadata")) { + ost->kf.type = KF_FORCE_SCD_METADATA; } else { int ret = parse_forced_key_frames(ost, &ost->kf, mux, forced_keyframes); if (ret < 0)