Commit 55f14c5a63 for ffmpeg
commit 55f14c5a63de58df18e683879de9e8a3f9a55876
Author: Ruk Doe <info@doany.io>
Date: Sat Oct 10 17:03:34 2026 +0900
avcodec/libaribcaption: decide text mode clears on the text regions
Text mode output is caption.text, which never holds ruby. It decided
whether there was anything to show from region_count, so a caption with
only ruby regions was output as text. Without CS, that event held an
empty text and erased the previous caption, which only CS may do. With
CS it failed: libaribcaption returns a NULL text when it is empty, and
av_strdup(NULL) was reported as ENOMEM.
Decide on the regions that are not ruby and hold a character instead.
The text itself is not a reliable test, as it can hold line breaks
without any character. Nothing to show gives no event, or the empty
clear event with CS, as in the other modes; a NULL text is taken as
empty.
Add a text mode test on the ruby sample. The ruby only caption without
CS at 5s now produces no event.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdDiDRusmi6eD7qSrKBjHb
diff --git a/libavcodec/libaribcaption.c b/libavcodec/libaribcaption.c
index 6b6345e2ef..4246681bb3 100644
--- a/libavcodec/libaribcaption.c
+++ b/libavcodec/libaribcaption.c
@@ -273,11 +273,16 @@ static void clut_add(ARIBCaptionContext *ctx, uint32_t rgba)
clut_set_alpha(ctx, CLUT_A(rgba));
}
-/* whether a region is left out: ruby with ignore_ruby, or no character */
+/* whether a region is left out: no character, or ruby when ruby is ignored */
+static int region_left_out(const aribcc_caption_region_t *region, int ignore_ruby)
+{
+ return !region->char_count || (region->is_ruby && ignore_ruby);
+}
+
static int region_skipped(const ARIBCaptionContext *ctx,
const aribcc_caption_region_t *region)
{
- return !region->char_count || (region->is_ruby && ctx->ignore_ruby);
+ return region_left_out(region, ctx->ignore_ruby);
}
/* add the character colors of a region to the CLUT */
@@ -815,14 +820,23 @@ static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx)
{
AVSubtitle *sub = ctx->sub;
AVSubtitleRect *rect;
- int ret = 0;
- const char *text;
-
- /* Only an explicit clear screen (CS) erases the previous caption;
- a statement containing only TIME also has no region. */
- if (ctx->caption.region_count == 0 &&
- !(ctx->caption.flags & ARIBCC_CAPTIONFLAGS_CLEARSCREEN))
- return 0;
+ int ret = 0, has_text = 0;
+ const char *text = ctx->caption.text ? ctx->caption.text : "";
+
+ /* The text leaves ruby out (except a placeholder for an unknown DRCS)
+ and may hold line breaks without any character, so decide on the
+ regions. */
+ for (int i = 0; i < ctx->caption.region_count && !has_text; i++)
+ has_text = !region_left_out(&ctx->caption.regions[i], 1);
+
+ if (!has_text) {
+ /* Nothing to show: there is no region, or only ruby. Only an
+ explicit clear screen (CS) erases the previous caption, with an
+ empty text; a statement containing only TIME also has no region. */
+ if (!(ctx->caption.flags & ARIBCC_CAPTIONFLAGS_CLEARSCREEN))
+ return 0;
+ text = "";
+ }
sub->rects = av_calloc(1, sizeof(*sub->rects));
if (!sub->rects) {
@@ -838,12 +852,7 @@ static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx)
}
rect = sub->rects[0];
- if (ctx->caption.region_count == 0)
- text = ""; /* clear previous caption */
- else {
- text = ctx->caption.text;
- ff_dlog(ctx, "TEXT subtitle: %s\n", text);
- }
+ ff_dlog(ctx, "TEXT subtitle: %s\n", text);
rect->text = av_strdup(text);
if (!rect->text) {
ret = AVERROR(ENOMEM);
diff --git a/tests/fate/subtitles.mak b/tests/fate/subtitles.mak
index 060df3bb0e..34238875be 100644
--- a/tests/fate/subtitles.mak
+++ b/tests/fate/subtitles.mak
@@ -190,6 +190,11 @@ FATE_SUBTITLES_ASS-$(call DEMDEC, MPEGTS, LIBARIBCAPTION) += fate-sub-aribcaptio
fate-sub-aribcaption-ruby-ass-single: CMD = fmtstdout ass -sub_type ass -font sans-serif -ass_single_rect 1 -ignore_ruby 1 -i $(TARGET_SAMPLES)/sub/aribcaption_ruby.ts
fate-sub-aribcaption-ruby-bitmap fate-sub-aribcaption-ruby-ass fate-sub-aribcaption-ruby-ass-single: CMP = diff
+# text mode never holds ruby: only CS may turn a ruby only caption into a clear
+FATE_SUBTITLES_PROBE-$(call DEMDEC, MPEGTS, LIBARIBCAPTION) += fate-sub-aribcaption-ruby-text
+fate-sub-aribcaption-ruby-text: CMD = probeframes -sub_type text $(TARGET_SAMPLES)/sub/aribcaption_ruby.ts
+fate-sub-aribcaption-ruby-text: CMP = diff
+
FATE_SUBTITLES-$(call ENCMUX, ASS, ASS) += $(FATE_SUBTITLES_ASS-yes)
FATE_SUBTITLES += $(FATE_SUBTITLES-yes)
FATE_SUBTITLES := $(if $(CONFIG_PIPE_PROTOCOL), $(FATE_SUBTITLES))
diff --git a/tests/ref/fate/sub-aribcaption-ruby-text b/tests/ref/fate/sub-aribcaption-ruby-text
new file mode 100644
index 0000000000..1d354bb0af
--- /dev/null
+++ b/tests/ref/fate/sub-aribcaption-ruby-text
@@ -0,0 +1,27 @@
+[SUBTITLE]
+media_type=subtitle
+pts=2000000
+pts_time=2.000000
+format=1
+start_display_time=0
+end_display_time=4294967295
+num_rects=1
+[/SUBTITLE]
+[SUBTITLE]
+media_type=subtitle
+pts=3000000
+pts_time=3.000000
+format=1
+start_display_time=0
+end_display_time=4294967295
+num_rects=1
+[/SUBTITLE]
+[SUBTITLE]
+media_type=subtitle
+pts=4000000
+pts_time=4.000000
+format=1
+start_display_time=0
+end_display_time=4294967295
+num_rects=1
+[/SUBTITLE]