Imported Upstream version 6.1
[platform/upstream/ffmpeg.git] / libavcodec / libaribcaption.c
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     int ass_single_rect;
72     char *font;
73     int force_stroke_text;
74     int ignore_background;
75     int ignore_ruby;
76     float stroke_width;
77     int replace_drcs;
78     int replace_msz_ascii;
79     int replace_msz_japanese;
80     int replace_msz_glyph;
81
82     int64_t pts;
83     AVRational time_base;
84     int canvas_width;
85     int canvas_height;
86     int plane_width;
87     int plane_height;
88     int frame_width;
89     int frame_height;
90     int bitmap_plane_width;
91     int bitmap_plane_height;
92     int font_size;
93     int charstyle;
94     int border_style;
95     int readorder;
96
97     aribcc_caption_t caption;
98     aribcc_render_result_t render_result;
99     uint32_t *clut;
100     int clut_idx;
101     int clut_overflow;
102     uint8_t clut_alpha[ARIBC_ALPHA_MAX_NUM];
103 } ARIBCaptionContext;
104
105 static void hex_dump_debug(void *ctx, const char *buf, int buf_size)
106 {
107     int i;
108
109     for (i = 0; i < buf_size; i++) {
110         ff_dlog(ctx, "%02hhx ", buf[i]);
111         if (i % 16 == 15)
112             ff_dlog(ctx, "\n");
113     }
114     if (i % 16)
115         ff_dlog(ctx, "\n");
116 }
117
118 static void logcat_callback(aribcc_loglevel_t level, const char* message, void* userdata)
119 {
120     ARIBCaptionContext *ctx = userdata;
121     int lvl;
122
123     if (ctx->decoder != NULL) {
124         switch (level) {
125         case ARIBCC_LOGLEVEL_ERROR:
126             lvl = AV_LOG_ERROR;
127             break;
128         case ARIBCC_LOGLEVEL_WARNING:
129             lvl = AV_LOG_WARNING;
130             break;
131         default:
132             lvl = AV_LOG_INFO;
133         }
134
135         av_log(ctx, lvl, "%s\n", message);
136     }
137 }
138
139 static void estimate_video_frame_size(ARIBCaptionContext *ctx)
140 {
141     if (ctx->avctx->width > 0 && ctx->avctx->height > 0) {
142         /* input video size specified by -canvas_size option */
143         ctx->bitmap_plane_width = ctx->avctx->width;
144         ctx->bitmap_plane_height = ctx->avctx->height;
145     } else if (ctx->plane_width == 960) {
146         /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] 4.3.1 */
147         /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] Appendix-4 */
148         ctx->bitmap_plane_width = 1440;
149         ctx->bitmap_plane_height = 1080;
150     } else {
151         ctx->bitmap_plane_width = ctx->plane_width;
152         ctx->bitmap_plane_height = ctx->plane_height;
153     }
154     /* Expand either width or height */
155     if (ctx->bitmap_plane_height * ctx->plane_width > ctx->bitmap_plane_width * ctx->plane_height) {
156         ctx->frame_height = ctx->bitmap_plane_height;
157         ctx->frame_width = ctx->frame_height * ctx->plane_width / ctx->plane_height;
158     } else {
159         ctx->frame_width = ctx->bitmap_plane_width;
160         ctx->frame_height = ctx->frame_width * ctx->plane_height / ctx->plane_width;
161     }
162 }
163
164 static void clut_set_alpha(ARIBCaptionContext *ctx, uint8_t a)
165 {
166     int i;
167
168     for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
169         if (ctx->clut_alpha[i] == 0) {
170             ctx->clut_alpha[i] = a;
171             return;
172         }
173         if (ctx->clut_alpha[i] == a)
174             return;
175     }
176     return;
177 }
178
179 static uint8_t clut_find_nearlest_alpha(ARIBCaptionContext *ctx, uint8_t a)
180 {
181     int i, j, d;
182
183     if (a == 0)
184         return a;
185     d = 256;
186     j = 0;
187     for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
188         if (ctx->clut_alpha[i] == a)
189             return a;
190         if (ctx->clut_alpha[i] == 0)
191             break;
192         if (abs((int)a - (int)ctx->clut_alpha[i]) < d) {
193             d = abs((int)a - (int)ctx->clut_alpha[i]);
194             j = i;
195         }
196     }
197     return ctx->clut_alpha[j];
198 }
199
200 static int clut_find(ARIBCaptionContext *ctx, uint32_t rgba)
201 {
202     int i;
203
204     for (i = 0; i < ctx->clut_idx; i++) {
205         if (ctx->clut[i] == rgba)
206             return i;
207     }
208     return -1;
209 }
210
211 static inline int clut_color_distance(uint32_t rgba1, uint32_t rgba2)
212 {
213     return abs((int)CLUT_R(rgba1) - (int)CLUT_R(rgba2)) +
214            abs((int)CLUT_G(rgba1) - (int)CLUT_G(rgba2)) +
215            abs((int)CLUT_B(rgba1) - (int)CLUT_B(rgba2));
216 }
217
218 static uint8_t clut_pick_or_set(ARIBCaptionContext *ctx, int r, int g, int b, int a)
219 {
220     int c, i, d, d_min;
221     uint32_t rgba;
222
223     a = clut_find_nearlest_alpha(ctx, a);
224     if (a == 0)
225         return 0; /* transparent */
226     rgba = CLUT_RGBA(r,g,b,a);
227
228     d_min = 256 * 3;
229     c = 0;
230     for (i = 0; i < ctx->clut_idx; i++) {
231         if (ctx->clut[i] == rgba)
232             return i;
233         if (CLUT_A(ctx->clut[i]) != a)
234             continue;
235         d = clut_color_distance(ctx->clut[i], rgba);
236         if (d < d_min) {
237             d_min = d;
238             c = i;
239         }
240     }
241     if (d_min > 3) {
242         if (ctx->clut_idx >= AVPALETTE_COUNT)
243             ctx->clut_overflow++;
244         else {
245             c = ctx->clut_idx;
246             ctx->clut[ctx->clut_idx++] = rgba;
247         }
248     }
249     return c;
250 }
251
252 /* initialiaze CLUT with each character colors */
253 static void clut_init(ARIBCaptionContext *ctx, aribcc_caption_region_t *region)
254 {
255     aribcc_color_t text_color, back_color, stroke_color;
256     uint32_t rgba;
257
258     ctx->clut[0] = CLUT_RGBA(0,0,0,0); /* transparent */
259     ctx->clut_alpha[0] = 0xFF;
260     ctx->clut_idx = 1;
261     ctx->clut_overflow = 0;
262     text_color = region->chars[0].text_color;
263     back_color = region->chars[0].back_color;
264     stroke_color = region->chars[0].stroke_color;
265     rgba = ARIBCC_COLOR_TO_CLUT_RGBA(text_color, ARIBC_ALPHA_DEFAULT_FRONT);
266     ctx->clut[ctx->clut_idx++] = rgba;
267     clut_set_alpha(ctx, CLUT_A(rgba));
268     rgba = ARIBCC_COLOR_TO_CLUT_RGBA(back_color, ARIBC_ALPHA_DEFAULT_BACK);
269     ctx->clut[ctx->clut_idx++] = rgba;
270     clut_set_alpha(ctx, CLUT_A(rgba));
271     rgba = ARIBCC_COLOR_TO_CLUT_RGBA(stroke_color, ARIBC_ALPHA_DEFAULT_FRONT);
272     if (clut_find(ctx, rgba) < 0) {
273         ctx->clut[ctx->clut_idx++] = rgba;
274         clut_set_alpha(ctx, CLUT_A(rgba));
275     }
276
277     for (int i = 1; i < region->char_count; i++) {
278         if (region->chars[i].text_color != text_color) {
279             rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].text_color,
280                                              ARIBC_ALPHA_DEFAULT_FRONT);
281             if (clut_find(ctx, rgba) < 0) {
282                 ctx->clut[ctx->clut_idx++] = rgba;
283                 clut_set_alpha(ctx, CLUT_A(rgba));
284             }
285         }
286         if (region->chars[i].back_color != back_color) {
287             rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].back_color,
288                                              ARIBC_ALPHA_DEFAULT_BACK);
289             if (clut_find(ctx, rgba) < 0) {
290                 ctx->clut[ctx->clut_idx++] = rgba;
291                 clut_set_alpha(ctx, CLUT_A(rgba));
292             }
293         }
294         if (region->chars[i].stroke_color != stroke_color) {
295             rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].stroke_color,
296                                              ARIBC_ALPHA_DEFAULT_FRONT);
297             if (clut_find(ctx, rgba) < 0) {
298                 if (ctx->clut_idx < AVPALETTE_COUNT)
299                     ctx->clut[ctx->clut_idx++] = rgba;
300                 clut_set_alpha(ctx, CLUT_A(rgba));
301             }
302         }
303     }
304 }
305
306 /**
307  * aribcaption_trans_{bitmap|ass|text}_subtitle()
308  *
309  * Transfer decoded subtitle to AVSubtitle with corresponding subtitle type.
310  *
311  * @param ctx pointer to the ARIBCaptionContext
312  * @return > 0 number of rectangles to be displayed
313  *         = 0 no subtitle
314  *         < 0 error code
315  */
316 static int aribcaption_trans_bitmap_subtitle(ARIBCaptionContext *ctx)
317 {
318     int ret = 0;
319     AVSubtitle *sub = ctx->sub;
320     int status, rect_idx;
321     int old_width = ctx->frame_width;
322     int old_height = ctx->frame_height;
323
324     if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0) {
325         ctx->plane_width = ctx->caption.plane_width;
326         ctx->plane_height = ctx->caption.plane_height;
327     }
328     estimate_video_frame_size(ctx);
329     if (ctx->frame_width != old_width || ctx->frame_height != old_height) {
330         ff_dlog(ctx, "canvas: %dx%d  plane: %dx%d  bitmap: %dx%d  frame: %dx%d\n",
331                 ctx->avctx->width, ctx->avctx->height,
332                 ctx->plane_width, ctx->plane_height,
333                 ctx->bitmap_plane_width, ctx->bitmap_plane_height,
334                 ctx->frame_width, ctx->frame_height);
335         if (!aribcc_renderer_set_frame_size(ctx->renderer,
336                                  ctx->frame_width, ctx->frame_height)) {
337             av_log(ctx, AV_LOG_ERROR,
338                    "aribcc_renderer_set_frame_size() returned with error.\n");
339             return AVERROR_EXTERNAL;
340         }
341     }
342
343     status = aribcc_renderer_append_caption(ctx->renderer, &ctx->caption);
344     if (!status) {
345         av_log(ctx, AV_LOG_ERROR,
346                "aribcc_renderer_append_caption() returned with error.\n");
347         return AVERROR_EXTERNAL;
348     }
349
350     status = aribcc_renderer_render(ctx->renderer, ctx->pts, &ctx->render_result);
351     switch (status) {
352     case ARIBCC_RENDER_STATUS_GOT_IMAGE:
353         break;
354
355     case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
356         aribcc_render_result_cleanup(&ctx->render_result);
357         ff_dlog(ctx, "got image unchanged\n");
358         return 0;
359
360     case ARIBCC_RENDER_STATUS_NO_IMAGE:
361         ff_dlog(ctx, "no image\n");
362         return 0;
363
364     case ARIBCC_RENDER_STATUS_ERROR:
365         av_log(ctx, AV_LOG_ERROR,
366                "aribcc_renderer_render() returned with error.\n");
367         return AVERROR_EXTERNAL;
368
369     default:
370         aribcc_render_result_cleanup(&ctx->render_result);
371         av_log(ctx, AV_LOG_ERROR,
372                "aribcc_renderer_render() returned unknown status: %d\n", status);
373         return AVERROR_EXTERNAL;
374     }
375
376     if (!ctx->render_result.image_count || ctx->render_result.images == NULL) {
377         aribcc_render_result_cleanup(&ctx->render_result);
378         ff_dlog(ctx, "no image (%d)\n", ctx->render_result.image_count);
379         return 0;
380     }
381
382     sub->format = 0; /* graphic */
383     sub->rects = av_calloc(ctx->render_result.image_count, sizeof(*sub->rects));
384     if (!sub->rects) {
385         ret = AVERROR(ENOMEM);
386         goto fail;
387     }
388     for (int i = 0; i < ctx->render_result.image_count; i++) {
389         sub->rects[i] = av_mallocz(sizeof(*sub->rects[i]));
390         if (!sub->rects[i]) {
391             ret = AVERROR(ENOMEM);
392             goto fail;
393         }
394     }
395
396     for (rect_idx = 0; rect_idx < ctx->caption.region_count; rect_idx++) {
397         AVSubtitleRect *rect = sub->rects[rect_idx];
398         aribcc_image_t *image = &ctx->render_result.images[rect_idx];
399         int w, h, shrink_height, dst_idx;
400
401         clut_init(ctx, &ctx->caption.regions[rect_idx]);
402
403         rect->w = image->width * ctx->bitmap_plane_width / ctx->frame_width;
404         rect->h = image->height * ctx->bitmap_plane_height / ctx->frame_height;
405         rect->data[0] = av_mallocz(rect->w * rect->h);
406         if (!rect->data[0]) {
407             ret = AVERROR(ENOMEM);
408             goto fail;
409         }
410         if ((image->height != rect->h && image->width != rect->w) ||
411             image->stride < image->width * 4 ||
412             image->stride * image->height > image->bitmap_size) {
413             av_log(ctx, AV_LOG_ERROR, "Bug: unexpected rendered image: %d(%d)x%d -> %dx%d\n",
414                    image->width, image->stride / 4, image->height, rect->w, rect->h);
415             ret = AVERROR_EXTERNAL;
416             goto fail;
417         }
418
419         shrink_height = image->height != rect->h;
420         dst_idx = 0;
421         for (h = 0; h < rect->h; h++) {
422             for (w = 0; w < rect->w; w++) {
423                 /* Bi-linear interpolation */
424                 int n, m, idx0, idx1, r, g, b, a;
425                 if (shrink_height) {
426                     int div_a, y0, y1;
427                     div_a = h * ctx->frame_height;
428                     n = ctx->bitmap_plane_height;
429                     y0 = div_a / n;
430                     y1 = FFMIN(y0 + 1, image->height - 1);
431                     m = div_a - n * y0;
432                     idx0 = image->stride * y0 + w * 4;
433                     idx1 = image->stride * y1 + w * 4;
434                 } else {
435                     int div_a, x0, x1;
436                     div_a = w * ctx->frame_width;
437                     n = ctx->bitmap_plane_width;
438                     x0 = div_a / n;
439                     x1 = FFMIN(x0 + 1, image->width - 1);
440                     m = div_a - n * x0;
441                     idx0 = image->stride * h + x0 * 4;
442                     idx1 = image->stride * h + x1 * 4;
443                 }
444                 r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
445                 g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
446                 b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
447                 a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
448                 rect->data[0][dst_idx++] = clut_pick_or_set(ctx, r, g, b, a);
449             }
450         }
451         rect->data[1] = av_memdup(ctx->clut, AVPALETTE_SIZE);
452         if (!rect->data[1]) {
453             ret = AVERROR(ENOMEM);
454             goto fail;
455         }
456
457         if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C) {
458             /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
459             /* No position information is provided for profile C */
460             rect->x = (ctx->frame_width - rect->w) / 2;
461             rect->y = ctx->frame_height - rect->h * (ctx->caption.region_count - rect_idx);
462         } else {
463             rect->x = image->dst_x * ctx->bitmap_plane_width / ctx->frame_width;
464             rect->y = image->dst_y * ctx->bitmap_plane_height / ctx->frame_height;
465         }
466         rect->type = SUBTITLE_BITMAP;
467         rect->linesize[0] = rect->w;
468         rect->nb_colors = 256;
469
470         ff_dlog(ctx, "BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n",
471                 (ctx->caption.regions[rect_idx].is_ruby) ? " (ruby)" : "",
472                 image->dst_x, image->dst_y, image->width, image->height,
473                 rect->x, rect->y, rect->w, rect->h,
474                 rect_idx, ctx->clut_idx);
475         if (ctx->clut_overflow)
476             av_log(ctx, AV_LOG_WARNING, "CLUT overflow (%d).\n", ctx->clut_overflow);
477     }
478     sub->num_rects = rect_idx;
479
480     return rect_idx;
481
482 fail:
483     if (sub->rects) {
484         for (int i = 0; i < ctx->caption.region_count; i++) {
485             if (sub->rects[i]) {
486                 av_freep(&sub->rects[i]->data[0]);
487                 av_freep(&sub->rects[i]->data[1]);
488                 av_freep(&sub->rects[i]);
489             }
490         }
491         av_freep(&sub->rects);
492     }
493     sub->num_rects = 0;
494
495     return ret;
496 }
497
498 static int set_ass_header(ARIBCaptionContext *ctx)
499 {
500     AVCodecContext *avctx = ctx->avctx;
501     int outline, shadow;
502     const char *font_name;
503     const char *fonts = ctx->font;
504
505     if (ctx->border_style == 4) {
506         outline = 0;
507         shadow = 4;
508     } else {
509         outline = 1;
510         shadow = 0;
511     }
512     if (ctx->force_stroke_text)
513         outline = (int)(ctx->stroke_width * 4.0 / 3.0);
514
515     if (fonts && *fonts)
516         font_name = av_get_token(&fonts, ",");
517     else
518         font_name = av_strdup(DEFAULT_FONT_ASS);
519     if (!font_name)
520         return AVERROR(ENOMEM);
521
522     av_freep(&avctx->subtitle_header);
523     avctx->subtitle_header = av_asprintf(
524             "[Script Info]\r\n"
525             "ScriptType: v4.00+\r\n"
526             "PlayResX: %d\r\n"
527             "PlayResY: %d\r\n"
528             "WrapStyle: 2\r\n"      /* 2: no word wrapping */
529             "\r\n"
530
531             "[V4+ Styles]\r\n"
532              "Format: Name, "
533              "Fontname, Fontsize, "
534              "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
535              "Bold, Italic, Underline, StrikeOut, "
536              "ScaleX, ScaleY, "
537              "Spacing, Angle, "
538              "BorderStyle, Outline, Shadow, "
539              "Alignment, MarginL, MarginR, MarginV, "
540              "Encoding\r\n"
541
542              "Style: "
543              "Default,"             /* Name */
544              "%s,%d,"               /* Font{name,size} */
545              "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */
546              "%d,%d,%d,0,"          /* Bold, Italic, Underline, StrikeOut */
547              "100,100,"             /* Scale{X,Y} */
548              "0,0,"                 /* Spacing, Angle */
549              "%d,%d,%d,"            /* BorderStyle, Outline, Shadow */
550              "%d,10,10,10,"         /* Alignment, Margin[LRV] */
551              "0\r\n"                /* Encoding */
552              "\r\n"
553
554              "[Events]\r\n"
555              "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
556             ctx->plane_width, ctx->plane_height,
557             font_name, ctx->font_size,
558             ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
559             ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
560             -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE,
561             ctx->border_style, outline, shadow, ASS_DEFAULT_ALIGNMENT);
562
563     av_freep(&font_name);
564     if (!avctx->subtitle_header)
565         return AVERROR(ENOMEM);
566     avctx->subtitle_header_size = strlen(avctx->subtitle_header);
567     return 0;
568 }
569
570 static void set_ass_color(AVBPrint *buf, int color_num,
571                           aribcc_color_t new_color, aribcc_color_t old_color)
572 {
573     if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color))
574         av_bprintf(buf, "{\\%dc&H%06x&}", color_num,
575                                           ARIBCC_COLOR_RGB(new_color));
576     if (ARIBCC_COLOR_DIFF_A(new_color, old_color))
577         av_bprintf(buf, "{\\%da&H%02x&}", color_num,
578                                           0xFF - ARIBCC_COLOR_A(new_color));
579 }
580
581 static int aribcaption_trans_ass_subtitle(ARIBCaptionContext *ctx)
582 {
583     AVSubtitle *sub = ctx->sub;
584     AVBPrint buf;
585     bool single_rect = ctx->ass_single_rect;
586     int ret = 0, rect_idx;
587
588     if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0 &&
589         (ctx->caption.plane_width != ctx->plane_width ||
590          ctx->caption.plane_height != ctx->plane_height)) {
591         ctx->plane_width = ctx->caption.plane_width;
592         ctx->plane_height = ctx->caption.plane_height;
593         if ((ret = set_ass_header(ctx)) < 0)
594             return ret;
595     }
596
597     /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
598     /* No position information is provided for profile C */
599     if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C)
600         single_rect = true;
601
602     sub->format = 1; /* text */
603     if (ctx->caption.region_count == 0) {
604         /* clear previous caption for indefinite duration  */
605         ff_ass_add_rect(sub, "", ctx->readorder++, 0, NULL, NULL);
606         return 1;
607     }
608
609     av_bprint_init(&buf, ARIBC_BPRINT_SIZE_INIT, ARIBC_BPRINT_SIZE_MAX);
610
611     if (single_rect && ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) {
612         int x, y, rx, ry;
613         x = ctx->plane_width;
614         y = ctx->plane_height;
615         for (int i = 0; i < ctx->caption.region_count; i++) {
616             rx = ctx->caption.regions[i].x;
617             ry = ctx->caption.regions[i].y;
618             if (rx < x)
619                 x = rx;
620             if (ry < y)
621                 y = ry;
622         }
623         av_bprintf(&buf, "{\\an7}");
624         if (y < 0)
625             y += ctx->plane_height;
626         if (x > 0 || y > 0)
627             av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
628     }
629
630     rect_idx = 0;
631     for (int i = 0; i < ctx->caption.region_count; i++) {
632         aribcc_caption_region_t *region = &ctx->caption.regions[i];
633         aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF,
634                                                      ARIBC_ALPHA_DEFAULT_FRONT);
635         aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
636                                                        ARIBC_ALPHA_DEFAULT_FRONT);
637         aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
638                                                      ARIBC_ALPHA_DEFAULT_BACK);
639         aribcc_charstyle_t charstyle = ctx->charstyle;
640         int char_width = ctx->font_size;
641         int char_height = ctx->font_size;
642         int char_horizontal_spacing = 0;
643
644         if (region->is_ruby && ctx->ignore_ruby)
645             continue;
646
647         if (!single_rect) {
648             int x = region->x;
649             int y = region->y;
650             if (x < 0)
651                 x += ctx->plane_width;
652             if (y < 0)
653                 y += ctx->plane_height;
654             av_bprint_clear(&buf);
655             av_bprintf(&buf, "{\\an7}");
656             if (x > 0 || y > 0)
657                 av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
658         }
659         if (region->is_ruby)
660             av_bprintf(&buf, "{\\fs%d}", char_height / 2);
661
662         for (int j = 0; j < region->char_count; j++) {
663             aribcc_caption_char_t *ch = &region->chars[j];
664
665             if (ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) {
666                 if (ch->char_horizontal_spacing != char_horizontal_spacing) {
667                     av_bprintf(&buf, "{\\fsp%d}", (region->is_ruby) ?
668                                      ch->char_horizontal_spacing / 2 :
669                                      ch->char_horizontal_spacing);
670                     char_horizontal_spacing = ch->char_horizontal_spacing;
671                 }
672                 if (ch->char_width != char_width) {
673                     av_bprintf(&buf, "{\\fscx%"PRId64"}",
674                                av_rescale(ch->char_width, 100, ctx->font_size));
675                     char_width = ch->char_width;
676                 }
677                 if (ch->char_height != char_height) {
678                     av_bprintf(&buf, "{\\fscy%"PRId64"}",
679                                av_rescale(ch->char_height, 100, ctx->font_size));
680                     char_height = ch->char_height;
681                 }
682             }
683             if (ch->style != charstyle) {
684                 aribcc_charstyle_t diff = ch->style ^ charstyle;
685                 if (diff & ARIBCC_CHARSTYLE_STROKE) {
686                     if (charstyle & ARIBCC_CHARSTYLE_STROKE) {
687                         if (ctx->force_stroke_text)
688                             av_bprintf(&buf, "{\\bord%d}",
689                                        (int)(ctx->stroke_width * 4.0 / 3.0));
690                         else
691                             av_bprintf(&buf, "{\\bord0}");
692                     } else
693                         av_bprintf(&buf, "{\\bord3}");
694                 }
695                 if (diff & ARIBCC_CHARSTYLE_BOLD) {
696                     if (charstyle & ARIBCC_CHARSTYLE_BOLD)
697                         av_bprintf(&buf, "{\\b0}");
698                     else
699                         av_bprintf(&buf, "{\\b1}");
700                 }
701                 if (diff & ARIBCC_CHARSTYLE_ITALIC) {
702                     if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
703                         av_bprintf(&buf, "{\\i0}");
704                     else
705                         av_bprintf(&buf, "{\\i1}");
706                 }
707                 if (diff & ARIBCC_CHARSTYLE_UNDERLINE) {
708                     if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
709                         av_bprintf(&buf, "{\\u0}");
710                     else
711                         av_bprintf(&buf, "{\\u1}");
712                 }
713                 charstyle = ch->style;
714             }
715             if (ch->text_color != text_color) {
716                 set_ass_color(&buf, 1, ch->text_color, text_color);
717                 text_color = ch->text_color;
718             }
719             if (ch->stroke_color != stroke_color) {
720                 set_ass_color(&buf, 3, ch->stroke_color, stroke_color);
721                 stroke_color = ch->stroke_color;
722             }
723             if (ch->back_color != back_color) {
724                 if (ctx->border_style == 4)
725                     set_ass_color(&buf, 4, ch->back_color, back_color);
726                 else
727                     set_ass_color(&buf, 3, ch->back_color, back_color);
728                 back_color = ch->back_color;
729             }
730             if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
731                 av_bprintf(&buf, "\xe3\x80\x93");  /* Geta Mark */
732             else
733                 ff_ass_bprint_text_event(&buf, ch->u8str, strlen(ch->u8str), "", 0);
734         }
735
736         if (single_rect) {
737             if (i + 1 < ctx->caption.region_count)
738                 av_bprintf(&buf, "{\\r}\\N");
739             ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]\n",
740                     (region->is_ruby) ? " (ruby)" : "",
741                     region->x, region->y, region->width, region->height,
742                     rect_idx);
743         } else {
744             if (!av_bprint_is_complete(&buf)) {
745                 ret = AVERROR(ENOMEM);
746                 goto fail;
747             }
748             ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n",
749                     (region->is_ruby) ? " (ruby)" : "",
750                     region->x, region->y, region->width, region->height,
751                     rect_idx, buf.str);
752
753             ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
754             if (ret != 0)
755                 goto fail;
756             rect_idx++;
757         }
758     }
759     if (single_rect) {
760         if (!av_bprint_is_complete(&buf)) {
761             ret = AVERROR(ENOMEM);
762             goto fail;
763         }
764         ff_dlog(ctx, "ASS subtitle: %s\n", buf.str);
765
766         ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
767         if (ret != 0)
768             goto fail;
769         rect_idx++;
770     }
771
772     av_bprint_finalize(&buf, NULL);
773     return rect_idx;
774
775 fail:
776     if (sub->rects) {
777         for (int i = 0; i < ctx->caption.region_count; i++) {
778             if (sub->rects[i]) {
779                 av_freep(&sub->rects[i]->ass);
780                 av_freep(&sub->rects[i]);
781             }
782         }
783         av_freep(&sub->rects);
784     }
785     sub->num_rects = 0;
786     av_bprint_finalize(&buf, NULL);
787
788     return ret;
789 }
790
791 static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx)
792 {
793     AVSubtitle *sub = ctx->sub;
794     AVSubtitleRect *rect;
795     int ret = 0;
796     const char *text;
797
798     sub->rects = av_calloc(ctx->caption.region_count, sizeof(*sub->rects));
799     if (!sub->rects) {
800         ret = AVERROR(ENOMEM);
801         goto fail;
802     }
803     sub->num_rects = 1;
804
805     sub->rects[0] = av_mallocz(sizeof(*sub->rects[0]));
806     if (!sub->rects[0]) {
807         ret = AVERROR(ENOMEM);
808         goto fail;
809     }
810     rect = sub->rects[0];
811
812     if (ctx->caption.region_count == 0)
813         text = ""; /* clear previous caption */
814     else {
815         text = ctx->caption.text;
816         ff_dlog(ctx, "TEXT subtitle: %s\n", text);
817     }
818     rect->text = av_strdup(text);
819     if (!rect->text) {
820         ret = AVERROR(ENOMEM);
821         goto fail;
822     }
823
824     sub->format = 1; /* text */
825     rect->type = SUBTITLE_TEXT;
826
827     return 1;
828
829 fail:
830     if (sub->rects) {
831         rect = sub->rects[0];
832         if (rect) {
833             av_freep(&rect->text);
834             av_freep(&rect);
835         }
836         av_freep(&sub->rects);
837     }
838     sub->num_rects = 0;
839
840     return ret;
841 }
842
843 static int aribcaption_decode(AVCodecContext *avctx, AVSubtitle *sub,
844                               int *got_sub_ptr, const AVPacket *avpkt)
845 {
846     ARIBCaptionContext *ctx = avctx->priv_data;
847     int status;
848
849     ff_dlog(ctx, "ARIB caption packet pts=%"PRIx64":\n", avpkt->pts);
850     if (sub->num_rects) {
851         avpriv_request_sample(ctx, "Different Version of Segment asked Twice");
852         return AVERROR_PATCHWELCOME;
853     }
854     hex_dump_debug(ctx, avpkt->data, avpkt->size);
855
856     ctx->sub = sub;
857     ctx->avpkt = avpkt;
858     ctx->time_base = avctx->pkt_timebase;
859     if (ctx->time_base.num <= 0 || ctx->time_base.den <= 0) {
860         av_log(ctx, AV_LOG_VERBOSE, "No timebase set. assuming 90kHz.\n");
861         ctx->time_base = av_make_q(1, 90000);
862     }
863     if (avpkt->pts == AV_NOPTS_VALUE)
864         ctx->pts = ARIBCC_PTS_NOPTS;
865     else
866         ctx->pts = av_rescale_q(avpkt->pts, ctx->time_base, (AVRational){1, 1000});
867
868     status = aribcc_decoder_decode(ctx->decoder, avpkt->data, avpkt->size,
869                                    ctx->pts, &ctx->caption);
870     if (status == ARIBCC_DECODE_STATUS_ERROR) {
871         av_log(ctx, AV_LOG_ERROR,
872                "aribcc_decoder_decode() returned with error.\n");
873         return AVERROR(EAGAIN);
874     }
875     if (status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
876         ff_dlog(ctx, "No caption.\n");
877         return avpkt->size;
878     } else {
879         ff_dlog(ctx, "type=%02x, flags=%x, lang=%03x\n",
880                 ctx->caption.type, ctx->caption.flags, ctx->caption.iso6392_language_code);
881         ff_dlog(ctx, "region count = %d, start=%d.%d, duration=%d.%d\n",
882                 ctx->caption.region_count,
883                 (int)(ctx->caption.pts / 1000), (int)(ctx->caption.pts % 1000),
884                 (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
885                       -1 : ctx->caption.wait_duration / 1000),
886                 (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
887                       0 : ctx->caption.wait_duration % 1000));
888     }
889
890     switch ((enum AVSubtitleType) ctx->subtitle_type) {
891     case SUBTITLE_TEXT:
892         status = aribcaption_trans_text_subtitle(ctx);
893         break;
894
895     case SUBTITLE_ASS:
896         status = aribcaption_trans_ass_subtitle(ctx);
897         break;
898
899     case SUBTITLE_BITMAP:
900         status = aribcaption_trans_bitmap_subtitle(ctx);
901         break;
902
903     case SUBTITLE_NONE:
904     default:
905         status = 0;
906     }
907
908     if (status < 0) {
909         av_log(ctx, AV_LOG_ERROR, "Failed to set Subtitle: %s\n",
910                av_err2str(status));
911         aribcc_caption_cleanup(&ctx->caption);
912         return status;
913     }
914     if (status > 0) {
915         *got_sub_ptr = 1;
916         if (ctx->avpkt->pts != AV_NOPTS_VALUE)
917             sub->pts = av_rescale_q(ctx->avpkt->pts,
918                                     ctx->time_base, AV_TIME_BASE_Q);
919         if (ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
920             sub->end_display_time = UINT32_MAX;
921         else
922             sub->end_display_time = (uint32_t)ctx->caption.wait_duration;
923     }
924
925     aribcc_caption_cleanup(&ctx->caption);
926     return avpkt->size;
927 }
928
929 static void aribcaption_flush(AVCodecContext *avctx)
930 {
931     ARIBCaptionContext *ctx = avctx->priv_data;
932
933     if (ctx->decoder)
934         aribcc_decoder_flush(ctx->decoder);
935     if (ctx->renderer)
936         aribcc_renderer_flush(ctx->renderer);
937     if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
938         ctx->readorder = 0;
939 }
940
941 static int aribcaption_close(AVCodecContext *avctx)
942 {
943     ARIBCaptionContext *ctx = avctx->priv_data;
944
945     av_freep(&ctx->clut);
946     if (ctx->renderer)
947         aribcc_renderer_free(ctx->renderer);
948     if (ctx->decoder)
949         aribcc_decoder_free(ctx->decoder);
950     if (ctx->context)
951         aribcc_context_free(ctx->context);
952
953     return 0;
954 }
955
956 static int aribcaption_init(AVCodecContext *avctx)
957 {
958     ARIBCaptionContext *ctx = avctx->priv_data;
959     aribcc_profile_t profile;
960     int ret = 0;
961
962     ctx->avctx = avctx;
963
964     switch (avctx->profile) {
965     case AV_PROFILE_ARIB_PROFILE_A:
966         profile = ARIBCC_PROFILE_A;
967         /* assume 960x540 at initial state */
968         ctx->plane_width = 960;
969         ctx->plane_height = 540;
970         ctx->font_size = 36;
971         break;
972     case AV_PROFILE_ARIB_PROFILE_C:
973         profile = ARIBCC_PROFILE_C;
974         ctx->plane_width = 320;
975         ctx->plane_height = 180;
976         ctx->font_size = 16;
977         break;
978     default:
979         av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n");
980         return AVERROR(EINVAL);
981     }
982     /* determine BorderStyle of ASS header */
983     if (ctx->ignore_background)
984         ctx->border_style = 1;
985     else
986         ctx->border_style = 4;
987     ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT;
988     if (ctx->force_stroke_text || ctx->ignore_background)
989         ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE;
990
991     if (!(ctx->context = aribcc_context_alloc())) {
992         av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption context.\n");
993         return AVERROR_EXTERNAL;
994     }
995     aribcc_context_set_logcat_callback(ctx->context, logcat_callback, avctx);
996     if (!(ctx->decoder = aribcc_decoder_alloc(ctx->context))) {
997         av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption decoder.\n");
998         return AVERROR_EXTERNAL;
999     }
1000     if (!aribcc_decoder_initialize(ctx->decoder,
1001                                    (enum aribcc_encoding_scheme_t) ctx->encoding_scheme,
1002                                    ARIBCC_CAPTIONTYPE_CAPTION,
1003                                    profile,
1004                                    ARIBCC_LANGUAGEID_FIRST)) {
1005         av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption decoder.\n");
1006         return AVERROR_EXTERNAL;
1007     }
1008     aribcc_decoder_set_replace_msz_fullwidth_ascii(ctx->decoder,
1009                                                    ctx->replace_msz_ascii);
1010     aribcc_decoder_set_replace_msz_fullwidth_japanese(ctx->decoder,
1011                                                       ctx->replace_msz_japanese);
1012
1013     /* Similar behavior as ffmpeg tool to set canvas size */
1014     if (ctx->canvas_width > 0 && ctx->canvas_height > 0 &&
1015         (ctx->avctx->width == 0 || ctx->avctx->height == 0)) {
1016         ctx->avctx->width = ctx->canvas_width;
1017         ctx->avctx->height = ctx->canvas_height;
1018     }
1019
1020     switch ((enum AVSubtitleType) ctx->subtitle_type) {
1021     case SUBTITLE_ASS:
1022         ret = set_ass_header(ctx);
1023         if (ret != 0) {
1024             av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n",
1025                                         av_err2str(ret));
1026             return ret;
1027         }
1028         break;
1029
1030     case SUBTITLE_BITMAP:
1031         if(!(ctx->renderer = aribcc_renderer_alloc(ctx->context))) {
1032             av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption renderer.\n");
1033             return AVERROR_EXTERNAL;
1034         }
1035         if(!aribcc_renderer_initialize(ctx->renderer,
1036                                        ARIBCC_CAPTIONTYPE_CAPTION,
1037                                        ARIBCC_FONTPROVIDER_TYPE_AUTO,
1038                                        ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
1039             av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption renderer.\n");
1040             return AVERROR_EXTERNAL;
1041         }
1042         estimate_video_frame_size(ctx);
1043         ff_dlog(ctx, "canvas: %dx%d  plane: %dx%d  bitmap: %dx%d  frame: %dx%d\n",
1044                 ctx->avctx->width, ctx->avctx->height,
1045                 ctx->plane_width, ctx->plane_height,
1046                 ctx->bitmap_plane_width, ctx->bitmap_plane_height,
1047                 ctx->frame_width, ctx->frame_height);
1048         if (!aribcc_renderer_set_frame_size(ctx->renderer,
1049                                             ctx->frame_width, ctx->frame_height)) {
1050             av_log(ctx, AV_LOG_ERROR,
1051                    "aribcc_renderer_set_frame_size() returned with error.\n");
1052             return AVERROR_EXTERNAL;
1053         }
1054
1055         if (!(ctx->clut = av_mallocz(AVPALETTE_SIZE)))
1056             return AVERROR(ENOMEM);
1057
1058         aribcc_renderer_set_storage_policy(ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0);
1059         aribcc_renderer_set_replace_drcs(ctx->renderer, ctx->replace_drcs);
1060         aribcc_renderer_set_force_stroke_text(ctx->renderer, ctx->force_stroke_text);
1061         aribcc_renderer_set_force_no_background(ctx->renderer, ctx->ignore_background);
1062         aribcc_renderer_set_force_no_ruby(ctx->renderer, ctx->ignore_ruby);
1063         aribcc_renderer_set_stroke_width(ctx->renderer, ctx->stroke_width);
1064         aribcc_renderer_set_replace_msz_halfwidth_glyph(ctx->renderer,
1065                                                         ctx->replace_msz_glyph);
1066         if (ctx->font) {
1067             int is_nomem = 0;
1068             size_t count = 0;
1069             const char **font_families = NULL;
1070             const char *fonts = ctx->font;
1071
1072             while (*fonts) {
1073                 const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families));
1074                 if (!ff) {
1075                     is_nomem = 1;
1076                     break;
1077                 } else {
1078                     font_families = ff;
1079                     ff[count++] = av_get_token(&fonts, ",");
1080                     if (!ff[count - 1]) {
1081                         is_nomem = 1;
1082                         break;
1083                     } else if (*fonts)
1084                         fonts++;
1085                 }
1086             }
1087             if (!is_nomem && count)
1088                 aribcc_renderer_set_default_font_family(ctx->renderer, font_families, count, true);
1089             while (count)
1090                 av_freep(&font_families[--count]);
1091             av_freep(&font_families);
1092             if (is_nomem)
1093                 return AVERROR(ENOMEM);
1094         }
1095         break;
1096
1097     case SUBTITLE_TEXT:
1098     case SUBTITLE_NONE:
1099     default:
1100         /* do nothing */ ;
1101     }
1102
1103     ctx->readorder = 0;
1104
1105     return 0;
1106 }
1107
1108 #if !defined(ASS_SINGLE_RECT)
1109 # define ASS_SINGLE_RECT 0
1110 #endif
1111
1112 #define OFFSET(x) offsetof(ARIBCaptionContext, x)
1113 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
1114 static const AVOption options[] = {
1115     { "sub_type", "subtitle rendering type",
1116       OFFSET(subtitle_type), AV_OPT_TYPE_INT,
1117       { .i64 = SUBTITLE_ASS }, SUBTITLE_NONE, SUBTITLE_ASS, SD, "type" },
1118     { "none",   "do nothing", 0, AV_OPT_TYPE_CONST,
1119       { .i64 = SUBTITLE_NONE }, .flags = SD, .unit = "type" },
1120     { "bitmap", "bitmap rendering", 0, AV_OPT_TYPE_CONST,
1121       { .i64 = SUBTITLE_BITMAP }, .flags = SD, .unit = "type" },
1122     { "text",   "plain text", 0, AV_OPT_TYPE_CONST,
1123       { .i64 = SUBTITLE_TEXT }, .flags = SD, .unit = "type" },
1124     { "ass",    "formatted text", 0, AV_OPT_TYPE_CONST,
1125       { .i64 = SUBTITLE_ASS }, .flags = SD, .unit = "type" },
1126     { "caption_encoding", "encoding scheme of subtitle text",
1127       OFFSET(encoding_scheme), AV_OPT_TYPE_INT, { .i64 = ARIBCC_ENCODING_SCHEME_AUTO },
1128       ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN, SD, "encoding" },
1129     { "auto",   "automatically detect encoding scheme", 0, AV_OPT_TYPE_CONST,
1130       { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags = SD, .unit = "encoding" },
1131     { "jis",    "8bit-char JIS encoding (Japanese ISDB captions)", 0, AV_OPT_TYPE_CONST,
1132       { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags = SD, .unit = "encoding" },
1133     { "utf8",   "UTF-8 encoding (Philippines ISDB-T captions)", 0, AV_OPT_TYPE_CONST,
1134       { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags = SD, .unit = "encoding" },
1135     { "latin",  "latin characters (SBTVD / ISDB-Tb captions used in South America)", 0, AV_OPT_TYPE_CONST,
1136       { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags = SD, .unit = "encoding" },
1137     { "ass_single_rect", "workaround of ASS subtitle for players which can't handle multi-rectangle [ass]",
1138       OFFSET(ass_single_rect), AV_OPT_TYPE_BOOL, { .i64 = ASS_SINGLE_RECT }, 0, 1, SD },
1139     { "font", "comma-separated font family [ass, bitmap]",
1140       OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
1141     { "force_outline_text", "always render characters with outline [(ass), bitmap]",
1142       OFFSET(force_stroke_text), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1143     { "ignore_background", "ignore rendering caption background [(ass), bitmap]",
1144       OFFSET(ignore_background), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1145     { "ignore_ruby", "ignore ruby-like characters [ass, bitmap]",
1146       OFFSET(ignore_ruby), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
1147     { "outline_width", "outline width of text [(ass), bitmap]",
1148       OFFSET(stroke_width), AV_OPT_TYPE_FLOAT, { .dbl = 1.5 }, 0.0, 3.0, SD },
1149     { "replace_drcs", "replace known DRCS [bitmap]",
1150       OFFSET(replace_drcs), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1151     { "replace_msz_ascii", "replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
1152       OFFSET(replace_msz_ascii), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1153     { "replace_msz_japanese", "replace MSZ fullwidth Japanese with halfwidth [ass, bitmap]",
1154       OFFSET(replace_msz_japanese), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1155     { "replace_msz_glyph", "replace MSZ characters with halfwidth glyphs [bitmap]",
1156       OFFSET(replace_msz_glyph), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
1157     {"canvas_size", "set input video size (WxH or abbreviation) [bitmap]",
1158       OFFSET(canvas_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, SD },
1159     { NULL }
1160 };
1161
1162 static const AVClass aribcaption_class = {
1163     .class_name = "aribcaption decoder",
1164     .item_name  = av_default_item_name,
1165     .option     = options,
1166     .version    = LIBAVUTIL_VERSION_INT,
1167 };
1168
1169 const FFCodec ff_libaribcaption_decoder = {
1170     .p.name         = "libaribcaption",
1171     .p.long_name    = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption decoder"),
1172     .p.type         = AVMEDIA_TYPE_SUBTITLE,
1173     .p.id           = AV_CODEC_ID_ARIB_CAPTION,
1174     .priv_data_size = sizeof(ARIBCaptionContext),
1175     .init           = aribcaption_init,
1176     .close          = aribcaption_close,
1177     FF_CODEC_DECODE_SUB_CB(aribcaption_decode),
1178     .flush          = aribcaption_flush,
1179     .p.priv_class   = &aribcaption_class,
1180     .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
1181 };