32 #include <aribcaption/aribcaption.h>
34 #if !defined(DEFAULT_FONT_ASS)
35 # define DEFAULT_FONT_ASS "sans-serif"
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
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)
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)
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) | \
107 for (
i = 0;
i < buf_size;
i++) {
123 case ARIBCC_LOGLEVEL_ERROR:
126 case ARIBCC_LOGLEVEL_WARNING:
139 if (
ctx->avctx->width > 0 &&
ctx->avctx->height > 0) {
141 ctx->bitmap_plane_width =
ctx->avctx->width;
142 ctx->bitmap_plane_height =
ctx->avctx->height;
143 }
else if (
ctx->plane_width == 960) {
146 ctx->bitmap_plane_width = 1440;
147 ctx->bitmap_plane_height = 1080;
149 ctx->bitmap_plane_width =
ctx->plane_width;
150 ctx->bitmap_plane_height =
ctx->plane_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;
157 ctx->frame_width =
ctx->bitmap_plane_width;
158 ctx->frame_height =
ctx->frame_width *
ctx->plane_height /
ctx->plane_width;
167 if (
ctx->clut_alpha[
i] == 0) {
168 ctx->clut_alpha[
i] =
a;
171 if (
ctx->clut_alpha[
i] ==
a)
186 if (
ctx->clut_alpha[
i] ==
a)
188 if (
ctx->clut_alpha[
i] == 0)
190 if (
abs((
int)
a - (
int)
ctx->clut_alpha[
i]) <
d) {
191 d =
abs((
int)
a - (
int)
ctx->clut_alpha[
i]);
195 return ctx->clut_alpha[j];
202 for (
i = 0;
i <
ctx->clut_idx;
i++) {
203 if (
ctx->clut[
i] == rgba)
228 for (
i = 0;
i <
ctx->clut_idx;
i++) {
229 if (
ctx->clut[
i] == rgba)
241 ctx->clut_overflow++;
244 ctx->clut[
ctx->clut_idx++] = rgba;
253 aribcc_color_t text_color, back_color, stroke_color;
257 ctx->clut_alpha[0] = 0xFF;
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;
264 ctx->clut[
ctx->clut_idx++] = rgba;
267 ctx->clut[
ctx->clut_idx++] = rgba;
271 ctx->clut[
ctx->clut_idx++] = rgba;
275 for (
int i = 1;
i < region->char_count;
i++) {
276 if (region->chars[
i].text_color != text_color) {
280 ctx->clut[
ctx->clut_idx++] = rgba;
284 if (region->chars[
i].back_color != back_color) {
288 ctx->clut[
ctx->clut_idx++] = rgba;
292 if (region->chars[
i].stroke_color != stroke_color) {
297 ctx->clut[
ctx->clut_idx++] = rgba;
319 int old_width =
ctx->frame_width;
320 int old_height =
ctx->frame_height;
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;
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)) {
336 "aribcc_renderer_set_frame_size() returned with error.\n");
341 status = aribcc_renderer_append_caption(
ctx->renderer, &
ctx->caption);
344 "aribcc_renderer_append_caption() returned with error.\n");
348 status = aribcc_renderer_render(
ctx->renderer,
ctx->pts, &
ctx->render_result);
350 case ARIBCC_RENDER_STATUS_GOT_IMAGE:
353 case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
354 aribcc_render_result_cleanup(&
ctx->render_result);
358 case ARIBCC_RENDER_STATUS_NO_IMAGE:
362 case ARIBCC_RENDER_STATUS_ERROR:
364 "aribcc_renderer_render() returned with error.\n");
368 aribcc_render_result_cleanup(&
ctx->render_result);
370 "aribcc_renderer_render() returned unknown status: %d\n",
status);
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);
386 for (
int i = 0;
i <
ctx->render_result.image_count;
i++) {
394 for (rect_idx = 0; rect_idx <
ctx->caption.region_count; rect_idx++) {
396 aribcc_image_t *image = &
ctx->render_result.images[rect_idx];
397 int w,
h, shrink_height, dst_idx;
401 rect->
w = image->width *
ctx->bitmap_plane_width /
ctx->frame_width;
402 rect->
h = image->height *
ctx->bitmap_plane_height /
ctx->frame_height;
404 if (!
rect->data[0]) {
408 if ((image->height !=
rect->
h && image->width !=
rect->
w) ||
409 image->stride < image->width * 4 ||
410 image->stride * image->height > image->bitmap_size) {
412 image->width, image->stride / 4, image->height,
rect->
w,
rect->
h);
417 shrink_height = image->height !=
rect->
h;
422 int n, m, idx0, idx1,
r,
g,
b,
a;
425 div_a =
h *
ctx->frame_height;
426 n =
ctx->bitmap_plane_height;
428 y1 =
FFMIN(y0 + 1, image->height - 1);
430 idx0 = image->stride * y0 +
w * 4;
431 idx1 = image->stride * y1 +
w * 4;
434 div_a =
w *
ctx->frame_width;
435 n =
ctx->bitmap_plane_width;
437 x1 =
FFMIN(x0 + 1, image->width - 1);
439 idx0 = image->stride *
h + x0 * 4;
440 idx1 = image->stride *
h + x1 * 4;
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;
450 if (!
rect->data[1]) {
459 rect->
y =
ctx->frame_height -
rect->
h * (
ctx->caption.region_count - rect_idx);
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;
466 rect->nb_colors = 256;
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,
472 rect_idx,
ctx->clut_idx);
473 if (
ctx->clut_overflow)
482 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
500 const char *font_name;
501 const char *fonts =
ctx->font;
503 if (
ctx->border_style == 4) {
510 if (
ctx->force_stroke_text)
511 outline = (
int)(
ctx->stroke_width * 4.0 / 3.0);
523 "ScriptType: v4.00+\r\n"
531 "Fontname, Fontsize, "
532 "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
533 "Bold, Italic, Underline, StrikeOut, "
536 "BorderStyle, Outline, Shadow, "
537 "Alignment, MarginL, MarginR, MarginV, "
543 "&H%x,&H%x,&H%x,&H%x,"
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,
569 aribcc_color_t new_color, aribcc_color_t old_color)
576 0xFF - ARIBCC_COLOR_A(new_color));
583 bool single_rect =
ctx->ass_single_rect;
584 int ret = 0, rect_idx;
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;
601 if (
ctx->caption.region_count == 0) {
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;
623 y +=
ctx->plane_height;
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,
633 aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
635 aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
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;
642 if (region->is_ruby &&
ctx->ignore_ruby)
649 x +=
ctx->plane_width;
651 y +=
ctx->plane_height;
658 av_bprintf(&buf,
"{\\fs%d}", char_height / 2);
660 for (
int j = 0; j < region->char_count; j++) {
661 aribcc_caption_char_t *ch = ®ion->chars[j];
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;
670 if (ch->char_width != char_width) {
673 char_width = ch->char_width;
675 if (ch->char_height != char_height) {
678 char_height = ch->char_height;
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)
687 (
int)(
ctx->stroke_width * 4.0 / 3.0));
693 if (
diff & ARIBCC_CHARSTYLE_BOLD) {
694 if (charstyle & ARIBCC_CHARSTYLE_BOLD)
699 if (
diff & ARIBCC_CHARSTYLE_ITALIC) {
700 if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
705 if (
diff & ARIBCC_CHARSTYLE_UNDERLINE) {
706 if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
711 charstyle = ch->style;
713 if (ch->text_color != text_color) {
715 text_color = ch->text_color;
717 if (ch->stroke_color != stroke_color) {
719 stroke_color = ch->stroke_color;
721 if (ch->back_color != back_color) {
722 if (
ctx->border_style == 4)
726 back_color = ch->back_color;
728 if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
735 if (
i + 1 <
ctx->caption.region_count)
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,
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,
775 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
804 if (!sub->
rects[0]) {
810 if (
ctx->caption.region_count == 0)
813 text =
ctx->caption.text;
842 int *got_sub_ptr,
const AVPacket *avpkt)
847 ff_dlog(
ctx,
"ARIB caption packet pts=%"PRIx64
":\n", avpkt->
pts);
857 if (
ctx->time_base.num <= 0 ||
ctx->time_base.den <= 0) {
862 ctx->pts = ARIBCC_PTS_NOPTS;
868 if (
status == ARIBCC_DECODE_STATUS_ERROR) {
870 "aribcc_decoder_decode() returned with error.\n");
873 if (
status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
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));
909 aribcc_caption_cleanup(&
ctx->caption);
917 if (
ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
923 aribcc_caption_cleanup(&
ctx->caption);
932 aribcc_decoder_flush(
ctx->decoder);
934 aribcc_renderer_flush(
ctx->renderer);
945 aribcc_renderer_free(
ctx->renderer);
947 aribcc_decoder_free(
ctx->decoder);
949 aribcc_context_free(
ctx->context);
966 ctx->plane_width = 960;
967 ctx->plane_height = 540;
972 ctx->plane_width = 320;
973 ctx->plane_height = 180;
981 if (
ctx->ignore_background)
982 ctx->border_style = 1;
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;
989 if (!(
ctx->context = aribcc_context_alloc())) {
994 if (!(
ctx->decoder = aribcc_decoder_alloc(
ctx->context))) {
998 if (!aribcc_decoder_initialize(
ctx->decoder,
999 (
enum aribcc_encoding_scheme_t)
ctx->encoding_scheme,
1000 ARIBCC_CAPTIONTYPE_CAPTION,
1002 ARIBCC_LANGUAGEID_FIRST)) {
1006 aribcc_decoder_set_replace_msz_fullwidth_ascii(
ctx->decoder,
1007 ctx->replace_fullwidth_ascii);
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;
1027 if(!(
ctx->renderer = aribcc_renderer_alloc(
ctx->context))) {
1031 if(!aribcc_renderer_initialize(
ctx->renderer,
1032 ARIBCC_CAPTIONTYPE_CAPTION,
1033 ARIBCC_FONTPROVIDER_TYPE_AUTO,
1034 ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
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)) {
1047 "aribcc_renderer_set_frame_size() returned with error.\n");
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);
1063 const char **font_families =
NULL;
1064 const char *fonts =
ctx->font;
1067 const char **ff =
av_realloc_array(font_families, count + 1,
sizeof(*font_families));
1074 if (!ff[count - 1]) {
1081 if (!is_nomem && count)
1082 aribcc_renderer_set_default_font_family(
ctx->renderer, font_families, count,
true);
1102 #if !defined(ASS_SINGLE_RECT)
1103 # define ASS_SINGLE_RECT 0
1106 #define OFFSET(x) offsetof(ARIBCaptionContext, x)
1107 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
1109 {
"sub_type",
"subtitle rendering type",
1120 {
"caption_encoding",
"encoding scheme of subtitle text",
1122 ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN,
SD,
"encoding" },
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" },
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]",
1133 {
"font",
"comma-separated font family [ass, bitmap]",
1135 {
"replace_fullwidth_ascii",
"replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
1137 {
"force_outline_text",
"always render characters with outline [(ass), bitmap]",
1139 {
"ignore_background",
"ignore rendering caption background [(ass), bitmap]",
1141 {
"ignore_ruby",
"ignore ruby-like characters [ass, bitmap]",
1143 {
"outline_width",
"outline width of text [(ass), bitmap]",
1145 {
"replace_drcs",
"replace known DRCS [bitmap]",
1147 {
"canvas_size",
"set input video size (WxH or abbreviation) [bitmap]",
1160 .
p.
name =
"libaribcaption",