History

lavc/libaribcaption.c: add ARIB caption decoder using libaribcaption

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. Advantages compared with libaribb24 on ASS subtitle are: * Subtitle positioning. * Multi-rect subtitle: some captions are displayed at different position at a time. * More stability and reproducibility. 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`.

TADANO Tokumei committed 3 years ago

Commit b6138631 parent 38bb1376 files · +1329 −0Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. configure +4 −0
  3. doc/decoders.texi +150 −0
  4. libavcodec/Makefile +1 −0
  5. libavcodec/allcodecs.c +2 −0
  6. libavcodec/libaribcaption.c +1171 −0
Changelog+1−0
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release, · 1 unmodified line
22 releases are sorted from youngest to oldest.
33
44 version <next>:
5+- libaribcaption decoder
56
67 version 6.0:
78 - Radiance HDR image support
configure+4−0
@@ -218,6 +218,7 @@ External library support: · 217 unmodified lines
218218 --enable-lcms2 enable ICC profile support via LittleCMS 2 [no]
219219 --enable-libaom enable AV1 video encoding/decoding via libaom [no]
220220 --enable-libaribb24 enable ARIB text and caption decoding via libaribb24 [no]
221+ --enable-libaribcaption enable ARIB text and caption decoding via libaribcaption [no]
221222 --enable-libass enable libass subtitles rendering,
222223 needed for subtitles and ass filter [no]
223224 --enable-libbluray enable BluRay reading using libbluray [no]
@@ -1805,6 +1806,7 @@ EXTERNAL_LIBRARY_LIST=" · 1581 unmodified lines
18051806 ladspa
18061807 lcms2
18071808 libaom
1809+ libaribcaption
18081810 libass
18091811 libbluray
18101812 libbs2b
@@ -3331,6 +3333,7 @@ libaom_av1_decoder_deps="libaom" · 1520 unmodified lines
33313333 libaom_av1_encoder_deps="libaom"
33323334 libaom_av1_encoder_select="extract_extradata_bsf"
33333335 libaribb24_decoder_deps="libaribb24"
3336+libaribcaption_decoder_deps="libaribcaption"
33343337 libcelt_decoder_deps="libcelt"
33353338 libcodec2_decoder_deps="libcodec2"
33363339 libcodec2_encoder_deps="libcodec2"
@@ -6569,6 +6572,7 @@ enabled libaom && require_pkg_config libaom "aom >= 1.0.0" aom/aom_co · 3232 unmodified lines
65696572 enabled libaribb24 && { check_pkg_config libaribb24 "aribb24 > 1.0.3" "aribb24/aribb24.h" arib_instance_new ||
65706573 { enabled gpl && require_pkg_config libaribb24 aribb24 "aribb24/aribb24.h" arib_instance_new; } ||
65716574 die "ERROR: libaribb24 requires version higher than 1.0.3 or --enable-gpl."; }
6575+enabled libaribcaption && require_pkg_config libaribcaption "libaribcaption >= 0.1.0" "aribcaption/aribcaption.h" aribcc_context_alloc
65726576 enabled lv2 && require_pkg_config lv2 lilv-0 "lilv/lilv.h" lilv_world_new
65736577 enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883
65746578 enabled libass && require_pkg_config libass "libass >= 0.11.0" ass/ass.h ass_library_init
doc/decoders.texi+150−0
@@ -353,6 +353,156 @@ Enabled by default. · 352 unmodified lines
353353
354354 @end table
355355
356+@section libaribcaption
357+
358+Yet another ARIB STD-B24 caption decoder using external @dfn{libaribcaption}
359+library.
360+
361+Implements profiles A and C of the Japanse ARIB STD-B24 standard,
362+Brazilian ABNT NBR 15606-1, and Philippines version of ISDB-T.
363+
364+Requires the presence of the libaribcaption headers and library
365+(@url{https://github.com/xqq/libaribcaption}) during configuration.
366+You need to explicitly configure the build with @code{--enable-libaribcaption}.
367+If both @dfn{libaribb24} and @dfn{libaribcaption} are enabled, @dfn{libaribcaption}
368+decoder precedes.
369+
370+@subsection libaribcaption Decoder Options
371+
372+@table @option
373+
374+@item -sub_type @var{subtitle_type}
375+Specifies the format of the decoded subtitles.
376+
377+@table @samp
378+@item bitmap
379+Graphical image.
380+@item ass
381+ASS formatted text.
382+@item text
383+Simple text based output without formatting.
384+@end table
385+
386+The default is @dfn{ass} as same as @dfn{libaribb24} decoder.
387+Some present players (e.g., @dfn{mpv}) expect ASS format for ARIB caption.
388+
389+@item -caption_encoding @var{encoding_scheme}
390+Specifies the encoding scheme of input subtitle text.
391+
392+@table @samp
393+@item auto
394+Automatically detect text encoding.
395+@item jis
396+8bit-char JIS encoding defined in ARIB STD B24.
397+This encoding used in Japan for ISDB captions.
398+@item utf8
399+UTF-8 encoding defined in ARIB STD B24.
400+This encoding is used in Philippines for ISDB-T captions.
401+@item latin
402+Latin character encoding defined in ABNT NBR 15606-1.
403+This encoding is used in South America for SBTVD / ISDB-Tb captions.
404+@end table
405+
406+The default is @dfn{ass} as same as @dfn{libaribb24} decoder.
407+Some present players (e.g., @dfn{mpv}) expect ASS format for ARIB caption.
408+
409+@item -font @var{font_name[,font_name2,...]}
410+Specify comma-separated list of font family names to be used for @dfn{bitmap}
411+or @dfn{ass} type subtitle rendering.
412+Only first font name is used for @dfn{ass} type subtitle.
413+
414+If not specified, use internaly defined default font family.
415+
416+@item -ass_single_rect @var{boolean}
417+ARIB STD-B24 specifies that some captions may be displayed at different
418+positions at a time (multi-rectangle subtitle).
419+Since some players (e.g., old @dfn{mpv}) can't handle multiple ASS rectangles
420+in a single AVSubtitle, or multiple ASS rectangles of indeterminate duration
421+with the same start timestamp, this option can change the behavior so that
422+all the texts are displayed in a single ASS rectangle.
423+
424+The default is @var{false}.
425+
426+If your player cannot handle AVSubtitles with multiple ASS rectangles properly,
427+set this option to @var{true} or define @env{ASS_SINGLE_RECT=1} to change
428+default behavior at compilation.
429+
430+@item -replace_fullwidth_ascii @var{boolean}
431+Specify whether to replace MSZ (Middle Size, half width) fullwidth
432+alphanumerics with halfwidth alphanumerics.
433+
434+The default is @var{true}.
435+
436+@item -force_outline_text @var{boolean}
437+Specify whether always render outline text for all characters regardless of
438+the indication by charactor style.
439+
440+The default is @var{false}.
441+
442+@item -outline_width @var{number} (0.0 - 3.0)
443+Specify width for outline text, in dots (relative).
444+
445+The default is @var{1.5}.
446+
447+@item -ignore_background @var{boolean}
448+Specify whether to ignore background color rendering.
449+
450+The default is @var{false}.
451+
452+@item -ignore_ruby @var{boolean}
453+Specify whether to ignore rendering for ruby-like (furigana) characters.
454+
455+The default is @var{false}.
456+
457+@item -replace_drcs @var{boolean}
458+Specify whether to render replaced DRCS characters as Unicode characters.
459+
460+The default is @var{true}.
461+
462+@item -canvas_size @var{image_size}
463+Specify the resolution of the canvas to render subtitles to; usually, this
464+should be frame size of input video.
465+This only applies when @code{-subtitle_type} is set to @var{bitmap}.
466+
467+The libaribcaption decoder assumes input frame size for bitmap rendering as below:
468+@enumerate
469+@item
470+PROFILE_A : 1440 x 1080 with SAR (PAR) 4:3
471+@item
472+PROFILE_C : 320 x 180 with SAR (PAR) 1:1
473+@end enumerate
474+
475+If actual frame size of input video does not match above assumption,
476+the rendered captions may be distorted.
477+To make the captions undistorted, add @code{-canvas_size} option to specify
478+actual input video size.
479+
480+Note that the @code{-canvas_size} option is not required for video with
481+different size but same aspect ratio.
482+In such cases, the caption will be stretched or shrunk to actual video size
483+if @code{-canvas_size} option is not specified.
484+If @code{-canvas_size} option is specified with different size,
485+the caption will be stretched or shrunk as specified size with calculated SAR.
486+
487+@end table
488+
489+@subsection libaribcaption decoder usage examples
490+
491+Display MPEG-TS file with ARIB subtitle by @code{ffplay} tool:
492+@example
493+ffplay -sub_type bitmap MPEG.TS
494+@end example
495+
496+Display MPEG-TS file with input frame size 1920x1080 by @code{ffplay} tool:
497+@example
498+ffplay -sub_type bitmap -canvas_size 1920x1080 MPEG.TS
499+@end example
500+
501+Embed ARIB subtitle in transcoded video:
502+@example
503+ffmpeg -sub_type bitmap -i src.m2t -filter_complex "[0:v][0:s]overlay" -vcodec h264 dest.mp4
504+@end example
505+
356506 @section dvbsub
357507
358508 @subsection Options
libavcodec/Makefile+1−0
@@ -1079,6 +1079,7 @@ OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER) += audiotoolboxenc.o · 1078 unmodified lines
10791079 OBJS-$(CONFIG_LIBAOM_AV1_DECODER) += libaomdec.o libaom.o
10801080 OBJS-$(CONFIG_LIBAOM_AV1_ENCODER) += libaomenc.o libaom.o
10811081 OBJS-$(CONFIG_LIBARIBB24_DECODER) += libaribb24.o ass.o
1082+OBJS-$(CONFIG_LIBARIBCAPTION_DECODER) += libaribcaption.o ass.o
10821083 OBJS-$(CONFIG_LIBCELT_DECODER) += libcelt_dec.o
10831084 OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o
10841085 OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o
libavcodec/allcodecs.c+2−0
@@ -759,6 +759,8 @@ extern const FFCodec ff_pcm_mulaw_at_decoder; · 758 unmodified lines
759759 extern const FFCodec ff_qdmc_at_decoder;
760760 extern const FFCodec ff_qdm2_at_decoder;
761761 extern FFCodec ff_libaom_av1_encoder;
762+/* preferred over libaribb24 */
763+extern const FFCodec ff_libaribcaption_decoder;
762764 extern const FFCodec ff_libaribb24_decoder;
763765 extern const FFCodec ff_libcelt_decoder;
764766 extern const FFCodec ff_libcodec2_encoder;
libavcodec/libaribcaption.c+1171−0
@@ -0,0 +1,1171 @@
1+/*
2+ * ARIB STD-B24 caption decoder using the libaribcaption library
3+ * Copyright (c) 2022 TADANO Tokumei
4+ *
5+ * This file is part of FFmpeg.
6+ *
7+ * FFmpeg is free software; you can redistribute it and/or
8+ * modify it under the terms of the GNU Lesser General Public
9+ * License as published by the Free Software Foundation; either
10+ * version 2.1 of the License, or (at your option) any later version.
11+ *
12+ * FFmpeg is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public
18+ * License along with FFmpeg; if not, write to the Free Software
19+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+ */
21+
22+#include "avcodec.h"
23+#include "codec_internal.h"
24+#include "internal.h"
25+#include "libavcodec/ass.h"
26+#include "libavutil/avstring.h"
27+#include "libavutil/avutil.h"
28+#include "libavutil/thread.h"
29+#include "libavutil/log.h"
30+#include "libavutil/opt.h"
31+
32+#include <aribcaption/aribcaption.h>
33+
34+#if !defined(DEFAULT_FONT_ASS)
35+# define DEFAULT_FONT_ASS "sans-serif"
36+#endif
37+
38+#define ARIBC_BPRINT_SIZE_INIT 64
39+#define ARIBC_BPRINT_SIZE_MAX (8 * 1024)
40+#define ARIBC_ALPHA_MAX_NUM 4
41+#define ARIBC_ALPHA_DEFAULT_FRONT 0xFF
42+#define ARIBC_ALPHA_DEFAULT_BACK 0x80
43+
44+#define ARIBCC_COLOR_RGB(c) ((c) & 0xFFFFFF)
45+#define ARIBCC_COLOR_DIFF_RGB(c1,c2) (((c1) ^ (c2)) & 0x00FFFFFF)
46+#define ARIBCC_COLOR_DIFF_A(c1,c2) (((c1) ^ (c2)) & 0xFF000000)
47+
48+#define CLUT_RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
49+#define CLUT_A(c) (((c) >> 24) & 0xFF)
50+#define CLUT_R(c) (((c) >> 16) & 0xFF)
51+#define CLUT_G(c) (((c) >> 8) & 0xFF)
52+#define CLUT_B(c) ( (c) & 0xFF)
53+
54+#define ARIBCC_COLOR_TO_CLUT_RGBA(c,a) (((ARIBCC_COLOR_A(c) ? ARIBCC_COLOR_A(c) : (a)) << 24) | \
55+ (ARIBCC_COLOR_R(c) << 16) | \
56+ (ARIBCC_COLOR_G(c) << 8) | \
57+ (ARIBCC_COLOR_B(c)))
58+
59+typedef struct ARIBCaptionContext {
60+ AVClass *class;
61+ AVCodecContext *avctx;
62+ const AVPacket *avpkt;
63+ AVSubtitle *sub;
64+
65+ aribcc_context_t *context;
66+ aribcc_decoder_t *decoder;
67+ aribcc_renderer_t *renderer;
68+
69+ int subtitle_type;
70+ int encoding_scheme;
71+ bool ass_single_rect;
72+ char *font;
73+ bool replace_fullwidth_ascii;
74+ bool force_stroke_text;
75+ bool ignore_background;
76+ bool ignore_ruby;
77+ float stroke_width;
78+ bool replace_drcs;
79+
80+ int64_t pts;
81+ AVRational time_base;
82+ int canvas_width;
83+ int canvas_height;
84+ int plane_width;
85+ int plane_height;
86+ int frame_width;
87+ int frame_height;
88+ int bitmap_plane_width;
89+ int bitmap_plane_height;
90+ int font_size;
91+ int charstyle;
92+ int border_style;
93+ int readorder;
94+
95+ aribcc_caption_t caption;
96+ aribcc_render_result_t render_result;
97+ uint32_t *clut;
98+ int clut_idx;
99+ int clut_overflow;
100+ uint8_t clut_alpha[ARIBC_ALPHA_MAX_NUM];
101+} ARIBCaptionContext;
102+
103+static void hex_dump_debug(void *ctx, const char *buf, int buf_size)
104+{
105+ int i;
106+
107+ for (i = 0; i < buf_size; i++) {
108+ ff_dlog(ctx, "%02hhx ", buf[i]);
109+ if (i % 16 == 15)
110+ ff_dlog(ctx, "\n");
111+ }
112+ if (i % 16)
113+ ff_dlog(ctx, "\n");
114+}
115+
116+static void logcat_callback(aribcc_loglevel_t level, const char* message, void* userdata)
117+{
118+ ARIBCaptionContext *ctx = userdata;
119+ int lvl;
120+
121+ if (ctx->decoder != NULL) {
122+ switch (level) {
123+ case ARIBCC_LOGLEVEL_ERROR:
124+ lvl = AV_LOG_ERROR;
125+ break;
126+ case ARIBCC_LOGLEVEL_WARNING:
127+ lvl = AV_LOG_WARNING;
128+ break;
129+ default:
130+ lvl = AV_LOG_INFO;
131+ }
132+
133+ av_log(ctx, lvl, "%s\n", message);
134+ }
135+}
136+
137+static void estimate_video_frame_size(ARIBCaptionContext *ctx)
138+{
139+ if (ctx->avctx->width > 0 && ctx->avctx->height > 0) {
140+ /* input video size specified by -canvas_size option */
141+ ctx->bitmap_plane_width = ctx->avctx->width;
142+ ctx->bitmap_plane_height = ctx->avctx->height;
143+ } else if (ctx->plane_width == 960) {
144+ /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] 4.3.1 */
145+ /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] Appendix-4 */
146+ ctx->bitmap_plane_width = 1440;
147+ ctx->bitmap_plane_height = 1080;
148+ } else {
149+ ctx->bitmap_plane_width = ctx->plane_width;
150+ ctx->bitmap_plane_height = ctx->plane_height;
151+ }
152+ /* Expand either width or height */
153+ if (ctx->bitmap_plane_height * ctx->plane_width > ctx->bitmap_plane_width * ctx->plane_height) {
154+ ctx->frame_height = ctx->bitmap_plane_height;
155+ ctx->frame_width = ctx->frame_height * ctx->plane_width / ctx->plane_height;
156+ } else {
157+ ctx->frame_width = ctx->bitmap_plane_width;
158+ ctx->frame_height = ctx->frame_width * ctx->plane_height / ctx->plane_width;
159+ }
160+}
161+
162+static void clut_set_alpha(ARIBCaptionContext *ctx, uint8_t a)
163+{
164+ int i;
165+
166+ for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
167+ if (ctx->clut_alpha[i] == 0) {
168+ ctx->clut_alpha[i] = a;
169+ return;
170+ }
171+ if (ctx->clut_alpha[i] == a)
172+ return;
173+ }
174+ return;
175+}
176+
177+static uint8_t clut_find_nearlest_alpha(ARIBCaptionContext *ctx, uint8_t a)
178+{
179+ int i, j, d;
180+
181+ if (a == 0)
182+ return a;
183+ d = 256;
184+ j = 0;
185+ for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
186+ if (ctx->clut_alpha[i] == a)
187+ return a;
188+ if (ctx->clut_alpha[i] == 0)
189+ break;
190+ if (abs((int)a - (int)ctx->clut_alpha[i]) < d) {
191+ d = abs((int)a - (int)ctx->clut_alpha[i]);
192+ j = i;
193+ }
194+ }
195+ return ctx->clut_alpha[j];
196+}
197+
198+static int clut_find(ARIBCaptionContext *ctx, uint32_t rgba)
199+{
200+ int i;
201+
202+ for (i = 0; i < ctx->clut_idx; i++) {
203+ if (ctx->clut[i] == rgba)
204+ return i;
205+ }
206+ return -1;
207+}
208+
209+static inline int clut_color_distance(uint32_t rgba1, uint32_t rgba2)
210+{
211+ return abs((int)CLUT_R(rgba1) - (int)CLUT_R(rgba2)) +
212+ abs((int)CLUT_G(rgba1) - (int)CLUT_G(rgba2)) +
213+ abs((int)CLUT_B(rgba1) - (int)CLUT_B(rgba2));
214+}
215+
216+static uint8_t clut_pick_or_set(ARIBCaptionContext *ctx, int r, int g, int b, int a)
217+{
218+ int c, i, d, d_min;
219+ uint32_t rgba;
220+
221+ a = clut_find_nearlest_alpha(ctx, a);
222+ if (a == 0)
223+ return 0; /* transparent */
224+ rgba = CLUT_RGBA(r,g,b,a);
225+
226+ d_min = 256 * 3;
227+ c = 0;
228+ for (i = 0; i < ctx->clut_idx; i++) {
229+ if (ctx->clut[i] == rgba)
230+ return i;
231+ if (CLUT_A(ctx->clut[i]) != a)
232+ continue;
233+ d = clut_color_distance(ctx->clut[i], rgba);
234+ if (d < d_min) {
235+ d_min = d;
236+ c = i;
237+ }
238+ }
239+ if (d_min > 3) {
240+ if (ctx->clut_idx >= AVPALETTE_COUNT)
241+ ctx->clut_overflow++;
242+ else {
243+ c = ctx->clut_idx;
244+ ctx->clut[ctx->clut_idx++] = rgba;
245+ }
246+ }
247+ return c;
248+}
249+
250+/* initialiaze CLUT with each character colors */
251+static void clut_init(ARIBCaptionContext *ctx, aribcc_caption_region_t *region)
252+{
253+ aribcc_color_t text_color, back_color, stroke_color;
254+ uint32_t rgba;
255+
256+ ctx->clut[0] = CLUT_RGBA(0,0,0,0); /* transparent */
257+ ctx->clut_alpha[0] = 0xFF;
258+ ctx->clut_idx = 1;
259+ ctx->clut_overflow = 0;
260+ text_color = region->chars[0].text_color;
261+ back_color = region->chars[0].back_color;
262+ stroke_color = region->chars[0].stroke_color;
263+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(text_color, ARIBC_ALPHA_DEFAULT_FRONT);
264+ ctx->clut[ctx->clut_idx++] = rgba;
265+ clut_set_alpha(ctx, CLUT_A(rgba));
266+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(back_color, ARIBC_ALPHA_DEFAULT_BACK);
267+ ctx->clut[ctx->clut_idx++] = rgba;
268+ clut_set_alpha(ctx, CLUT_A(rgba));
269+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(stroke_color, ARIBC_ALPHA_DEFAULT_FRONT);
270+ if (clut_find(ctx, rgba) < 0) {
271+ ctx->clut[ctx->clut_idx++] = rgba;
272+ clut_set_alpha(ctx, CLUT_A(rgba));
273+ }
274+
275+ for (int i = 1; i < region->char_count; i++) {
276+ if (region->chars[i].text_color != text_color) {
277+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].text_color,
278+ ARIBC_ALPHA_DEFAULT_FRONT);
279+ if (clut_find(ctx, rgba) < 0) {
280+ ctx->clut[ctx->clut_idx++] = rgba;
281+ clut_set_alpha(ctx, CLUT_A(rgba));
282+ }
283+ }
284+ if (region->chars[i].back_color != back_color) {
285+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].back_color,
286+ ARIBC_ALPHA_DEFAULT_BACK);
287+ if (clut_find(ctx, rgba) < 0) {
288+ ctx->clut[ctx->clut_idx++] = rgba;
289+ clut_set_alpha(ctx, CLUT_A(rgba));
290+ }
291+ }
292+ if (region->chars[i].stroke_color != stroke_color) {
293+ rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].stroke_color,
294+ ARIBC_ALPHA_DEFAULT_FRONT);
295+ if (clut_find(ctx, rgba) < 0) {
296+ if (ctx->clut_idx < AVPALETTE_COUNT)
297+ ctx->clut[ctx->clut_idx++] = rgba;
298+ clut_set_alpha(ctx, CLUT_A(rgba));
299+ }
300+ }
301+ }
302+}
303+
304+/**
305+ * aribcaption_trans_{bitmap|ass|text}_subtitle()
306+ *
307+ * Transfer decoded subtitle to AVSubtitle with corresponding subtitle type.
308+ *
309+ * @param ctx pointer to the ARIBCaptionContext
310+ * @return > 0 number of rectangles to be displayed
311+ * = 0 no subtitle
312+ * < 0 error code
313+ */
314+static int aribcaption_trans_bitmap_subtitle(ARIBCaptionContext *ctx)
315+{
316+ int ret = 0;
317+ AVSubtitle *sub = ctx->sub;
318+ int status, rect_idx;
319+ int old_width = ctx->frame_width;
320+ int old_height = ctx->frame_height;
321+
322+ if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0) {
323+ ctx->plane_width = ctx->caption.plane_width;
324+ ctx->plane_height = ctx->caption.plane_height;
325+ }
326+ estimate_video_frame_size(ctx);
327+ if (ctx->frame_width != old_width || ctx->frame_height != old_height) {
328+ ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
329+ ctx->avctx->width, ctx->avctx->height,
330+ ctx->plane_width, ctx->plane_height,
331+ ctx->bitmap_plane_width, ctx->bitmap_plane_height,
332+ ctx->frame_width, ctx->frame_height);
333+ if (!aribcc_renderer_set_frame_size(ctx->renderer,
334+ ctx->frame_width, ctx->frame_height)) {
335+ av_log(ctx, AV_LOG_ERROR,
336+ "aribcc_renderer_set_frame_size() returned with error.\n");
337+ return AVERROR_EXTERNAL;
338+ }
339+ }
340+
341+ status = aribcc_renderer_append_caption(ctx->renderer, &ctx->caption);
342+ if (!status) {
343+ av_log(ctx, AV_LOG_ERROR,
344+ "aribcc_renderer_append_caption() returned with error.\n");
345+ return AVERROR_EXTERNAL;
346+ }
347+
348+ status = aribcc_renderer_render(ctx->renderer, ctx->pts, &ctx->render_result);
349+ switch (status) {
350+ case ARIBCC_RENDER_STATUS_GOT_IMAGE:
351+ break;
352+
353+ case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
354+ aribcc_render_result_cleanup(&ctx->render_result);
355+ ff_dlog(ctx, "got image unchanged\n");
356+ return 0;
357+
358+ case ARIBCC_RENDER_STATUS_NO_IMAGE:
359+ ff_dlog(ctx, "no image\n");
360+ return 0;
361+
362+ case ARIBCC_RENDER_STATUS_ERROR:
363+ av_log(ctx, AV_LOG_ERROR,
364+ "aribcc_renderer_render() returned with error.\n");
365+ return AVERROR_EXTERNAL;
366+
367+ default:
368+ aribcc_render_result_cleanup(&ctx->render_result);
369+ av_log(ctx, AV_LOG_ERROR,
370+ "aribcc_renderer_render() returned unknown status: %d\n", status);
371+ return AVERROR_EXTERNAL;
372+ }
373+
374+ if (!ctx->render_result.image_count || ctx->render_result.images == NULL) {
375+ aribcc_render_result_cleanup(&ctx->render_result);
376+ ff_dlog(ctx, "no image (%d)\n", ctx->render_result.image_count);
377+ return 0;
378+ }
379+
380+ sub->format = 0; /* graphic */
381+ sub->rects = av_calloc(ctx->render_result.image_count, sizeof(*sub->rects));
382+ if (!sub->rects) {
383+ ret = AVERROR(ENOMEM);
384+ goto fail;
385+ }
386+ for (int i = 0; i < ctx->render_result.image_count; i++) {
387+ sub->rects[i] = av_mallocz(sizeof(*sub->rects[i]));
388+ if (!sub->rects[i]) {
389+ ret = AVERROR(ENOMEM);
390+ goto fail;
391+ }
392+ }
393+
394+ for (rect_idx = 0; rect_idx < ctx->caption.region_count; rect_idx++) {
395+ AVSubtitleRect *rect = sub->rects[rect_idx];
396+ aribcc_image_t *image = &ctx->render_result.images[rect_idx];
397+ int w, h, shrink_height, dst_idx;
398+
399+ clut_init(ctx, &ctx->caption.regions[rect_idx]);
400+
401+ rect->w = image->width * ctx->bitmap_plane_width / ctx->frame_width;
402+ rect->h = image->height * ctx->bitmap_plane_height / ctx->frame_height;
403+ rect->data[0] = av_mallocz(rect->w * rect->h);
404+ if (!rect->data[0]) {
405+ ret = AVERROR(ENOMEM);
406+ goto fail;
407+ }
408+ if ((image->height != rect->h && image->width != rect->w) ||
409+ image->stride < image->width * 4 ||
410+ image->stride * image->height > image->bitmap_size) {
411+ av_log(ctx, AV_LOG_ERROR, "Bug: unexpected rendered image: %d(%d)x%d -> %dx%d\n",
412+ image->width, image->stride / 4, image->height, rect->w, rect->h);
413+ ret = AVERROR_EXTERNAL;
414+ goto fail;
415+ }
416+
417+ shrink_height = image->height != rect->h;
418+ dst_idx = 0;
419+ for (h = 0; h < rect->h; h++) {
420+ for (w = 0; w < rect->w; w++) {
421+ /* Bi-linear interpolation */
422+ int n, m, idx0, idx1, r, g, b, a;
423+ if (shrink_height) {
424+ int div_a, y0, y1;
425+ div_a = h * ctx->frame_height;
426+ n = ctx->bitmap_plane_height;
427+ y0 = div_a / n;
428+ y1 = FFMIN(y0 + 1, image->height - 1);
429+ m = div_a - n * y0;
430+ idx0 = image->stride * y0 + w * 4;
431+ idx1 = image->stride * y1 + w * 4;
432+ } else {
433+ int div_a, x0, x1;
434+ div_a = w * ctx->frame_width;
435+ n = ctx->bitmap_plane_width;
436+ x0 = div_a / n;
437+ x1 = FFMIN(x0 + 1, image->width - 1);
438+ m = div_a - n * x0;
439+ idx0 = image->stride * h + x0 * 4;
440+ idx1 = image->stride * h + x1 * 4;
441+ }
442+ r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
443+ g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
444+ b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
445+ a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
446+ rect->data[0][dst_idx++] = clut_pick_or_set(ctx, r, g, b, a);
447+ }
448+ }
449+ rect->data[1] = av_memdup(ctx->clut, AVPALETTE_SIZE);
450+ if (!rect->data[1]) {
451+ ret = AVERROR(ENOMEM);
452+ goto fail;
453+ }
454+
455+ if (ctx->avctx->profile == FF_PROFILE_ARIB_PROFILE_C) {
456+ /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
457+ /* No position information is provided for profile C */
458+ rect->x = (ctx->frame_width - rect->w) / 2;
459+ rect->y = ctx->frame_height - rect->h * (ctx->caption.region_count - rect_idx);
460+ } else {
461+ rect->x = image->dst_x * ctx->bitmap_plane_width / ctx->frame_width;
462+ rect->y = image->dst_y * ctx->bitmap_plane_height / ctx->frame_height;
463+ }
464+ rect->type = SUBTITLE_BITMAP;
465+ rect->linesize[0] = rect->w;
466+ rect->nb_colors = 256;
467+
468+ ff_dlog(ctx, "BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n",
469+ (ctx->caption.regions[rect_idx].is_ruby) ? " (ruby)" : "",
470+ image->dst_x, image->dst_y, image->width, image->height,
471+ rect->x, rect->y, rect->w, rect->h,
472+ rect_idx, ctx->clut_idx);
473+ if (ctx->clut_overflow)
474+ av_log(ctx, AV_LOG_WARNING, "CLUT overflow (%d).\n", ctx->clut_overflow);
475+ }
476+ sub->num_rects = rect_idx;
477+
478+ return rect_idx;
479+
480+fail:
481+ if (sub->rects) {
482+ for (int i = 0; i < ctx->caption.region_count; i++) {
483+ if (sub->rects[i]) {
484+ av_freep(&sub->rects[i]->data[0]);
485+ av_freep(&sub->rects[i]->data[1]);
486+ av_freep(&sub->rects[i]);
487+ }
488+ }
489+ av_freep(&sub->rects);
490+ }
491+ sub->num_rects = 0;
492+
493+ return ret;
494+}
495+
496+static int set_ass_header(ARIBCaptionContext *ctx)
497+{
498+ AVCodecContext *avctx = ctx->avctx;
499+ int outline, shadow;
500+ const char *font_name;
501+ const char *fonts = ctx->font;
502+
503+ if (ctx->border_style == 4) {
504+ outline = 0;
505+ shadow = 4;
506+ } else {
507+ outline = 1;
508+ shadow = 0;
509+ }
510+ if (ctx->force_stroke_text)
511+ outline = (int)(ctx->stroke_width * 4.0 / 3.0);
512+
513+ if (fonts && *fonts)
514+ font_name = av_get_token(&fonts, ",");
515+ else
516+ font_name = av_strdup(DEFAULT_FONT_ASS);
517+ if (!font_name)
518+ return AVERROR(ENOMEM);
519+
520+ av_freep(&avctx->subtitle_header);
521+ avctx->subtitle_header = av_asprintf(
522+ "[Script Info]\r\n"
523+ "ScriptType: v4.00+\r\n"
524+ "PlayResX: %d\r\n"
525+ "PlayResY: %d\r\n"
526+ "WrapStyle: 2\r\n" /* 2: no word wrapping */
527+ "\r\n"
528+
529+ "[V4+ Styles]\r\n"
530+ "Format: Name, "
531+ "Fontname, Fontsize, "
532+ "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
533+ "Bold, Italic, Underline, StrikeOut, "
534+ "ScaleX, ScaleY, "
535+ "Spacing, Angle, "
536+ "BorderStyle, Outline, Shadow, "
537+ "Alignment, MarginL, MarginR, MarginV, "
538+ "Encoding\r\n"
539+
540+ "Style: "
541+ "Default," /* Name */
542+ "%s,%d," /* Font{name,size} */
543+ "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */
544+ "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */
545+ "100,100," /* Scale{X,Y} */
546+ "0,0," /* Spacing, Angle */
547+ "%d,%d,%d," /* BorderStyle, Outline, Shadow */
548+ "%d,10,10,10," /* Alignment, Margin[LRV] */
549+ "0\r\n" /* Encoding */
550+ "\r\n"
551+
552+ "[Events]\r\n"
553+ "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
554+ ctx->plane_width, ctx->plane_height,
555+ font_name, ctx->font_size,
556+ ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
557+ ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
558+ -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE,
559+ ctx->border_style, outline, shadow, ASS_DEFAULT_ALIGNMENT);
560+
561+ av_freep(&font_name);
562+ if (!avctx->subtitle_header)
563+ return AVERROR(ENOMEM);
564+ avctx->subtitle_header_size = strlen(avctx->subtitle_header);
565+ return 0;
566+}
567+
568+static void set_ass_color(AVBPrint *buf, int color_num,
569+ aribcc_color_t new_color, aribcc_color_t old_color)
570+{
571+ if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color))
572+ av_bprintf(buf, "{\\%dc&H%06x&}", color_num,
573+ ARIBCC_COLOR_RGB(new_color));
574+ if (ARIBCC_COLOR_DIFF_A(new_color, old_color))
575+ av_bprintf(buf, "{\\%da&H%02x&}", color_num,
576+ 0xFF - ARIBCC_COLOR_A(new_color));
577+}
578+
579+static int aribcaption_trans_ass_subtitle(ARIBCaptionContext *ctx)
580+{
581+ AVSubtitle *sub = ctx->sub;
582+ AVBPrint buf;
583+ bool single_rect = ctx->ass_single_rect;
584+ int ret = 0, rect_idx;
585+
586+ if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0 &&
587+ (ctx->caption.plane_width != ctx->plane_width ||
588+ ctx->caption.plane_height != ctx->plane_height)) {
589+ ctx->plane_width = ctx->caption.plane_width;
590+ ctx->plane_height = ctx->caption.plane_height;
591+ if ((ret = set_ass_header(ctx)) < 0)
592+ return ret;
593+ }
594+
595+ /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
596+ /* No position information is provided for profile C */
597+ if (ctx->avctx->profile == FF_PROFILE_ARIB_PROFILE_C)
598+ single_rect = true;
599+
600+ sub->format = 1; /* text */
601+ if (ctx->caption.region_count == 0) {
602+ /* clear previous caption for indefinite duration */
603+ ff_ass_add_rect(sub, "", ctx->readorder++, 0, NULL, NULL);
604+ return 1;
605+ }
606+
607+ av_bprint_init(&buf, ARIBC_BPRINT_SIZE_INIT, ARIBC_BPRINT_SIZE_MAX);
608+
609+ if (single_rect && ctx->avctx->profile != FF_PROFILE_ARIB_PROFILE_C) {
610+ int x, y, rx, ry;
611+ x = ctx->plane_width;
612+ y = ctx->plane_height;
613+ for (int i = 0; i < ctx->caption.region_count; i++) {
614+ rx = ctx->caption.regions[i].x;
615+ ry = ctx->caption.regions[i].y;
616+ if (rx < x)
617+ x = rx;
618+ if (ry < y)
619+ y = ry;
620+ }
621+ av_bprintf(&buf, "{\\an7}");
622+ if (y < 0)
623+ y += ctx->plane_height;
624+ if (x > 0 || y > 0)
625+ av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
626+ }
627+
628+ rect_idx = 0;
629+ for (int i = 0; i < ctx->caption.region_count; i++) {
630+ aribcc_caption_region_t *region = &ctx->caption.regions[i];
631+ aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF,
632+ ARIBC_ALPHA_DEFAULT_FRONT);
633+ aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
634+ ARIBC_ALPHA_DEFAULT_FRONT);
635+ aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
636+ ARIBC_ALPHA_DEFAULT_BACK);
637+ aribcc_charstyle_t charstyle = ctx->charstyle;
638+ int char_width = ctx->font_size;
639+ int char_height = ctx->font_size;
640+ int char_horizontal_spacing = 0;
641+
642+ if (region->is_ruby && ctx->ignore_ruby)
643+ continue;
644+
645+ if (!single_rect) {
646+ int x = region->x;
647+ int y = region->y;
648+ if (x < 0)
649+ x += ctx->plane_width;
650+ if (y < 0)
651+ y += ctx->plane_height;
652+ av_bprint_clear(&buf);
653+ av_bprintf(&buf, "{\\an7}");
654+ if (x > 0 || y > 0)
655+ av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
656+ }
657+ if (region->is_ruby)
658+ av_bprintf(&buf, "{\\fs%d}", char_height / 2);
659+
660+ for (int j = 0; j < region->char_count; j++) {
661+ aribcc_caption_char_t *ch = &region->chars[j];
662+
663+ if (ctx->avctx->profile != FF_PROFILE_ARIB_PROFILE_C) {
664+ if (ch->char_horizontal_spacing != char_horizontal_spacing) {
665+ av_bprintf(&buf, "{\\fsp%d}", (region->is_ruby) ?
666+ ch->char_horizontal_spacing / 2 :
667+ ch->char_horizontal_spacing);
668+ char_horizontal_spacing = ch->char_horizontal_spacing;
669+ }
670+ if (ch->char_width != char_width) {
671+ av_bprintf(&buf, "{\\fscx%"PRId64"}",
672+ av_rescale(ch->char_width, 100, ctx->font_size));
673+ char_width = ch->char_width;
674+ }
675+ if (ch->char_height != char_height) {
676+ av_bprintf(&buf, "{\\fscy%"PRId64"}",
677+ av_rescale(ch->char_height, 100, ctx->font_size));
678+ char_height = ch->char_height;
679+ }
680+ }
681+ if (ch->style != charstyle) {
682+ aribcc_charstyle_t diff = ch->style ^ charstyle;
683+ if (diff & ARIBCC_CHARSTYLE_STROKE) {
684+ if (charstyle & ARIBCC_CHARSTYLE_STROKE) {
685+ if (ctx->force_stroke_text)
686+ av_bprintf(&buf, "{\\bord%d}",
687+ (int)(ctx->stroke_width * 4.0 / 3.0));
688+ else
689+ av_bprintf(&buf, "{\\bord0}");
690+ } else
691+ av_bprintf(&buf, "{\\bord3}");
692+ }
693+ if (diff & ARIBCC_CHARSTYLE_BOLD) {
694+ if (charstyle & ARIBCC_CHARSTYLE_BOLD)
695+ av_bprintf(&buf, "{\\b0}");
696+ else
697+ av_bprintf(&buf, "{\\b1}");
698+ }
699+ if (diff & ARIBCC_CHARSTYLE_ITALIC) {
700+ if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
701+ av_bprintf(&buf, "{\\i0}");
702+ else
703+ av_bprintf(&buf, "{\\i1}");
704+ }
705+ if (diff & ARIBCC_CHARSTYLE_UNDERLINE) {
706+ if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
707+ av_bprintf(&buf, "{\\u0}");
708+ else
709+ av_bprintf(&buf, "{\\u1}");
710+ }
711+ charstyle = ch->style;
712+ }
713+ if (ch->text_color != text_color) {
714+ set_ass_color(&buf, 1, ch->text_color, text_color);
715+ text_color = ch->text_color;
716+ }
717+ if (ch->stroke_color != stroke_color) {
718+ set_ass_color(&buf, 3, ch->stroke_color, stroke_color);
719+ stroke_color = ch->stroke_color;
720+ }
721+ if (ch->back_color != back_color) {
722+ if (ctx->border_style == 4)
723+ set_ass_color(&buf, 4, ch->back_color, back_color);
724+ else
725+ set_ass_color(&buf, 3, ch->back_color, back_color);
726+ back_color = ch->back_color;
727+ }
728+ if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
729+ av_bprintf(&buf, "\xe3\x80\x93"); /* Geta Mark */
730+ else
731+ ff_ass_bprint_text_event(&buf, ch->u8str, strlen(ch->u8str), "", 0);
732+ }
733+
734+ if (single_rect) {
735+ if (i + 1 < ctx->caption.region_count)
736+ av_bprintf(&buf, "{\\r}\\N");
737+ ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]\n",
738+ (region->is_ruby) ? " (ruby)" : "",
739+ region->x, region->y, region->width, region->height,
740+ rect_idx);
741+ } else {
742+ if (!av_bprint_is_complete(&buf)) {
743+ ret = AVERROR(ENOMEM);
744+ goto fail;
745+ }
746+ ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n",
747+ (region->is_ruby) ? " (ruby)" : "",
748+ region->x, region->y, region->width, region->height,
749+ rect_idx, buf.str);
750+
751+ ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
752+ if (ret != 0)
753+ goto fail;
754+ rect_idx++;
755+ }
756+ }
757+ if (single_rect) {
758+ if (!av_bprint_is_complete(&buf)) {
759+ ret = AVERROR(ENOMEM);
760+ goto fail;
761+ }
762+ ff_dlog(ctx, "ASS subtitle: %s\n", buf.str);
763+
764+ ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
765+ if (ret != 0)
766+ goto fail;
767+ rect_idx++;
768+ }
769+
770+ av_bprint_finalize(&buf, NULL);
771+ return rect_idx;
772+
773+fail:
774+ if (sub->rects) {
775+ for (int i = 0; i < ctx->caption.region_count; i++) {
776+ if (sub->rects[i]) {
777+ av_freep(&sub->rects[i]->ass);
778+ av_freep(&sub->rects[i]);
779+ }
780+ }
781+ av_freep(&sub->rects);
782+ }
783+ sub->num_rects = 0;
784+ av_bprint_finalize(&buf, NULL);
785+
786+ return ret;
787+}
788+
789+static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx)
790+{
791+ AVSubtitle *sub = ctx->sub;
792+ AVSubtitleRect *rect;
793+ int ret = 0;
794+ const char *text;
795+
796+ sub->rects = av_calloc(ctx->caption.region_count, sizeof(*sub->rects));
797+ if (!sub->rects) {
798+ ret = AVERROR(ENOMEM);
799+ goto fail;
800+ }
801+ sub->num_rects = 1;
802+
803+ sub->rects[0] = av_mallocz(sizeof(*sub->rects[0]));
804+ if (!sub->rects[0]) {
805+ ret = AVERROR(ENOMEM);
806+ goto fail;
807+ }
808+ rect = sub->rects[0];
809+
810+ if (ctx->caption.region_count == 0)
811+ text = ""; /* clear previous caption */
812+ else {
813+ text = ctx->caption.text;
814+ ff_dlog(ctx, "TEXT subtitle: %s\n", text);
815+ }
816+ rect->text = av_strdup(text);
817+ if (!rect->text) {
818+ ret = AVERROR(ENOMEM);
819+ goto fail;
820+ }
821+
822+ sub->format = 1; /* text */
823+ rect->type = SUBTITLE_TEXT;
824+
825+ return 1;
826+
827+fail:
828+ if (sub->rects) {
829+ rect = sub->rects[0];
830+ if (rect) {
831+ av_freep(&rect->text);
832+ av_freep(&rect);
833+ }
834+ av_freep(&sub->rects);
835+ }
836+ sub->num_rects = 0;
837+
838+ return ret;
839+}
840+
841+static int aribcaption_decode(AVCodecContext *avctx, AVSubtitle *sub,
842+ int *got_sub_ptr, const AVPacket *avpkt)
843+{
844+ ARIBCaptionContext *ctx = avctx->priv_data;
845+ int status;
846+
847+ ff_dlog(ctx, "ARIB caption packet pts=%"PRIx64":\n", avpkt->pts);
848+ if (sub->num_rects) {
849+ avpriv_request_sample(ctx, "Different Version of Segment asked Twice");
850+ return AVERROR_PATCHWELCOME;
851+ }
852+ hex_dump_debug(ctx, avpkt->data, avpkt->size);
853+
854+ ctx->sub = sub;
855+ ctx->avpkt = avpkt;
856+ ctx->time_base = avctx->pkt_timebase;
857+ if (ctx->time_base.num <= 0 || ctx->time_base.den <= 0) {
858+ av_log(ctx, AV_LOG_VERBOSE, "No timebase set. assuming 90kHz.\n");
859+ ctx->time_base = av_make_q(1, 90000);
860+ }
861+ if (avpkt->pts == AV_NOPTS_VALUE)
862+ ctx->pts = ARIBCC_PTS_NOPTS;
863+ else
864+ ctx->pts = av_rescale_q(avpkt->pts, ctx->time_base, (AVRational){1, 1000});
865+
866+ status = aribcc_decoder_decode(ctx->decoder, avpkt->data, avpkt->size,
867+ ctx->pts, &ctx->caption);
868+ if (status == ARIBCC_DECODE_STATUS_ERROR) {
869+ av_log(ctx, AV_LOG_ERROR,
870+ "aribcc_decoder_decode() returned with error.\n");
871+ return AVERROR(EAGAIN);
872+ }
873+ if (status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
874+ ff_dlog(ctx, "No caption.\n");
875+ return avpkt->size;
876+ } else {
877+ ff_dlog(ctx, "type=%02x, flags=%x, lang=%03x\n",
878+ ctx->caption.type, ctx->caption.flags, ctx->caption.iso6392_language_code);
879+ ff_dlog(ctx, "region count = %d, start=%d.%d, duration=%d.%d\n",
880+ ctx->caption.region_count,
881+ (int)(ctx->caption.pts / 1000), (int)(ctx->caption.pts % 1000),
882+ (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
883+ -1 : ctx->caption.wait_duration / 1000),
884+ (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
885+ 0 : ctx->caption.wait_duration % 1000));
886+ }
887+
888+ switch ((enum AVSubtitleType) ctx->subtitle_type) {
889+ case SUBTITLE_TEXT:
890+ status = aribcaption_trans_text_subtitle(ctx);
891+ break;
892+
893+ case SUBTITLE_ASS:
894+ status = aribcaption_trans_ass_subtitle(ctx);
895+ break;
896+
897+ case SUBTITLE_BITMAP:
898+ status = aribcaption_trans_bitmap_subtitle(ctx);
899+ break;
900+
901+ case SUBTITLE_NONE:
902+ default:
903+ status = 0;
904+ }
905+
906+ if (status < 0) {
907+ av_log(ctx, AV_LOG_ERROR, "Failed to set Subtitle: %s\n",
908+ av_err2str(status));
909+ aribcc_caption_cleanup(&ctx->caption);
910+ return status;
911+ }
912+ if (status > 0) {
913+ *got_sub_ptr = 1;
914+ if (ctx->avpkt->pts != AV_NOPTS_VALUE)
915+ sub->pts = av_rescale_q(ctx->avpkt->pts,
916+ ctx->time_base, AV_TIME_BASE_Q);
917+ if (ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
918+ sub->end_display_time = UINT32_MAX;
919+ else
920+ sub->end_display_time = (uint32_t)ctx->caption.wait_duration;
921+ }
922+
923+ aribcc_caption_cleanup(&ctx->caption);
924+ return avpkt->size;
925+}
926+
927+static void aribcaption_flush(AVCodecContext *avctx)
928+{
929+ ARIBCaptionContext *ctx = avctx->priv_data;
930+
931+ if (ctx->decoder)
932+ aribcc_decoder_flush(ctx->decoder);
933+ if (ctx->renderer)
934+ aribcc_renderer_flush(ctx->renderer);
935+ if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
936+ ctx->readorder = 0;
937+}
938+
939+static int aribcaption_close(AVCodecContext *avctx)
940+{
941+ ARIBCaptionContext *ctx = avctx->priv_data;
942+
943+ av_freep(&ctx->clut);
944+ if (ctx->renderer)
945+ aribcc_renderer_free(ctx->renderer);
946+ if (ctx->decoder)
947+ aribcc_decoder_free(ctx->decoder);
948+ if (ctx->context)
949+ aribcc_context_free(ctx->context);
950+
951+ return 0;
952+}
953+
954+static int aribcaption_init(AVCodecContext *avctx)
955+{
956+ ARIBCaptionContext *ctx = avctx->priv_data;
957+ aribcc_profile_t profile;
958+ int ret = 0;
959+
960+ ctx->avctx = avctx;
961+
962+ switch (avctx->profile) {
963+ case FF_PROFILE_ARIB_PROFILE_A:
964+ profile = ARIBCC_PROFILE_A;
965+ /* assume 960x540 at initial state */
966+ ctx->plane_width = 960;
967+ ctx->plane_height = 540;
968+ ctx->font_size = 36;
969+ break;
970+ case FF_PROFILE_ARIB_PROFILE_C:
971+ profile = ARIBCC_PROFILE_C;
972+ ctx->plane_width = 320;
973+ ctx->plane_height = 180;
974+ ctx->font_size = 16;
975+ break;
976+ default:
977+ av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n");
978+ return AVERROR(EINVAL);
979+ }
980+ /* determine BorderStyle of ASS header */
981+ if (ctx->ignore_background)
982+ ctx->border_style = 1;
983+ else
984+ ctx->border_style = 4;
985+ ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT;
986+ if (ctx->force_stroke_text || ctx->ignore_background)
987+ ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE;
988+
989+ if (!(ctx->context = aribcc_context_alloc())) {
990+ av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption context.\n");
991+ return AVERROR_EXTERNAL;
992+ }
993+ aribcc_context_set_logcat_callback(ctx->context, logcat_callback, avctx);
994+ if (!(ctx->decoder = aribcc_decoder_alloc(ctx->context))) {
995+ av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption decoder.\n");
996+ return AVERROR_EXTERNAL;
997+ }
998+ if (!aribcc_decoder_initialize(ctx->decoder,
999+ (enum aribcc_encoding_scheme_t) ctx->encoding_scheme,
1000+ ARIBCC_CAPTIONTYPE_CAPTION,
1001+ profile,
1002+ ARIBCC_LANGUAGEID_FIRST)) {
1003+ av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption decoder.\n");
1004+ return AVERROR_EXTERNAL;
1005+ }
1006+ aribcc_decoder_set_replace_msz_fullwidth_ascii(ctx->decoder,
1007+ ctx->replace_fullwidth_ascii);
1008+
1009+ /* Similar behavior as ffmpeg tool to set canvas size */
1010+ if (ctx->canvas_width > 0 && ctx->canvas_height > 0 &&
1011+ (ctx->avctx->width == 0 || ctx->avctx->height == 0)) {
1012+ ctx->avctx->width = ctx->canvas_width;
1013+ ctx->avctx->height = ctx->canvas_height;
1014+ }
1015+
1016+ switch ((enum AVSubtitleType) ctx->subtitle_type) {
1017+ case SUBTITLE_ASS:
1018+ ret = set_ass_header(ctx);
1019+ if (ret != 0) {
1020+ av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n",
1021+ av_err2str(ret));
1022+ return ret;
1023+ }
1024+ break;
1025+
1026+ case SUBTITLE_BITMAP:
1027+ if(!(ctx->renderer = aribcc_renderer_alloc(ctx->context))) {
1028+ av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption renderer.\n");
1029+ return AVERROR_EXTERNAL;
1030+ }
1031+ if(!aribcc_renderer_initialize(ctx->renderer,
1032+ ARIBCC_CAPTIONTYPE_CAPTION,
1033+ ARIBCC_FONTPROVIDER_TYPE_AUTO,
1034+ ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
1035+ av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption renderer.\n");
1036+ return AVERROR_EXTERNAL;
1037+ }
1038+ estimate_video_frame_size(ctx);
1039+ ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
1040+ ctx->avctx->width, ctx->avctx->height,
1041+ ctx->plane_width, ctx->plane_height,
1042+ ctx->bitmap_plane_width, ctx->bitmap_plane_height,
1043+ ctx->frame_width, ctx->frame_height);
1044+ if (!aribcc_renderer_set_frame_size(ctx->renderer,
1045+ ctx->frame_width, ctx->frame_height)) {
1046+ av_log(ctx, AV_LOG_ERROR,
1047+ "aribcc_renderer_set_frame_size() returned with error.\n");
1048+ return AVERROR_EXTERNAL;
1049+ }
1050+
1051+ if (!(ctx->clut = av_mallocz(AVPALETTE_SIZE)))
1052+ return AVERROR(ENOMEM);
1053+
1054+ aribcc_renderer_set_storage_policy(ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0);
1055+ aribcc_renderer_set_replace_drcs(ctx->renderer, ctx->replace_drcs);
1056+ aribcc_renderer_set_force_stroke_text(ctx->renderer, ctx->force_stroke_text);
1057+ aribcc_renderer_set_force_no_background(ctx->renderer, ctx->ignore_background);
1058+ aribcc_renderer_set_force_no_ruby(ctx->renderer, ctx->ignore_ruby);
1059+ aribcc_renderer_set_stroke_width(ctx->renderer, ctx->stroke_width);
1060+ if (ctx->font) {
1061+ int is_nomem = 0;
1062+ size_t count = 0;
1063+ const char **font_families = NULL;
1064+ const char *fonts = ctx->font;
1065+
1066+ while (*fonts) {
1067+ const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families));
1068+ if (!ff) {
1069+ is_nomem = 1;
1070+ break;
1071+ } else {
1072+ font_families = ff;
1073+ ff[count++] = av_get_token(&fonts, ",");
1074+ if (!ff[count - 1]) {
1075+ is_nomem = 1;
1076+ break;
1077+ } else if (*fonts)
1078+ fonts++;
1079+ }
1080+ }
1081+ if (!is_nomem && count)
1082+ aribcc_renderer_set_default_font_family(ctx->renderer, font_families, count, true);
1083+ while (count)
1084+ av_freep(&font_families[--count]);
1085+ av_freep(&font_families);
1086+ if (is_nomem)
1087+ return AVERROR(ENOMEM);
1088+ }
1089+ break;
1090+
1091+ case SUBTITLE_TEXT:
1092+ case SUBTITLE_NONE:
1093+ default:
1094+ /* do nothing */ ;
1095+ }
1096+
1097+ ctx->readorder = 0;
1098+
1099+ return 0;
1100+}
1101+
1102+#if !defined(ASS_SINGLE_RECT)
1103+# define ASS_SINGLE_RECT 0
1104+#endif
1105+
1106+#define OFFSET(x) offsetof(ARIBCaptionContext, x)
1107+#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
1108+static const AVOption options[] = {
1109+ { "sub_type", "subtitle rendering type",
1110+ OFFSET(subtitle_type), AV_OPT_TYPE_INT,
1111+ { .i64 = SUBTITLE_ASS }, SUBTITLE_NONE, SUBTITLE_ASS, SD, "type" },
1112+ { "none", "do nothing", 0, AV_OPT_TYPE_CONST,
1113+ { .i64 = SUBTITLE_NONE }, .flags = SD, .unit = "type" },
1114+ { "bitmap", "bitmap rendering", 0, AV_OPT_TYPE_CONST,
1115+ { .i64 = SUBTITLE_BITMAP }, .flags = SD, .unit = "type" },
1116+ { "text", "plain text", 0, AV_OPT_TYPE_CONST,
1117+ { .i64 = SUBTITLE_TEXT }, .flags = SD, .unit = "type" },
1118+ { "ass", "formatted text", 0, AV_OPT_TYPE_CONST,
1119+ { .i64 = SUBTITLE_ASS }, .flags = SD, .unit = "type" },
1120+ { "caption_encoding", "encoding scheme of subtitle text",
1121+ OFFSET(encoding_scheme), AV_OPT_TYPE_INT, { .i64 = ARIBCC_ENCODING_SCHEME_AUTO },
1122+ ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN, SD, "encoding" },
1123+ { "auto", "automatically detect encoding scheme", 0, AV_OPT_TYPE_CONST,
1124+ { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags = SD, .unit = "encoding" },
1125+ { "jis", "8bit-char JIS encoding (Japanese ISDB captions)", 0, AV_OPT_TYPE_CONST,
1126+ { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags = SD, .unit = "encoding" },
1127+ { "utf8", "UTF-8 encoding (Philippines ISDB-T captions)", 0, AV_OPT_TYPE_CONST,
1128+ { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags = SD, .unit = "encoding" },
1129+ { "latin", "latin characters (SBTVD / ISDB-Tb captions used in South America)", 0, AV_OPT_TYPE_CONST,
1130+ { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags = SD, .unit = "encoding" },
1131+ { "ass_single_rect", "workaround of ASS subtitle for players which can't handle multi-rectangle [ass]",
1132+ OFFSET(ass_single_rect), AV_OPT_TYPE_BOOL, { .i64 = ASS_SINGLE_RECT }, 0, 1, SD },
1133+ { "font", "comma-separated font family [ass, bitmap]",
1134+ OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
1135+ { "replace_fullwidth_ascii", "replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
1136+ OFFSET(replace_fullwidth_ascii), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1137+ { "force_outline_text", "always render characters with outline [(ass), bitmap]",
1138+ OFFSET(force_stroke_text), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1139+ { "ignore_background", "ignore rendering caption background [(ass), bitmap]",
1140+ OFFSET(ignore_background), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1141+ { "ignore_ruby", "ignore ruby-like characters [ass, bitmap]",
1142+ OFFSET(ignore_ruby), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1143+ { "outline_width", "outline width of text [(ass), bitmap]",
1144+ OFFSET(stroke_width), AV_OPT_TYPE_FLOAT, { .dbl = 1.5 }, 0.0, 3.0, SD },
1145+ { "replace_drcs", "replace known DRCS [bitmap]",
1146+ OFFSET(replace_drcs), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1147+ {"canvas_size", "set input video size (WxH or abbreviation) [bitmap]",
1148+ OFFSET(canvas_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, SD },
1149+ { NULL }
1150+};
1151+
1152+static const AVClass aribcaption_class = {
1153+ .class_name = "aribcaption decoder",
1154+ .item_name = av_default_item_name,
1155+ .option = options,
1156+ .version = LIBAVUTIL_VERSION_INT,
1157+};
1158+
1159+const FFCodec ff_libaribcaption_decoder = {
1160+ .p.name = "libaribcaption",
1161+ .p.long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption decoder"),
1162+ .p.type = AVMEDIA_TYPE_SUBTITLE,
1163+ .p.id = AV_CODEC_ID_ARIB_CAPTION,
1164+ .priv_data_size = sizeof(ARIBCaptionContext),
1165+ .init = aribcaption_init,
1166+ .close = aribcaption_close,
1167+ FF_CODEC_DECODE_SUB_CB(aribcaption_decode),
1168+ .flush = aribcaption_flush,
1169+ .p.priv_class = &aribcaption_class,
1170+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
1171+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t