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) | \
109 for (
i = 0;
i < buf_size;
i++) {
125 case ARIBCC_LOGLEVEL_ERROR:
128 case ARIBCC_LOGLEVEL_WARNING:
141 if (
ctx->avctx->width > 0 &&
ctx->avctx->height > 0) {
143 ctx->bitmap_plane_width =
ctx->avctx->width;
144 ctx->bitmap_plane_height =
ctx->avctx->height;
145 }
else if (
ctx->plane_width == 960) {
148 ctx->bitmap_plane_width = 1440;
149 ctx->bitmap_plane_height = 1080;
151 ctx->bitmap_plane_width =
ctx->plane_width;
152 ctx->bitmap_plane_height =
ctx->plane_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;
159 ctx->frame_width =
ctx->bitmap_plane_width;
160 ctx->frame_height =
ctx->frame_width *
ctx->plane_height /
ctx->plane_width;
169 if (
ctx->clut_alpha[
i] == 0) {
170 ctx->clut_alpha[
i] =
a;
173 if (
ctx->clut_alpha[
i] ==
a)
188 if (
ctx->clut_alpha[
i] ==
a)
190 if (
ctx->clut_alpha[
i] == 0)
192 if (
abs((
int)
a - (
int)
ctx->clut_alpha[
i]) <
d) {
193 d =
abs((
int)
a - (
int)
ctx->clut_alpha[
i]);
197 return ctx->clut_alpha[j];
204 for (
i = 0;
i <
ctx->clut_idx;
i++) {
205 if (
ctx->clut[
i] == rgba)
230 for (
i = 0;
i <
ctx->clut_idx;
i++) {
231 if (
ctx->clut[
i] == rgba)
243 ctx->clut_overflow++;
246 ctx->clut[
ctx->clut_idx++] = rgba;
255 aribcc_color_t text_color, back_color, stroke_color;
259 ctx->clut_alpha[0] = 0xFF;
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;
266 ctx->clut[
ctx->clut_idx++] = rgba;
269 ctx->clut[
ctx->clut_idx++] = rgba;
273 ctx->clut[
ctx->clut_idx++] = rgba;
277 for (
int i = 1;
i < region->char_count;
i++) {
278 if (region->chars[
i].text_color != text_color) {
282 ctx->clut[
ctx->clut_idx++] = rgba;
286 if (region->chars[
i].back_color != back_color) {
290 ctx->clut[
ctx->clut_idx++] = rgba;
294 if (region->chars[
i].stroke_color != stroke_color) {
299 ctx->clut[
ctx->clut_idx++] = rgba;
321 int old_width =
ctx->frame_width;
322 int old_height =
ctx->frame_height;
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;
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)) {
338 "aribcc_renderer_set_frame_size() returned with error.\n");
343 status = aribcc_renderer_append_caption(
ctx->renderer, &
ctx->caption);
346 "aribcc_renderer_append_caption() returned with error.\n");
350 status = aribcc_renderer_render(
ctx->renderer,
ctx->pts, &
ctx->render_result);
352 case ARIBCC_RENDER_STATUS_GOT_IMAGE:
355 case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
356 aribcc_render_result_cleanup(&
ctx->render_result);
360 case ARIBCC_RENDER_STATUS_NO_IMAGE:
364 case ARIBCC_RENDER_STATUS_ERROR:
366 "aribcc_renderer_render() returned with error.\n");
370 aribcc_render_result_cleanup(&
ctx->render_result);
372 "aribcc_renderer_render() returned unknown status: %d\n",
status);
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);
388 for (
int i = 0;
i <
ctx->render_result.image_count;
i++) {
396 for (rect_idx = 0; rect_idx <
ctx->caption.region_count; rect_idx++) {
398 aribcc_image_t *image = &
ctx->render_result.images[rect_idx];
399 int w,
h, shrink_height, dst_idx;
403 rect->
w = image->width *
ctx->bitmap_plane_width /
ctx->frame_width;
404 rect->
h = image->height *
ctx->bitmap_plane_height /
ctx->frame_height;
406 if (!
rect->data[0]) {
410 if ((image->height !=
rect->
h && image->width !=
rect->
w) ||
411 image->stride < image->width * 4 ||
412 image->stride * image->height > image->bitmap_size) {
414 image->width, image->stride / 4, image->height,
rect->
w,
rect->
h);
419 shrink_height = image->height !=
rect->
h;
424 int n, m, idx0, idx1,
r,
g,
b,
a;
427 div_a =
h *
ctx->frame_height;
428 n =
ctx->bitmap_plane_height;
430 y1 =
FFMIN(y0 + 1, image->height - 1);
432 idx0 = image->stride * y0 +
w * 4;
433 idx1 = image->stride * y1 +
w * 4;
436 div_a =
w *
ctx->frame_width;
437 n =
ctx->bitmap_plane_width;
439 x1 =
FFMIN(x0 + 1, image->width - 1);
441 idx0 = image->stride *
h + x0 * 4;
442 idx1 = image->stride *
h + x1 * 4;
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;
452 if (!
rect->data[1]) {
461 rect->
y =
ctx->frame_height -
rect->
h * (
ctx->caption.region_count - rect_idx);
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;
468 rect->nb_colors = 256;
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,
474 rect_idx,
ctx->clut_idx);
475 if (
ctx->clut_overflow)
484 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
502 const char *font_name;
503 const char *fonts =
ctx->font;
505 if (
ctx->border_style == 4) {
512 if (
ctx->force_stroke_text)
513 outline = (
int)(
ctx->stroke_width * 4.0 / 3.0);
525 "ScriptType: v4.00+\r\n"
533 "Fontname, Fontsize, "
534 "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
535 "Bold, Italic, Underline, StrikeOut, "
538 "BorderStyle, Outline, Shadow, "
539 "Alignment, MarginL, MarginR, MarginV, "
545 "&H%x,&H%x,&H%x,&H%x,"
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,
571 aribcc_color_t new_color, aribcc_color_t old_color)
578 0xFF - ARIBCC_COLOR_A(new_color));
585 bool single_rect =
ctx->ass_single_rect;
586 int ret = 0, rect_idx;
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;
603 if (
ctx->caption.region_count == 0) {
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;
625 y +=
ctx->plane_height;
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,
635 aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
637 aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
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;
644 if (region->is_ruby &&
ctx->ignore_ruby)
651 x +=
ctx->plane_width;
653 y +=
ctx->plane_height;
660 av_bprintf(&buf,
"{\\fs%d}", char_height / 2);
662 for (
int j = 0; j < region->char_count; j++) {
663 aribcc_caption_char_t *ch = ®ion->chars[j];
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;
672 if (ch->char_width != char_width) {
675 char_width = ch->char_width;
677 if (ch->char_height != char_height) {
680 char_height = ch->char_height;
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)
689 (
int)(
ctx->stroke_width * 4.0 / 3.0));
695 if (
diff & ARIBCC_CHARSTYLE_BOLD) {
696 if (charstyle & ARIBCC_CHARSTYLE_BOLD)
701 if (
diff & ARIBCC_CHARSTYLE_ITALIC) {
702 if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
707 if (
diff & ARIBCC_CHARSTYLE_UNDERLINE) {
708 if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
713 charstyle = ch->style;
715 if (ch->text_color != text_color) {
717 text_color = ch->text_color;
719 if (ch->stroke_color != stroke_color) {
721 stroke_color = ch->stroke_color;
723 if (ch->back_color != back_color) {
724 if (
ctx->border_style == 4)
728 back_color = ch->back_color;
730 if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
737 if (
i + 1 <
ctx->caption.region_count)
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,
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,
777 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
806 if (!sub->
rects[0]) {
812 if (
ctx->caption.region_count == 0)
815 text =
ctx->caption.text;
844 int *got_sub_ptr,
const AVPacket *avpkt)
849 ff_dlog(
ctx,
"ARIB caption packet pts=%"PRIx64
":\n", avpkt->
pts);
859 if (
ctx->time_base.num <= 0 ||
ctx->time_base.den <= 0) {
864 ctx->pts = ARIBCC_PTS_NOPTS;
870 if (
status == ARIBCC_DECODE_STATUS_ERROR) {
872 "aribcc_decoder_decode() returned with error.\n");
875 if (
status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
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));
911 aribcc_caption_cleanup(&
ctx->caption);
919 if (
ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
925 aribcc_caption_cleanup(&
ctx->caption);
934 aribcc_decoder_flush(
ctx->decoder);
936 aribcc_renderer_flush(
ctx->renderer);
947 aribcc_renderer_free(
ctx->renderer);
949 aribcc_decoder_free(
ctx->decoder);
951 aribcc_context_free(
ctx->context);
968 ctx->plane_width = 960;
969 ctx->plane_height = 540;
974 ctx->plane_width = 320;
975 ctx->plane_height = 180;
983 if (
ctx->ignore_background)
984 ctx->border_style = 1;
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;
991 if (!(
ctx->context = aribcc_context_alloc())) {
996 if (!(
ctx->decoder = aribcc_decoder_alloc(
ctx->context))) {
1000 if (!aribcc_decoder_initialize(
ctx->decoder,
1001 (
enum aribcc_encoding_scheme_t)
ctx->encoding_scheme,
1002 ARIBCC_CAPTIONTYPE_CAPTION,
1004 ARIBCC_LANGUAGEID_FIRST)) {
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);
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;
1031 if(!(
ctx->renderer = aribcc_renderer_alloc(
ctx->context))) {
1035 if(!aribcc_renderer_initialize(
ctx->renderer,
1036 ARIBCC_CAPTIONTYPE_CAPTION,
1037 ARIBCC_FONTPROVIDER_TYPE_AUTO,
1038 ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
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)) {
1051 "aribcc_renderer_set_frame_size() returned with error.\n");
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);
1069 const char **font_families =
NULL;
1070 const char *fonts =
ctx->font;
1073 const char **ff =
av_realloc_array(font_families, count + 1,
sizeof(*font_families));
1080 if (!ff[count - 1]) {
1087 if (!is_nomem && count)
1088 aribcc_renderer_set_default_font_family(
ctx->renderer, font_families, count,
true);
1108 #if !defined(ASS_SINGLE_RECT)
1109 # define ASS_SINGLE_RECT 0
1112 #define OFFSET(x) offsetof(ARIBCaptionContext, x)
1113 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
1115 {
"sub_type",
"subtitle rendering type",
1126 {
"caption_encoding",
"encoding scheme of subtitle text",
1128 ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN,
SD, .unit =
"encoding" },
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" },
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]",
1139 {
"font",
"comma-separated font family [ass, bitmap]",
1141 {
"force_outline_text",
"always render characters with outline [(ass), bitmap]",
1143 {
"ignore_background",
"ignore rendering caption background [(ass), bitmap]",
1145 {
"ignore_ruby",
"ignore ruby-like characters [ass, bitmap]",
1147 {
"outline_width",
"outline width of text [(ass), bitmap]",
1149 {
"replace_drcs",
"replace known DRCS [bitmap]",
1151 {
"replace_msz_ascii",
"replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
1153 {
"replace_msz_japanese",
"replace MSZ fullwidth Japanese with halfwidth [ass, bitmap]",
1155 {
"replace_msz_glyph",
"replace MSZ characters with halfwidth glyphs [bitmap]",
1157 {
"canvas_size",
"set input video size (WxH or abbreviation) [bitmap]",
1170 .
p.
name =
"libaribcaption",