fftools/ffmpeg: add force key frame by scdet metadata support

For example:

./ffmpeg -hwaccel videotoolbox \
	-i input.mp4 -c:a copy \
	-vf scdet=threshold=10 \
	-c:v h264_videotoolbox \
	-force_key_frames scd_metadata \
	-g 1000 -t 30 output.mp4
This commit is contained in:
Zhao Zhili
2025-11-28 11:17:05 +08:00
committed by Zhao Zhili
parent 27e94281d1
commit 540aacf759
4 changed files with 16 additions and 0 deletions

View File

@@ -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

View File

@@ -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 {

View File

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

View File

@@ -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)