33 #include <aribcaption/aribcaption.h>
35 #if !defined(DEFAULT_FONT_ASS)
36 # define DEFAULT_FONT_ASS "sans-serif"
39 #define ARIBC_BPRINT_SIZE_INIT 64
40 #define ARIBC_BPRINT_SIZE_MAX (8 * 1024)
41 #define ARIBC_ALPHA_MAX_NUM 4
42 #define ARIBC_ALPHA_DEFAULT_FRONT 0xFF
43 #define ARIBC_ALPHA_DEFAULT_BACK 0x80
45 #define ARIBCC_COLOR_RGB(c) ((c) & 0xFFFFFF)
46 #define ARIBCC_COLOR_DIFF_RGB(c1,c2) (((c1) ^ (c2)) & 0x00FFFFFF)
47 #define ARIBCC_COLOR_DIFF_A(c1,c2) (((c1) ^ (c2)) & 0xFF000000)
49 #define CLUT_RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
50 #define CLUT_A(c) (((c) >> 24) & 0xFF)
51 #define CLUT_R(c) (((c) >> 16) & 0xFF)
52 #define CLUT_G(c) (((c) >> 8) & 0xFF)
53 #define CLUT_B(c) ( (c) & 0xFF)
55 #define ARIBCC_COLOR_TO_CLUT_RGBA(c,a) (((ARIBCC_COLOR_A(c) ? ARIBCC_COLOR_A(c) : (a)) << 24) | \
56 (ARIBCC_COLOR_R(c) << 16) | \
57 (ARIBCC_COLOR_G(c) << 8) | \
110 for (
i = 0;
i < buf_size;
i++) {
126 case ARIBCC_LOGLEVEL_ERROR:
129 case ARIBCC_LOGLEVEL_WARNING:
142 if (
ctx->avctx->width > 0 &&
ctx->avctx->height > 0) {
144 ctx->bitmap_plane_width =
ctx->avctx->width;
145 ctx->bitmap_plane_height =
ctx->avctx->height;
146 }
else if (
ctx->plane_width == 960) {
149 ctx->bitmap_plane_width = 1440;
150 ctx->bitmap_plane_height = 1080;
152 ctx->bitmap_plane_width =
ctx->plane_width;
153 ctx->bitmap_plane_height =
ctx->plane_height;
156 if (
ctx->bitmap_plane_height *
ctx->plane_width >
ctx->bitmap_plane_width *
ctx->plane_height) {
157 ctx->frame_height =
ctx->bitmap_plane_height;
158 ctx->frame_width =
ctx->frame_height *
ctx->plane_width /
ctx->plane_height;
160 ctx->frame_width =
ctx->bitmap_plane_width;
161 ctx->frame_height =
ctx->frame_width *
ctx->plane_height /
ctx->plane_width;
170 if (
ctx->clut_alpha[
i] == 0) {
171 ctx->clut_alpha[
i] =
a;
174 if (
ctx->clut_alpha[
i] ==
a)
189 if (
ctx->clut_alpha[
i] ==
a)
191 if (
ctx->clut_alpha[
i] == 0)
193 if (
abs((
int)
a - (
int)
ctx->clut_alpha[
i]) < d) {
194 d =
abs((
int)
a - (
int)
ctx->clut_alpha[
i]);
198 return ctx->clut_alpha[j];
205 for (
i = 0;
i <
ctx->clut_idx;
i++) {
206 if (
ctx->clut[
i] == rgba)
231 for (
i = 0;
i <
ctx->clut_idx;
i++) {
232 if (
ctx->clut[
i] == rgba)
244 ctx->clut_overflow++;
247 ctx->clut[
ctx->clut_idx++] = rgba;
256 aribcc_color_t text_color, back_color, stroke_color;
260 ctx->clut_alpha[0] = 0xFF;
262 ctx->clut_overflow = 0;
263 text_color = region->chars[0].text_color;
264 back_color = region->chars[0].back_color;
265 stroke_color = region->chars[0].stroke_color;
267 ctx->clut[
ctx->clut_idx++] = rgba;
270 ctx->clut[
ctx->clut_idx++] = rgba;
274 ctx->clut[
ctx->clut_idx++] = rgba;
278 for (
int i = 1;
i < region->char_count;
i++) {
279 if (region->chars[
i].text_color != text_color) {
283 ctx->clut[
ctx->clut_idx++] = rgba;
287 if (region->chars[
i].back_color != back_color) {
291 ctx->clut[
ctx->clut_idx++] = rgba;
295 if (region->chars[
i].stroke_color != stroke_color) {
300 ctx->clut[
ctx->clut_idx++] = rgba;
322 int old_width =
ctx->frame_width;
323 int old_height =
ctx->frame_height;
325 if (
ctx->caption.plane_width > 0 &&
ctx->caption.plane_height > 0) {
326 ctx->plane_width =
ctx->caption.plane_width;
327 ctx->plane_height =
ctx->caption.plane_height;
330 if (
ctx->frame_width != old_width ||
ctx->frame_height != old_height) {
331 ff_dlog(
ctx,
"canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
332 ctx->avctx->width,
ctx->avctx->height,
333 ctx->plane_width,
ctx->plane_height,
334 ctx->bitmap_plane_width,
ctx->bitmap_plane_height,
335 ctx->frame_width,
ctx->frame_height);
336 if (!aribcc_renderer_set_frame_size(
ctx->renderer,
337 ctx->frame_width,
ctx->frame_height)) {
339 "aribcc_renderer_set_frame_size() returned with error.\n");
344 status = aribcc_renderer_append_caption(
ctx->renderer, &
ctx->caption);
347 "aribcc_renderer_append_caption() returned with error.\n");
351 status = aribcc_renderer_render(
ctx->renderer,
ctx->pts, &
ctx->render_result);
353 case ARIBCC_RENDER_STATUS_GOT_IMAGE:
356 case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
357 aribcc_render_result_cleanup(&
ctx->render_result);
361 case ARIBCC_RENDER_STATUS_NO_IMAGE:
365 case ARIBCC_RENDER_STATUS_ERROR:
367 "aribcc_renderer_render() returned with error.\n");
371 aribcc_render_result_cleanup(&
ctx->render_result);
373 "aribcc_renderer_render() returned unknown status: %d\n",
status);
377 if (!
ctx->render_result.image_count ||
ctx->render_result.images ==
NULL) {
378 aribcc_render_result_cleanup(&
ctx->render_result);
379 ff_dlog(
ctx,
"no image (%d)\n",
ctx->render_result.image_count);
389 for (
int i = 0;
i <
ctx->render_result.image_count;
i++) {
397 for (rect_idx = 0; rect_idx <
ctx->caption.region_count; rect_idx++) {
399 aribcc_image_t *image = &
ctx->render_result.images[rect_idx];
400 int w,
h, shrink_height, dst_idx;
404 rect->
w = image->width *
ctx->bitmap_plane_width /
ctx->frame_width;
405 rect->
h = image->height *
ctx->bitmap_plane_height /
ctx->frame_height;
407 if (!
rect->data[0]) {
411 if ((image->height !=
rect->
h && image->width !=
rect->
w) ||
412 image->stride < image->width * 4 ||
413 image->stride * image->height > image->bitmap_size) {
415 image->width, image->stride / 4, image->height,
rect->
w,
rect->
h);
420 shrink_height = image->height !=
rect->
h;
425 int n, m, idx0, idx1,
r,
g,
b,
a;
428 div_a =
h *
ctx->frame_height;
429 n =
ctx->bitmap_plane_height;
431 y1 =
FFMIN(y0 + 1, image->height - 1);
433 idx0 = image->stride * y0 +
w * 4;
434 idx1 = image->stride * y1 +
w * 4;
437 div_a =
w *
ctx->frame_width;
438 n =
ctx->bitmap_plane_width;
440 x1 =
FFMIN(x0 + 1, image->width - 1);
442 idx0 = image->stride *
h + x0 * 4;
443 idx1 = image->stride *
h + x1 * 4;
445 r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
446 g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
447 b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
448 a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
453 if (!
rect->data[1]) {
462 rect->
y =
ctx->frame_height -
rect->
h * (
ctx->caption.region_count - rect_idx);
464 rect->
x = image->dst_x *
ctx->bitmap_plane_width /
ctx->frame_width;
465 rect->
y = image->dst_y *
ctx->bitmap_plane_height /
ctx->frame_height;
469 rect->nb_colors = 256;
471 ff_dlog(
ctx,
"BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n",
472 (
ctx->caption.regions[rect_idx].is_ruby) ?
" (ruby)" :
"",
473 image->dst_x, image->dst_y, image->width, image->height,
475 rect_idx,
ctx->clut_idx);
476 if (
ctx->clut_overflow)
485 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
503 const char *font_name;
504 const char *fonts =
ctx->font;
506 if (
ctx->border_style == 4) {
513 if (
ctx->force_stroke_text)
514 outline = (int)(
ctx->stroke_width * 4.0 / 3.0);
526 "ScriptType: v4.00+\r\n"
534 "Fontname, Fontsize, "
535 "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
536 "Bold, Italic, Underline, StrikeOut, "
539 "BorderStyle, Outline, Shadow, "
540 "Alignment, MarginL, MarginR, MarginV, "
546 "&H%x,&H%x,&H%x,&H%x,"
556 "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
557 ctx->plane_width,
ctx->plane_height,
558 font_name,
ctx->font_size,
572 aribcc_color_t new_color, aribcc_color_t old_color)
579 0xFF - ARIBCC_COLOR_A(new_color));
586 bool single_rect =
ctx->ass_single_rect;
587 int ret = 0, rect_idx;
589 if (
ctx->caption.plane_width > 0 &&
ctx->caption.plane_height > 0 &&
590 (
ctx->caption.plane_width !=
ctx->plane_width ||
591 ctx->caption.plane_height !=
ctx->plane_height)) {
592 ctx->plane_width =
ctx->caption.plane_width;
593 ctx->plane_height =
ctx->caption.plane_height;
604 if (
ctx->caption.region_count == 0) {
614 x =
ctx->plane_width;
615 y =
ctx->plane_height;
616 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
617 rx =
ctx->caption.regions[
i].x;
618 ry =
ctx->caption.regions[
i].y;
626 y +=
ctx->plane_height;
632 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
633 aribcc_caption_region_t *region = &
ctx->caption.regions[
i];
634 aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF,
636 aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
638 aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
640 aribcc_charstyle_t charstyle =
ctx->charstyle;
641 int char_width =
ctx->font_size;
642 int char_height =
ctx->font_size;
643 int char_horizontal_spacing = 0;
645 if (region->is_ruby &&
ctx->ignore_ruby)
652 x +=
ctx->plane_width;
654 y +=
ctx->plane_height;
661 av_bprintf(&buf,
"{\\fs%d}", char_height / 2);
663 for (
int j = 0; j < region->char_count; j++) {
664 aribcc_caption_char_t *ch = ®ion->chars[j];
667 if (ch->char_horizontal_spacing != char_horizontal_spacing) {
668 av_bprintf(&buf,
"{\\fsp%d}", (region->is_ruby) ?
669 ch->char_horizontal_spacing / 2 :
670 ch->char_horizontal_spacing);
671 char_horizontal_spacing = ch->char_horizontal_spacing;
673 if (ch->char_width != char_width) {
676 char_width = ch->char_width;
678 if (ch->char_height != char_height) {
681 char_height = ch->char_height;
684 if (ch->style != charstyle) {
685 aribcc_charstyle_t
diff = ch->style ^ charstyle;
686 if (
diff & ARIBCC_CHARSTYLE_STROKE) {
687 if (charstyle & ARIBCC_CHARSTYLE_STROKE) {
688 if (
ctx->force_stroke_text)
690 (
int)(
ctx->stroke_width * 4.0 / 3.0));
696 if (
diff & ARIBCC_CHARSTYLE_BOLD) {
697 if (charstyle & ARIBCC_CHARSTYLE_BOLD)
702 if (
diff & ARIBCC_CHARSTYLE_ITALIC) {
703 if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
708 if (
diff & ARIBCC_CHARSTYLE_UNDERLINE) {
709 if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
714 charstyle = ch->style;
716 if (ch->text_color != text_color) {
718 text_color = ch->text_color;
720 if (ch->stroke_color != stroke_color) {
722 stroke_color = ch->stroke_color;
724 if (ch->back_color != back_color) {
725 if (
ctx->border_style == 4)
729 back_color = ch->back_color;
731 if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
738 if (
i + 1 <
ctx->caption.region_count)
740 ff_dlog(
ctx,
"ASS subtitle%s (%d,%d) %dx%d [%d]\n",
741 (region->is_ruby) ?
" (ruby)" :
"",
742 region->x, region->y, region->width, region->height,
749 ff_dlog(
ctx,
"ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n",
750 (region->is_ruby) ?
" (ruby)" :
"",
751 region->x, region->y, region->width, region->height,
778 for (
int i = 0;
i <
ctx->caption.region_count;
i++) {
807 if (!sub->
rects[0]) {
813 if (
ctx->caption.region_count == 0)
816 text =
ctx->caption.text;
845 int *got_sub_ptr,
const AVPacket *avpkt)
850 ff_dlog(
ctx,
"ARIB caption packet pts=%"PRIx64
":\n", avpkt->
pts);
860 if (
ctx->time_base.num <= 0 ||
ctx->time_base.den <= 0) {
865 ctx->pts = ARIBCC_PTS_NOPTS;
871 if (
status == ARIBCC_DECODE_STATUS_ERROR) {
873 "aribcc_decoder_decode() returned with error.\n");
876 if (
status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
880 ff_dlog(
ctx,
"type=%02x, flags=%x, lang=%03x\n",
881 ctx->caption.type,
ctx->caption.
flags,
ctx->caption.iso6392_language_code);
882 ff_dlog(
ctx,
"region count = %d, start=%d.%d, duration=%d.%d\n",
883 ctx->caption.region_count,
884 (
int)(
ctx->caption.pts / 1000), (
int)(
ctx->caption.pts % 1000),
885 (
int)((
ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
886 -1 :
ctx->caption.wait_duration / 1000),
887 (
int)((
ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
888 0 :
ctx->caption.wait_duration % 1000));
912 aribcc_caption_cleanup(&
ctx->caption);
920 if (
ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
926 aribcc_caption_cleanup(&
ctx->caption);
935 aribcc_decoder_flush(
ctx->decoder);
937 aribcc_renderer_flush(
ctx->renderer);
948 aribcc_renderer_free(
ctx->renderer);
950 aribcc_decoder_free(
ctx->decoder);
952 aribcc_context_free(
ctx->context);
969 ctx->plane_width = 960;
970 ctx->plane_height = 540;
975 ctx->plane_width = 320;
976 ctx->plane_height = 180;
984 if (
ctx->ignore_background)
985 ctx->border_style = 1;
987 ctx->border_style = 4;
988 ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT;
989 if (
ctx->force_stroke_text ||
ctx->ignore_background)
990 ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE;
992 if (!(
ctx->context = aribcc_context_alloc())) {
997 if (!(
ctx->decoder = aribcc_decoder_alloc(
ctx->context))) {
1001 if (!aribcc_decoder_initialize(
ctx->decoder,
1002 (
enum aribcc_encoding_scheme_t)
ctx->encoding_scheme,
1003 ARIBCC_CAPTIONTYPE_CAPTION,
1005 ARIBCC_LANGUAGEID_FIRST)) {
1009 aribcc_decoder_set_replace_msz_fullwidth_ascii(
ctx->decoder,
1010 ctx->replace_msz_ascii);
1011 aribcc_decoder_set_replace_msz_fullwidth_japanese(
ctx->decoder,
1012 ctx->replace_msz_japanese);
1015 if (
ctx->canvas_width > 0 &&
ctx->canvas_height > 0 &&
1016 (
ctx->avctx->width == 0 ||
ctx->avctx->height == 0)) {
1017 ctx->avctx->width =
ctx->canvas_width;
1018 ctx->avctx->height =
ctx->canvas_height;
1032 if(!(
ctx->renderer = aribcc_renderer_alloc(
ctx->context))) {
1036 if(!aribcc_renderer_initialize(
ctx->renderer,
1037 ARIBCC_CAPTIONTYPE_CAPTION,
1038 ARIBCC_FONTPROVIDER_TYPE_AUTO,
1039 ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
1044 ff_dlog(
ctx,
"canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
1045 ctx->avctx->width,
ctx->avctx->height,
1046 ctx->plane_width,
ctx->plane_height,
1047 ctx->bitmap_plane_width,
ctx->bitmap_plane_height,
1048 ctx->frame_width,
ctx->frame_height);
1049 if (!aribcc_renderer_set_frame_size(
ctx->renderer,
1050 ctx->frame_width,
ctx->frame_height)) {
1052 "aribcc_renderer_set_frame_size() returned with error.\n");
1059 aribcc_renderer_set_storage_policy(
ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0);
1060 aribcc_renderer_set_replace_drcs(
ctx->renderer,
ctx->replace_drcs);
1061 aribcc_renderer_set_force_stroke_text(
ctx->renderer,
ctx->force_stroke_text);
1062 aribcc_renderer_set_force_no_background(
ctx->renderer,
ctx->ignore_background);
1063 aribcc_renderer_set_force_no_ruby(
ctx->renderer,
ctx->ignore_ruby);
1064 aribcc_renderer_set_stroke_width(
ctx->renderer,
ctx->stroke_width);
1065 aribcc_renderer_set_replace_msz_halfwidth_glyph(
ctx->renderer,
1066 ctx->replace_msz_glyph);
1070 const char **font_families =
NULL;
1071 const char *fonts =
ctx->font;
1074 const char **ff =
av_realloc_array(font_families, count + 1,
sizeof(*font_families));
1081 if (!ff[count - 1]) {
1088 if (!is_nomem && count)
1089 aribcc_renderer_set_default_font_family(
ctx->renderer, font_families, count,
true);
1109 #if !defined(ASS_SINGLE_RECT)
1110 # define ASS_SINGLE_RECT 0
1113 #define OFFSET(x) offsetof(ARIBCaptionContext, x)
1114 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
1116 {
"sub_type",
"subtitle rendering type",
1127 {
"caption_encoding",
"encoding scheme of subtitle text",
1129 ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN,
SD, .unit =
"encoding" },
1131 { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags =
SD, .unit =
"encoding" },
1132 {
"jis",
"8bit-char JIS encoding (Japanese ISDB captions)", 0,
AV_OPT_TYPE_CONST,
1133 { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags =
SD, .unit =
"encoding" },
1135 { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags =
SD, .unit =
"encoding" },
1136 {
"latin",
"latin characters (SBTVD / ISDB-Tb captions used in South America)", 0,
AV_OPT_TYPE_CONST,
1137 { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags =
SD, .unit =
"encoding" },
1138 {
"ass_single_rect",
"workaround of ASS subtitle for players which can't handle multi-rectangle [ass]",
1140 {
"font",
"comma-separated font family [ass, bitmap]",
1142 {
"force_outline_text",
"always render characters with outline [(ass), bitmap]",
1144 {
"ignore_background",
"ignore rendering caption background [(ass), bitmap]",
1146 {
"ignore_ruby",
"ignore ruby-like characters [ass, bitmap]",
1148 {
"outline_width",
"outline width of text [(ass), bitmap]",
1150 {
"replace_drcs",
"replace known DRCS [bitmap]",
1152 {
"replace_msz_ascii",
"replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
1154 {
"replace_msz_japanese",
"replace MSZ fullwidth Japanese with halfwidth [ass, bitmap]",
1156 {
"replace_msz_glyph",
"replace MSZ characters with halfwidth glyphs [bitmap]",
1158 {
"canvas_size",
"set input video size (WxH or abbreviation) [bitmap]",
1171 .
p.
name =
"libaribcaption",