This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 11b68cce09 avfilter/af_whisper: Add 'lock', 'eval' lang modes
11b68cce09 is described below
commit 11b68cce09900f8a4a25ceba42055094af00bf37
Author: WyattBlue <[email protected]>
AuthorDate: Wed Jul 1 00:33:54 2026 -0400
Commit: michaelni <[email protected]>
CommitDate: Sat Aug 22 17:41:51 2026 +0000
avfilter/af_whisper: Add 'lock', 'eval' lang modes
Add 'lock' and 'eval' languages modes. With autodetection, whisper
reruns language detection on every queued chunk, an extra encoder pass
that can also drift to a different language mid-file.
Add two explicit values for the language option: 'eval' keeps the
per-chunk reevaluation, 'lock' caches the language detected by the
first chunk that produces transcribed text and reuses it for the
following chunks. Requiring actual text means a silent, musical or
[BLANK_AUDIO]-only opening chunk leaves detection to a later one.
'auto' remains an alias for 'eval' so existing behavior is unchanged.
---
doc/filters.texi | 18 +++++++++++++++++-
libavfilter/af_whisper.c | 30 ++++++++++++++++++++++++++----
2 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 51885fddde..8425382225 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -7740,7 +7740,23 @@ The filter has following options:
The file path of the downloaded whisper.cpp model (mandatory).
@item language
-The language to use for transcription ('auto' for auto-detect).
+The language to use for transcription. Besides a language code, the following
+special values enable auto-detection:
+
+@table @samp
+@item eval
+Re-run language detection on every queued chunk. This costs an extra encoder
+pass per chunk and the detected language may change mid-file.
+
+@item lock
+Detect the language on the first chunk that produces transcribed text, then
+reuse it for the remaining chunks.
+
+@item auto
+Currently an alias for @samp{eval}. This may change in a future version, so use
+@samp{eval} or @samp{lock} if the behavior matters.
+@end table
+
Default value: @code{"auto"}
@item translate
diff --git a/libavfilter/af_whisper.c b/libavfilter/af_whisper.c
index 75103ced0c..33d8a2112a 100644
--- a/libavfilter/af_whisper.c
+++ b/libavfilter/af_whisper.c
@@ -43,6 +43,8 @@ typedef struct WhisperContext {
char *model_path;
const char *language;
char *language_str;
+ char *locked_language;
+ bool lock_language;
bool translate;
bool use_gpu;
int gpu_device;
@@ -152,18 +154,25 @@ static int init(AVFilterContext *ctx)
wctx->avio_context->direct = AVIO_FLAG_DIRECT;
}
+ // 'eval' and 'lock' both auto-detect; they differ only in whether the
+ // detected language is reused for the following chunks
+ const char *lang = wctx->language_str;
+ wctx->lock_language = !strcmp(lang, "lock");
+ if (wctx->lock_language || !strcmp(lang, "eval"))
+ lang = "auto";
+
if (!whisper_is_multilingual(wctx->ctx_wsp)) {
- if (!wctx->translate && strcmp(wctx->language_str, "auto") == 0) {
+ if (!wctx->translate && strcmp(lang, "auto") == 0) {
av_log(ctx, AV_LOG_WARNING,
"Multilingual model not provided. Non-English audio may not
be correctly transcribed.\n");
- } else if (wctx->translate || (strcmp(wctx->language_str, "auto") != 0
&& strcmp(wctx->language_str, "en") != 0)) {
+ } else if (wctx->translate || (strcmp(lang, "auto") != 0 &&
strcmp(lang, "en") != 0)) {
av_log(ctx, AV_LOG_ERROR,
"%s requested but multilingual model not provided.\n",
wctx->translate ? "Translation" : "Transcription");
return AVERROR(ENOSYS);
}
wctx->language = "en";
} else
- wctx->language = wctx->language_str;
+ wctx->language = lang;
av_log(ctx, AV_LOG_INFO,
"Whisper filter initialized: model: %s lang: %s queue: %" PRId64 "
ms\n",
@@ -193,6 +202,7 @@ static void uninit(AVFilterContext *ctx)
}
av_freep(&wctx->audio_buffer);
+ av_freep(&wctx->locked_language);
if (wctx->avio_context)
avio_closep(&wctx->avio_context);
@@ -295,6 +305,18 @@ static void run_transcription(AVFilterContext *ctx,
AVFrame *frame, int samples)
av_freep(&text_cleaned);
}
+ if (wctx->lock_language && segments_text && !av_strcasecmp(wctx->language,
"auto")) {
+ const int lang_id = whisper_full_lang_id(wctx->ctx_wsp);
+ if (lang_id >= 0) {
+ char *detected = av_strdup(whisper_lang_str(lang_id));
+ if (detected) {
+ wctx->locked_language = detected;
+ wctx->language = detected;
+ av_log(ctx, AV_LOG_INFO, "Locked auto-detected language:
%s\n", detected);
+ }
+ }
+ }
+
AVDictionary **metadata = &frame->metadata;
if (metadata && segments_text) {
av_dict_set(metadata, "lavfi.whisper.text", segments_text, 0);
@@ -459,7 +481,7 @@ static int query_formats(const AVFilterContext *ctx,
static const AVOption whisper_options[] = {
{ "model", "Path to the whisper.cpp model file", OFFSET(model_path),
AV_OPT_TYPE_STRING,.flags = FLAGS },
- { "language", "Language for transcription ('auto' for auto-detect)",
OFFSET(language_str), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = FLAGS },
+ { "language", "Language for transcription ('auto', 'eval' or 'lock' for
auto-detect)", OFFSET(language_str), AV_OPT_TYPE_STRING, {.str = "auto"},
.flags = FLAGS },
{ "translate", "Translate from source language to English",
OFFSET(translate), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = FLAGS },
{ "queue", "Audio queue size", OFFSET(queue), AV_OPT_TYPE_DURATION, {.i64
= 3000000}, 20000, HOURS, .flags = FLAGS },
{ "use_gpu", "Use GPU for processing", OFFSET(use_gpu), AV_OPT_TYPE_BOOL,
{.i64 = 1}, 0, 1, .flags = FLAGS },
--
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]