add ARIB caption decoder using libaribcaption#1
add ARIB caption decoder using libaribcaption#1aimoff wants to merge 10 commits intorcombs:masterfrom
Conversation
This patch add another ARIB caption decoder using libaribcaption external library. Unlike libaribb24, it supports 3 types of subtitle outputs: * text: plain text * ass: ASS formatted text * bitmap: bitmap image Default subtitle type is ass as same as libaribb24. To compile with this feature: * libaribcaption external library has to be pre-installed. https://github.com/xqq/libaribcaption * configure with `--enable-libaribcaption` option. `--enable-libaribb24` and `--enable-libaribcaption` options are not exclusive. If both enabled, libaribcaption precedes as order listed in `libavcodec/allcodecs.c`.
To support bitmap type of subtitles, remove AV_CODEC_PROP_TEXT_SUB property from codec descriptor for AV_CODEC_ID_ARIB_CAPTION. It is similar way to `libavcodec/libzvbi-teletextdec.c` (AV_CODEC_ID_DVB_TELETEXT). Instead, each subtitle decoder has to specify subtitile format. `libavcodec/libaribb24.c` uses same AV_CODEC_ID_ARIB_CAPTION and expects AV_CODEC_PROP_TEXT_SUB is defined. Thus add a line to specify text format subtitle.
Some additional properties are set for ARIB caption. * need_parsing = 0 ARIB caption doesn't require any parser. This avoids "parser not found" warning message. * need_context_update = 1 When any profiles are changed, set this flag to notify. * codecpar->width / codecpar->height Find best video stream and set frame size for ARIB_PROFILE_A type of ARIB caption.
| #if !defined(DEFAULT_SUBTITLE_TYPE) | ||
| # define DEFAULT_SUBTITLE_TYPE SUBTITLE_ASS | ||
| #endif | ||
| #if !defined(ASS_WORKAROUND) | ||
| # define ASS_WORKAROUND 1 | ||
| #endif |
There was a problem hiding this comment.
These should simply be set directly in the AVOption array.
There was a problem hiding this comment.
These provide only default behavior.
Former one can be embeded in the AVOption array. The default behavior would not be changed.
Latter one depends when most of softwares (or major softwares) can handle multi-rectangle ASS subtitle. Thus, I prefer to put it out from the AVOption array.
| const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families)); | ||
| if (!ff) | ||
| is_nomem = 1; | ||
| else { | ||
| font_families = ff; | ||
| ff[count++] = av_get_token(&fonts, ","); | ||
| if (!ff[count - 1]) | ||
| is_nomem = 1; | ||
| else if (*fonts) | ||
| fonts++; | ||
| } |
There was a problem hiding this comment.
If you get an allocation failure, you should return AVERROR(ENOMEM), rather than attempting to continue in a degraded state.
There was a problem hiding this comment.
This does't continue in a degraded state, but return AVERROR(ENOMEM) after clean up.
while (count)
av_freep(&font_families[--count]);
av_freep(&font_families);
if (is_nomem) {
aribcaption_close(avctx);
return AVERROR(ENOMEM);
}If such clean up is not needed (i.e., automatically freed), I can simply return with AVERROR(ENOMEM).
There was a problem hiding this comment.
You can add a label (e.g. fail:) and goto it on failure.
There was a problem hiding this comment.
It is a matter of preference if it is not specified in coding rule.
I prefer to keep current way because same clean up routine runs regardless of error or not.
There was a problem hiding this comment.
Instead of while (!is_nomem && *fonts) loop, I can change to add break; at allocation failure to quit from loop.
| if (fonts && *fonts) | ||
| font_name = av_get_token(&fonts, ","); | ||
| else | ||
| font_name = av_strdup(DEFAULT_FONT_FAMILY); |
There was a problem hiding this comment.
Why not just make sans-serif the default for the AVOption?
There was a problem hiding this comment.
The libaribcaption library has it's default font for bitmap depending on operating system. I would like to follow it if font option is not specified.
On the other hand, we have to set default font for ASS subtitle.
There was a problem hiding this comment.
This isn't using a default from libaribcaption, though? It's just using DEFAULT_FONT_FAMILY, which is defined as sans-serif.
There was a problem hiding this comment.
libaribcaption renders (with default font) when sub_type is bitmap only.
When sub_type is ass, libaribcaption parses ARIB caption, but does not render nor encode to ASS text.
The DEFAULT_FONT_FAMILY is set to ASS header only, but most players such as mpv set its own ASS header and specified font is ignored.
There was a problem hiding this comment.
Ahh, maybe name the macro ASS_DEFAULT_FONT or the like for clarity?
There was a problem hiding this comment.
ASS_DEFAULT_FONT is already defined in libavcodec/ass.h but is "Arial".
The font is not suitable for ARIB caption.
Which is your preference, redifine with same macro name or define another new (but ASS related) macro name?
There was a problem hiding this comment.
Hmm, maybe DEFAULT_FONT_ASS? And add a comment indicating that it's only used for ASS, because the bitmap renderer has its own internal default.
| if (v_st && | ||
| (st->codecpar->width != v_st->codecpar->width || | ||
| st->codecpar->height != v_st->codecpar->height)) { | ||
| st->codecpar->width = v_st->codecpar->width; | ||
| st->codecpar->height = v_st->codecpar->height; | ||
| sti->need_context_update = 1; | ||
| } |
There was a problem hiding this comment.
This section isn't appropriate. Instead, provide an AVOption with type AV_OPT_TYPE_IMAGE_SIZE in your decoder, defaulting to the layout dimensions derived from the subtitle stream itself, and allow player apps to set it to an appropriate value (this is usually the output resolution) when using the bitmap renderer.
There was a problem hiding this comment.
I add -canvas_size option to specify input video size.
The same option already exists in ffmpeg tool, and it set width and height in AVCodecContext. Since ffmpeg tool doesn't pass -canvas_size option to libaribcaption, thus I use the values in AVCodecContext. The -canvas_size option in AVOption is used by other tools such as ffplay.
Note that the canvas_size specifies frame size of input video (i.e., SAR basis). Actual canvas size is stretched to 16:9 to match DAR-derived video size.
| ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE; | ||
|
|
||
| encode_scheme = ARIBCC_ENCODING_SCHEME_AUTO; | ||
| if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_DO_NOTHING) |
There was a problem hiding this comment.
This is meant to control libavcodec's built-in encoding conversion infrastructure. If you want to let callers control the encoding libaribcaption uses, you should provide an AVOption defaulting to ARIBCC_ENCODING_SCHEME_AUTO.
There was a problem hiding this comment.
Encoding depends on broadcast source (i.e., country).
Most of cases, it should be determined automatically.
It is last resort when it cannot be determined automatically.
If sub_charenc_mode is not suitable means, I'll add one in AVOption.
There was a problem hiding this comment.
Tentatively, I add -encoding_scheme option locally and specify it as same value as aribcc_encoding_scheme_t:
- auto
- jis
- utf8
- latin
I concern the option name might be too generic and it may conflct with other codecs.
There was a problem hiding this comment.
Nice! If you want to be more specific, you can use arib_text_encoding or the like, but I don't think it's a big deal either way.
There was a problem hiding this comment.
I determined the option name as -caption_encoding.
This should not be confused.
| if (ret != 0) { | ||
| av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n", | ||
| av_err2str(ret)); | ||
| aribcaption_close(avctx); |
There was a problem hiding this comment.
You have a lot of calls to aribcaption_close on error paths in this function. You can instead set FF_CODEC_CAP_INIT_CLEANUP in caps_internal, which will result in libavcodec automatically calling your close routine if initialization fails.
| /* clear previous caption for indefinite duration */ | ||
| ff_ass_add_rect(sub, "{\\r}", ctx->readorder++, 0, NULL, NULL); | ||
| return 1; |
There was a problem hiding this comment.
The {\r} shouldn't be necessary here; you should be able to just use an empty string.
| @item -ass_workaround @var{boolean} | ||
| Since some players (e.g., @dfn{mpv}) can't handle multi-rectangle ASS | ||
| subtitle properly, default behavior is all the texts are displayed in | ||
| a single-rectangle at a time. |
There was a problem hiding this comment.
What behavior are you seeing in mpv with this set to false? If this isn't working properly, it should be fixed (either in the decoder or in mpv).
There was a problem hiding this comment.
There is a mpv implementation that can handle multi-rectangle ASS subtitle:
https://github.com/0p1pp1/mpv
You can find typical case in https://streams.videolan.org/streams/ts/ARIB/japan/osaka_15.ts around 00:03:35.

2 captions are displayed on each person at a same time.
You can see same scene with ffplay -sub_type bitmap osaka_15.ts after this patchset is applied.
Current plain mpv cannot handle multi-rectangle ASS subtitle.
If plain mpv plays it with -ass_workaround false, first caption is immediately clear after second caption is displayed; i.e., only second caption can be seen.
There was a problem hiding this comment.

This works fine in current mpv (mpv-player/mpv@7990dd8) for me?
There was a problem hiding this comment.
Oh, my knowledge is little bit old.
I found mpv release/0.35 can handle multi-rectangle ASS subtitles.
I would like change default value of ass_workaround to 0, but stay default definition to be outside of AVOption.
There was a problem hiding this comment.
I don't understand why you want the default to be a macro.
Also, I'd suggest naming the option single_rect to clarify its behavior.
There was a problem hiding this comment.
The reason is explained in later description (but should amend the explanation to revert default value).
The default is @var{true}.
If a player can handle multi-rectangle ASS subtitle and you prefer
more suitable positioning, set this option to @var{false} or define
@env{ASS_WORKAROUND=0} to change default behavior at compilation.This can easily change default behavior without change the code.
There was a problem hiding this comment.
I will change the option name to ass_single_rect.
| if (ctx->clut) | ||
| av_freep(&ctx->clut); |
There was a problem hiding this comment.
You don't need to if around this; av_freep(&x) where x is null is a no-op.
| if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color)) | ||
| av_bprintf(buf, "{\\%dc&H%06x&}", color_num, | ||
| ARIBCC_COLOR_RGB(new_color)); | ||
| if (ARIBCC_COLOR_DIFF_A(new_color, old_color) && ARIBCC_COLOR_A(new_color)) |
There was a problem hiding this comment.
This should check if the alpha is different; if it changes to 0, we should set that.
There was a problem hiding this comment.
Actual ISDB broadcasting doesn't allways set alpha value, and 0 is set in many cases.
Alpha value 0 means transparent, and as the result subtitle cannot be seen if we strictly obey it.
There was a problem hiding this comment.
0 in ASS means fully opaque; 0xFF means fully transparent. I think you need to invert the value here.
I don't see any corresponding code in libaribcaption's renderer to ignore 0 values?
There was a problem hiding this comment.
Is it true?
If so, above line 559 should not changed. Instead, ASS style override code number would be 0xFF - alpha.
There was a problem hiding this comment.
Correct, though I'm still confused about why this check is necessary when libaribcaption's renderer doesn't seem to have anything equivalent (unless I'm missing something?). In any case, it at least needs a comment explaining why.
There was a problem hiding this comment.
I checked some TS files on my hand. I can't find text color with alpha 0. So, I might see apparition.
I'll remove the check.
| FF_CODEC_DECODE_SUB_CB(aribcaption_decode), | ||
| .flush = aribcaption_flush, | ||
| .p.priv_class= &aribcaption_class, | ||
| .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, |
There was a problem hiding this comment.
This patch doesn't build as-is, because FF_CODEC_CAP_INIT_THREADSAFE is now the default, and the macro has been removed.
| av_bprintf(&buf, "{\\r}%s", | ||
| (i + 1 < ctx->caption.region_count) ? "\\N" : ""); |
There was a problem hiding this comment.
This line only makes sense on the ass_workaround path, and the {\\r} is only necessary if inserting the newline, so this could be:
| av_bprintf(&buf, "{\\r}%s", | |
| (i + 1 < ctx->caption.region_count) ? "\\N" : ""); | |
| if (ctx->ass_workaround && (i + 1 < ctx->caption.region_count)) | |
| av_bprintf(&buf, "{\\r}\\N"); |
There was a problem hiding this comment.
The {\\r} is diverted from former libaribb24.
It seems it is not required for multi-rect ASS, I will change the code as you suggested.
* rename `DEFAULT_FONT_FAMILY` macro to `DEFAULT_FONT_ASS`.
* rename `ass_workaround` option to `ass_single_rect`.
* remove unnecessary check before `av_freep()`.
* remove unnecessary `"{\r}"` output at end of each ASS rect.
* remove `FF_CODEC_CAP_INIT_THREADSAFE` and set `FF_CODEC_CAP_INIT_CLEANUP`
flag in `.caps_internal`, then remove unnecessary `aribcaption_close()`.
* remove `DEFAULT_SUBTITLE_TYPE` macro and embed default `-sub_type` option
in `AVOption` structure.
* add `-caption_encoding` to specify encoding of subtitle text.
* specify input video size by `-canvas_size` option.
* some cosmetic changes.
|
I pushed latest code that reflect @rcombs comments. |
* change behavior of ASS subtitle with PROFILE_C to be single rectangle. * document more detail of `-canvas_size` option. * some cosmetic changes.
|
I changed behavior of ARIB caption with PROFILE_C. |
rcombs
left a comment
There was a problem hiding this comment.
A few more comments, mostly regarding docs.
| Since some players (e.g., old @dfn{mpv}) can't handle multi-rectangle ASS | ||
| subtitle properly, this option can change the behavior so that all the texts | ||
| are displayed in a single-rectangle. |
There was a problem hiding this comment.
"can't handle multiple ASS rectangles in a single AVSubtitle, or multiple ASS rectangles of indeterminate duration with the same start timestamp"
also, "in a single rectangle"
| If a player can handle multi-rectangle ASS subtitle and you prefer | ||
| more suitable positioning, set this option to @var{false} or define | ||
| @env{ASS_WORKAROUND=0} to change default behavior at compilation. | ||
| If your player cannot handle multi-rectangle ASS subtitle properly, |
There was a problem hiding this comment.
"cannot handle AVSubtitles with multiple ASS rectangles properly,"
| The default is @var{true}. | ||
|
|
||
| @item -canvas_size @var{image_size} | ||
| Specify input video size to determine size of canvas to render subtitle. |
There was a problem hiding this comment.
First sentence is still under considering as discussed below.
|
|
||
| enum AVSubtitleType subtitle_type; | ||
| bool ass_workaround; | ||
| enum aribcc_encoding_scheme_t encoding_scheme; |
There was a problem hiding this comment.
This should be an int, since enum aribcc_encoding_scheme_t isn't guaranteed to have the same size as int across all platforms.
| @item -canvas_size @var{image_size} | ||
| Specify input video size to determine size of canvas to render subtitle. | ||
|
|
||
| The aribcaption decoder assumes input frame size for bitmap rendering as bellow: |
| if (ctx->avctx->profile == FF_PROFILE_ARIB_PROFILE_C) | ||
| single_rect = true; |
There was a problem hiding this comment.
The profile C is defined for devices that have very limited resources.
Please refer ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] "Operational provisions related to C-profile". The section has been removed from version 6.7. The Table 3-1 explains that the caption may be up to 4 lines, and Table 3-2 explains the position is not provided by contents.
Thus, I think it is better to treat it as single ASS rectangle.
At present, mpv doesn't display ASS subtitle of profile C correctly even if this change is applied. I don't know the reason. Output of ffmpeg -i INPUT.TS -map STREM_ID_OF_PROFILE_C OUTPUT.ass seems to be correct, but mpv displays garbled characters.
In addition, similar change should be made for bitmap subtiles, but I don't have easy resolution.
There was a problem hiding this comment.
Do you have a sample file I can take a look at?
If you decide to keep this, you should include a comment noting that profile C doesn't allow for positioning.
There was a problem hiding this comment.
You can see it in the same TS file:
https://streams.videolan.org/streams/ts/ARIB/japan/osaka_15.ts
ffmpeg -fix_sub_duration -i osaka_15.ts -map 0:14 osaka_15.ass
mpv osaka_15.ts --vid=2 --aid=2 --sid=2
Recently @jeeb found the root cause and sent a patch to mpv:
mpv-player/mpv#11340
You can see the subtitles by mpv with this patch.
There was a problem hiding this comment.
Would definitely be worth reporting to @xqq. In any case, worth a comment for now :)
There was a problem hiding this comment.
I already shared such information with @xqq.
With latest patch for this PR, you can see bitmap subtitle of profile C by ffplay.
ffplay -sub_type bitmap osaka_15.ts -vst 13 -ast 12 -sst 14
| { | ||
| ARIBCaptionContext *ctx = avctx->priv_data; | ||
|
|
||
| if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) { |
There was a problem hiding this comment.
This flag should only affect setting readorder; the rest of the function should run regardless.
| if (ctx->renderer) { | ||
| aribcc_renderer_free(ctx->renderer); | ||
| ctx->renderer = NULL; | ||
| } | ||
| if (ctx->decoder) { | ||
| aribcc_decoder_free(ctx->decoder); | ||
| ctx->decoder = NULL; | ||
| } | ||
| if (ctx->context) { | ||
| aribcc_context_free(ctx->context); | ||
| ctx->context = NULL; | ||
| } |
There was a problem hiding this comment.
- none of these checks should be required; these functions are safe to pass NULL to
- it's not necessary to zero the pointers, since the
ctxis about to be freed anyway.
There was a problem hiding this comment.
these functions are safe to pass NULL to
I'm not sure but it depends on current libaribcaption's implementation (not on the spec).
I woule like to keep the checks.
I'll accept latter suggestion.
| { | ||
| ARIBCaptionContext *ctx = avctx->priv_data; | ||
|
|
||
| aribcaption_flush(avctx); |
There was a problem hiding this comment.
It shouldn't be necessary to call flush within close.
| ARIBCaptionContext *ctx = userdata; | ||
| int lvl; | ||
|
|
||
| if (ctx->decoder != NULL) { |
There was a problem hiding this comment.
Any particular reason for this check?
There was a problem hiding this comment.
I don't remenber the details, but I caught some crashes without the check.
Probably, the callback might be called after the context has been freed.
Documentation is still in progress
The document is slightly changed.
Don't calculate position of single rect for profile C
|
I believe almost done. |
| av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n"); | ||
| return AVERROR(EINVAL); |
There was a problem hiding this comment.
Oh, you'll want to provide an AVOption to set a default profile, like in the existing decoder; default it to a since that's what most streams are.
There was a problem hiding this comment.
Does it mean to apply simiar patch as following?
https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2022-November/303578.html
I don't undestand your intention.
On current lavf/mpegts.c code, such streams are not treated as ARIB caption.
If the profile is unkonwn, the codec id is not set, and libarib* decoder will not be called.
Lines 2138 to 2181 in 5bad485
There was a problem hiding this comment.
It's possible to get arib caption streams from other containers or sources (eg matroska now), or to manually instruct ffmpeg to treat a stream as containing a particular codec if it isn't detected properly (eg due to missing descriptors).
There was a problem hiding this comment.
I don't prefer such way.
Of course you can submit another patch after this patch set will be merged.
There was a problem hiding this comment.
I don't know actual code and/or status of matroska.
But I prefer such workaround will be treated in libavformat.
I think more flexible decision can be made in libavformat.
PR for review of patchset sent to FFmpeg ML.
This patch set add another ARIB caption decoder using libaribcaption
external library: https://github.com/xqq/libaribcaption
The library decodes subtitles of ISDB-based TV broadcasting.
It is not only for Japanese ARIB STD-B24 caption, but also for
Brazilian ABNT NBR 15606-1 and Philippines version of ISDB-T.
Unlike libaribb24, it supports 3 types of subtitle outputs:
Default subtitle type is ASS as same as libaribb24, but it can
be changed by
-sub_typeoption.You can see ARIB caption with ffplay tool:
ffplay -sub_type bitmap MPEG.TS
Sample files exist under:
https://streams.videolan.org/streams/ts/ARIB/
Some of them are encrypted and some don't contain ARIB caption data.
Good samples for ARIB caption are:
brazil/07-25_20-33-35_UCV - HD_.ts
japan/channel2[137]_clear.ts
japan/osaka_15.ts