| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- /*
- * ARIB STD-B24 caption decoder using the libaribcaption library
- * Copyright (c) 2022 TADANO Tokumei
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include "avcodec.h"
- #include "codec_internal.h"
- #include "internal.h"
- #include "libavcodec/ass.h"
- #include "libavutil/avstring.h"
- #include "libavutil/avutil.h"
- #include "libavutil/mem.h"
- #include "libavutil/thread.h"
- #include "libavutil/log.h"
- #include "libavutil/opt.h"
- #include <aribcaption/aribcaption.h>
- #if !defined(DEFAULT_FONT_ASS)
- # define DEFAULT_FONT_ASS "sans-serif"
- #endif
- #define ARIBC_BPRINT_SIZE_INIT 64
- #define ARIBC_BPRINT_SIZE_MAX (8 * 1024)
- #define ARIBC_ALPHA_MAX_NUM 4
- #define ARIBC_ALPHA_DEFAULT_FRONT 0xFF
- #define ARIBC_ALPHA_DEFAULT_BACK 0x80
- #define ARIBCC_COLOR_RGB(c) ((c) & 0xFFFFFF)
- #define ARIBCC_COLOR_DIFF_RGB(c1,c2) (((c1) ^ (c2)) & 0x00FFFFFF)
- #define ARIBCC_COLOR_DIFF_A(c1,c2) (((c1) ^ (c2)) & 0xFF000000)
- #define CLUT_RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
- #define CLUT_A(c) (((c) >> 24) & 0xFF)
- #define CLUT_R(c) (((c) >> 16) & 0xFF)
- #define CLUT_G(c) (((c) >> 8) & 0xFF)
- #define CLUT_B(c) ( (c) & 0xFF)
- #define ARIBCC_COLOR_TO_CLUT_RGBA(c,a) (((ARIBCC_COLOR_A(c) ? ARIBCC_COLOR_A(c) : (a)) << 24) | \
- (ARIBCC_COLOR_R(c) << 16) | \
- (ARIBCC_COLOR_G(c) << 8) | \
- (ARIBCC_COLOR_B(c)))
- typedef struct ARIBCaptionContext {
- AVClass *class;
- AVCodecContext *avctx;
- const AVPacket *avpkt;
- AVSubtitle *sub;
- aribcc_context_t *context;
- aribcc_decoder_t *decoder;
- aribcc_renderer_t *renderer;
- int subtitle_type;
- int encoding_scheme;
- int ass_single_rect;
- char *font;
- int force_stroke_text;
- int ignore_background;
- int ignore_ruby;
- float stroke_width;
- int replace_drcs;
- int replace_msz_ascii;
- int replace_msz_japanese;
- int replace_msz_glyph;
- int64_t pts;
- AVRational time_base;
- int canvas_width;
- int canvas_height;
- int plane_width;
- int plane_height;
- int frame_width;
- int frame_height;
- int bitmap_plane_width;
- int bitmap_plane_height;
- int font_size;
- int charstyle;
- int border_style;
- int readorder;
- aribcc_caption_t caption;
- aribcc_render_result_t render_result;
- uint32_t *clut;
- int clut_idx;
- int clut_overflow;
- uint8_t clut_alpha[ARIBC_ALPHA_MAX_NUM];
- } ARIBCaptionContext;
- static void hex_dump_debug(void *ctx, const char *buf, int buf_size)
- {
- int i;
- for (i = 0; i < buf_size; i++) {
- ff_dlog(ctx, "%02hhx ", buf[i]);
- if (i % 16 == 15)
- ff_dlog(ctx, "\n");
- }
- if (i % 16)
- ff_dlog(ctx, "\n");
- }
- static void logcat_callback(aribcc_loglevel_t level, const char* message, void* userdata)
- {
- ARIBCaptionContext *ctx = userdata;
- int lvl;
- if (ctx->decoder != NULL) {
- switch (level) {
- case ARIBCC_LOGLEVEL_ERROR:
- lvl = AV_LOG_ERROR;
- break;
- case ARIBCC_LOGLEVEL_WARNING:
- lvl = AV_LOG_WARNING;
- break;
- default:
- lvl = AV_LOG_INFO;
- }
- av_log(ctx, lvl, "%s\n", message);
- }
- }
- static void estimate_video_frame_size(ARIBCaptionContext *ctx)
- {
- if (ctx->avctx->width > 0 && ctx->avctx->height > 0) {
- /* input video size specified by -canvas_size option */
- ctx->bitmap_plane_width = ctx->avctx->width;
- ctx->bitmap_plane_height = ctx->avctx->height;
- } else if (ctx->plane_width == 960) {
- /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] 4.3.1 */
- /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] Appendix-4 */
- ctx->bitmap_plane_width = 1440;
- ctx->bitmap_plane_height = 1080;
- } else {
- ctx->bitmap_plane_width = ctx->plane_width;
- ctx->bitmap_plane_height = ctx->plane_height;
- }
- /* Expand either width or height */
- if (ctx->bitmap_plane_height * ctx->plane_width > ctx->bitmap_plane_width * ctx->plane_height) {
- ctx->frame_height = ctx->bitmap_plane_height;
- ctx->frame_width = ctx->frame_height * ctx->plane_width / ctx->plane_height;
- } else {
- ctx->frame_width = ctx->bitmap_plane_width;
- ctx->frame_height = ctx->frame_width * ctx->plane_height / ctx->plane_width;
- }
- }
- static void clut_set_alpha(ARIBCaptionContext *ctx, uint8_t a)
- {
- int i;
- for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
- if (ctx->clut_alpha[i] == 0) {
- ctx->clut_alpha[i] = a;
- return;
- }
- if (ctx->clut_alpha[i] == a)
- return;
- }
- return;
- }
- static uint8_t clut_find_nearlest_alpha(ARIBCaptionContext *ctx, uint8_t a)
- {
- int i, j, d;
- if (a == 0)
- return a;
- d = 256;
- j = 0;
- for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) {
- if (ctx->clut_alpha[i] == a)
- return a;
- if (ctx->clut_alpha[i] == 0)
- break;
- if (abs((int)a - (int)ctx->clut_alpha[i]) < d) {
- d = abs((int)a - (int)ctx->clut_alpha[i]);
- j = i;
- }
- }
- return ctx->clut_alpha[j];
- }
- static int clut_find(ARIBCaptionContext *ctx, uint32_t rgba)
- {
- int i;
- for (i = 0; i < ctx->clut_idx; i++) {
- if (ctx->clut[i] == rgba)
- return i;
- }
- return -1;
- }
- static inline int clut_color_distance(uint32_t rgba1, uint32_t rgba2)
- {
- return abs((int)CLUT_R(rgba1) - (int)CLUT_R(rgba2)) +
- abs((int)CLUT_G(rgba1) - (int)CLUT_G(rgba2)) +
- abs((int)CLUT_B(rgba1) - (int)CLUT_B(rgba2));
- }
- static uint8_t clut_pick_or_set(ARIBCaptionContext *ctx, int r, int g, int b, int a)
- {
- int c, i, d, d_min;
- uint32_t rgba;
- a = clut_find_nearlest_alpha(ctx, a);
- if (a == 0)
- return 0; /* transparent */
- rgba = CLUT_RGBA(r,g,b,a);
- d_min = 256 * 3;
- c = 0;
- for (i = 0; i < ctx->clut_idx; i++) {
- if (ctx->clut[i] == rgba)
- return i;
- if (CLUT_A(ctx->clut[i]) != a)
- continue;
- d = clut_color_distance(ctx->clut[i], rgba);
- if (d < d_min) {
- d_min = d;
- c = i;
- }
- }
- if (d_min > 3) {
- if (ctx->clut_idx >= AVPALETTE_COUNT)
- ctx->clut_overflow++;
- else {
- c = ctx->clut_idx;
- ctx->clut[ctx->clut_idx++] = rgba;
- }
- }
- return c;
- }
- /* initialize CLUT with each character colors */
- static void clut_init(ARIBCaptionContext *ctx, aribcc_caption_region_t *region)
- {
- aribcc_color_t text_color, back_color, stroke_color;
- uint32_t rgba;
- ctx->clut[0] = CLUT_RGBA(0,0,0,0); /* transparent */
- ctx->clut_alpha[0] = 0xFF;
- ctx->clut_idx = 1;
- ctx->clut_overflow = 0;
- text_color = region->chars[0].text_color;
- back_color = region->chars[0].back_color;
- stroke_color = region->chars[0].stroke_color;
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(text_color, ARIBC_ALPHA_DEFAULT_FRONT);
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(back_color, ARIBC_ALPHA_DEFAULT_BACK);
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(stroke_color, ARIBC_ALPHA_DEFAULT_FRONT);
- if (clut_find(ctx, rgba) < 0) {
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- }
- for (int i = 1; i < region->char_count; i++) {
- if (region->chars[i].text_color != text_color) {
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].text_color,
- ARIBC_ALPHA_DEFAULT_FRONT);
- if (clut_find(ctx, rgba) < 0) {
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- }
- }
- if (region->chars[i].back_color != back_color) {
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].back_color,
- ARIBC_ALPHA_DEFAULT_BACK);
- if (clut_find(ctx, rgba) < 0) {
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- }
- }
- if (region->chars[i].stroke_color != stroke_color) {
- rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].stroke_color,
- ARIBC_ALPHA_DEFAULT_FRONT);
- if (clut_find(ctx, rgba) < 0) {
- if (ctx->clut_idx < AVPALETTE_COUNT)
- ctx->clut[ctx->clut_idx++] = rgba;
- clut_set_alpha(ctx, CLUT_A(rgba));
- }
- }
- }
- }
- /**
- * aribcaption_trans_{bitmap|ass|text}_subtitle()
- *
- * Transfer decoded subtitle to AVSubtitle with corresponding subtitle type.
- *
- * @param ctx pointer to the ARIBCaptionContext
- * @return > 0 number of rectangles to be displayed
- * = 0 no subtitle
- * < 0 error code
- */
- static int aribcaption_trans_bitmap_subtitle(ARIBCaptionContext *ctx)
- {
- int ret = 0;
- AVSubtitle *sub = ctx->sub;
- int status, rect_idx;
- int old_width = ctx->frame_width;
- int old_height = ctx->frame_height;
- if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0) {
- ctx->plane_width = ctx->caption.plane_width;
- ctx->plane_height = ctx->caption.plane_height;
- }
- estimate_video_frame_size(ctx);
- if (ctx->frame_width != old_width || ctx->frame_height != old_height) {
- ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
- ctx->avctx->width, ctx->avctx->height,
- ctx->plane_width, ctx->plane_height,
- ctx->bitmap_plane_width, ctx->bitmap_plane_height,
- ctx->frame_width, ctx->frame_height);
- if (!aribcc_renderer_set_frame_size(ctx->renderer,
- ctx->frame_width, ctx->frame_height)) {
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_renderer_set_frame_size() returned with error.\n");
- return AVERROR_EXTERNAL;
- }
- }
- status = aribcc_renderer_append_caption(ctx->renderer, &ctx->caption);
- if (!status) {
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_renderer_append_caption() returned with error.\n");
- return AVERROR_EXTERNAL;
- }
- status = aribcc_renderer_render(ctx->renderer, ctx->pts, &ctx->render_result);
- switch (status) {
- case ARIBCC_RENDER_STATUS_GOT_IMAGE:
- break;
- case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED:
- aribcc_render_result_cleanup(&ctx->render_result);
- ff_dlog(ctx, "got image unchanged\n");
- return 0;
- case ARIBCC_RENDER_STATUS_NO_IMAGE:
- ff_dlog(ctx, "no image\n");
- return 0;
- case ARIBCC_RENDER_STATUS_ERROR:
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_renderer_render() returned with error.\n");
- return AVERROR_EXTERNAL;
- default:
- aribcc_render_result_cleanup(&ctx->render_result);
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_renderer_render() returned unknown status: %d\n", status);
- return AVERROR_EXTERNAL;
- }
- if (!ctx->render_result.image_count || ctx->render_result.images == NULL) {
- aribcc_render_result_cleanup(&ctx->render_result);
- ff_dlog(ctx, "no image (%d)\n", ctx->render_result.image_count);
- return 0;
- }
- sub->format = 0; /* graphic */
- sub->rects = av_calloc(ctx->render_result.image_count, sizeof(*sub->rects));
- if (!sub->rects) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- for (int i = 0; i < ctx->render_result.image_count; i++) {
- sub->rects[i] = av_mallocz(sizeof(*sub->rects[i]));
- if (!sub->rects[i]) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- }
- for (rect_idx = 0; rect_idx < ctx->caption.region_count; rect_idx++) {
- AVSubtitleRect *rect = sub->rects[rect_idx];
- aribcc_image_t *image = &ctx->render_result.images[rect_idx];
- int w, h, shrink_height, dst_idx;
- clut_init(ctx, &ctx->caption.regions[rect_idx]);
- rect->w = image->width * ctx->bitmap_plane_width / ctx->frame_width;
- rect->h = image->height * ctx->bitmap_plane_height / ctx->frame_height;
- rect->data[0] = av_mallocz(rect->w * rect->h);
- if (!rect->data[0]) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- if ((image->height != rect->h && image->width != rect->w) ||
- image->stride < image->width * 4 ||
- image->stride * image->height > image->bitmap_size) {
- av_log(ctx, AV_LOG_ERROR, "Bug: unexpected rendered image: %d(%d)x%d -> %dx%d\n",
- image->width, image->stride / 4, image->height, rect->w, rect->h);
- ret = AVERROR_EXTERNAL;
- goto fail;
- }
- shrink_height = image->height != rect->h;
- dst_idx = 0;
- for (h = 0; h < rect->h; h++) {
- for (w = 0; w < rect->w; w++) {
- /* Bi-linear interpolation */
- int n, m, idx0, idx1, r, g, b, a;
- if (shrink_height) {
- int div_a, y0, y1;
- div_a = h * ctx->frame_height;
- n = ctx->bitmap_plane_height;
- y0 = div_a / n;
- y1 = FFMIN(y0 + 1, image->height - 1);
- m = div_a - n * y0;
- idx0 = image->stride * y0 + w * 4;
- idx1 = image->stride * y1 + w * 4;
- } else {
- int div_a, x0, x1;
- div_a = w * ctx->frame_width;
- n = ctx->bitmap_plane_width;
- x0 = div_a / n;
- x1 = FFMIN(x0 + 1, image->width - 1);
- m = div_a - n * x0;
- idx0 = image->stride * h + x0 * 4;
- idx1 = image->stride * h + x1 * 4;
- }
- r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
- g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
- b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
- a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n;
- rect->data[0][dst_idx++] = clut_pick_or_set(ctx, r, g, b, a);
- }
- }
- rect->data[1] = av_memdup(ctx->clut, AVPALETTE_SIZE);
- if (!rect->data[1]) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C) {
- /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
- /* No position information is provided for profile C */
- rect->x = (ctx->frame_width - rect->w) / 2;
- rect->y = ctx->frame_height - rect->h * (ctx->caption.region_count - rect_idx);
- } else {
- rect->x = image->dst_x * ctx->bitmap_plane_width / ctx->frame_width;
- rect->y = image->dst_y * ctx->bitmap_plane_height / ctx->frame_height;
- }
- rect->type = SUBTITLE_BITMAP;
- rect->linesize[0] = rect->w;
- rect->nb_colors = 256;
- ff_dlog(ctx, "BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n",
- (ctx->caption.regions[rect_idx].is_ruby) ? " (ruby)" : "",
- image->dst_x, image->dst_y, image->width, image->height,
- rect->x, rect->y, rect->w, rect->h,
- rect_idx, ctx->clut_idx);
- if (ctx->clut_overflow)
- av_log(ctx, AV_LOG_WARNING, "CLUT overflow (%d).\n", ctx->clut_overflow);
- }
- sub->num_rects = rect_idx;
- return rect_idx;
- fail:
- if (sub->rects) {
- for (int i = 0; i < ctx->caption.region_count; i++) {
- if (sub->rects[i]) {
- av_freep(&sub->rects[i]->data[0]);
- av_freep(&sub->rects[i]->data[1]);
- av_freep(&sub->rects[i]);
- }
- }
- av_freep(&sub->rects);
- }
- sub->num_rects = 0;
- return ret;
- }
- static int set_ass_header(ARIBCaptionContext *ctx)
- {
- AVCodecContext *avctx = ctx->avctx;
- int outline, shadow;
- const char *font_name;
- const char *fonts = ctx->font;
- if (ctx->border_style == 4) {
- outline = 0;
- shadow = 4;
- } else {
- outline = 1;
- shadow = 0;
- }
- if (ctx->force_stroke_text)
- outline = (int)(ctx->stroke_width * 4.0 / 3.0);
- if (fonts && *fonts)
- font_name = av_get_token(&fonts, ",");
- else
- font_name = av_strdup(DEFAULT_FONT_ASS);
- if (!font_name)
- return AVERROR(ENOMEM);
- av_freep(&avctx->subtitle_header);
- avctx->subtitle_header = av_asprintf(
- "[Script Info]\n"
- "ScriptType: v4.00+\n"
- "PlayResX: %d\n"
- "PlayResY: %d\n"
- "WrapStyle: 2\n" /* 2: no word wrapping */
- "\n"
- "[V4+ Styles]\n"
- "Format: Name, "
- "Fontname, Fontsize, "
- "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
- "Bold, Italic, Underline, StrikeOut, "
- "ScaleX, ScaleY, "
- "Spacing, Angle, "
- "BorderStyle, Outline, Shadow, "
- "Alignment, MarginL, MarginR, MarginV, "
- "Encoding\n"
- "Style: "
- "Default," /* Name */
- "%s,%d," /* Font{name,size} */
- "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */
- "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */
- "100,100," /* Scale{X,Y} */
- "0,0," /* Spacing, Angle */
- "%d,%d,%d," /* BorderStyle, Outline, Shadow */
- "%d,10,10,10," /* Alignment, Margin[LRV] */
- "0\n" /* Encoding */
- "\n"
- "[Events]\n"
- "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
- ctx->plane_width, ctx->plane_height,
- font_name, ctx->font_size,
- ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR,
- ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
- -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE,
- ctx->border_style, outline, shadow, ASS_DEFAULT_ALIGNMENT);
- av_freep(&font_name);
- if (!avctx->subtitle_header)
- return AVERROR(ENOMEM);
- avctx->subtitle_header_size = strlen(avctx->subtitle_header);
- return 0;
- }
- static void set_ass_color(AVBPrint *buf, int color_num,
- aribcc_color_t new_color, aribcc_color_t old_color)
- {
- if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color))
- av_bprintf(buf, "{\\%dc&H%06x&}", color_num,
- ARIBCC_COLOR_RGB(new_color));
- if (ARIBCC_COLOR_DIFF_A(new_color, old_color))
- av_bprintf(buf, "{\\%da&H%02x&}", color_num,
- 0xFF - ARIBCC_COLOR_A(new_color));
- }
- static int aribcaption_trans_ass_subtitle(ARIBCaptionContext *ctx)
- {
- AVSubtitle *sub = ctx->sub;
- AVBPrint buf;
- bool single_rect = ctx->ass_single_rect;
- int ret = 0, rect_idx;
- if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0 &&
- (ctx->caption.plane_width != ctx->plane_width ||
- ctx->caption.plane_height != ctx->plane_height)) {
- ctx->plane_width = ctx->caption.plane_width;
- ctx->plane_height = ctx->caption.plane_height;
- if ((ret = set_ass_header(ctx)) < 0)
- return ret;
- }
- /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */
- /* No position information is provided for profile C */
- if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C)
- single_rect = true;
- sub->format = 1; /* text */
- if (ctx->caption.region_count == 0) {
- /* clear previous caption for indefinite duration */
- ff_ass_add_rect(sub, "", ctx->readorder++, 0, NULL, NULL);
- return 1;
- }
- av_bprint_init(&buf, ARIBC_BPRINT_SIZE_INIT, ARIBC_BPRINT_SIZE_MAX);
- if (single_rect && ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) {
- int x, y, rx, ry;
- x = ctx->plane_width;
- y = ctx->plane_height;
- for (int i = 0; i < ctx->caption.region_count; i++) {
- rx = ctx->caption.regions[i].x;
- ry = ctx->caption.regions[i].y;
- if (rx < x)
- x = rx;
- if (ry < y)
- y = ry;
- }
- av_bprintf(&buf, "{\\an7}");
- if (y < 0)
- y += ctx->plane_height;
- if (x > 0 || y > 0)
- av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
- }
- rect_idx = 0;
- for (int i = 0; i < ctx->caption.region_count; i++) {
- aribcc_caption_region_t *region = &ctx->caption.regions[i];
- aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF,
- ARIBC_ALPHA_DEFAULT_FRONT);
- aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0,
- ARIBC_ALPHA_DEFAULT_FRONT);
- aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0,
- ARIBC_ALPHA_DEFAULT_BACK);
- aribcc_charstyle_t charstyle = ctx->charstyle;
- int char_width = ctx->font_size;
- int char_height = ctx->font_size;
- int char_horizontal_spacing = 0;
- if (region->is_ruby && ctx->ignore_ruby)
- continue;
- if (!single_rect) {
- int x = region->x;
- int y = region->y;
- if (x < 0)
- x += ctx->plane_width;
- if (y < 0)
- y += ctx->plane_height;
- av_bprint_clear(&buf);
- av_bprintf(&buf, "{\\an7}");
- if (x > 0 || y > 0)
- av_bprintf(&buf, "{\\pos(%d,%d)}", x, y);
- }
- if (region->is_ruby)
- av_bprintf(&buf, "{\\fs%d}", char_height / 2);
- for (int j = 0; j < region->char_count; j++) {
- aribcc_caption_char_t *ch = ®ion->chars[j];
- if (ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) {
- if (ch->char_horizontal_spacing != char_horizontal_spacing) {
- av_bprintf(&buf, "{\\fsp%d}", (region->is_ruby) ?
- ch->char_horizontal_spacing / 2 :
- ch->char_horizontal_spacing);
- char_horizontal_spacing = ch->char_horizontal_spacing;
- }
- if (ch->char_width != char_width) {
- av_bprintf(&buf, "{\\fscx%"PRId64"}",
- av_rescale(ch->char_width, 100, ctx->font_size));
- char_width = ch->char_width;
- }
- if (ch->char_height != char_height) {
- av_bprintf(&buf, "{\\fscy%"PRId64"}",
- av_rescale(ch->char_height, 100, ctx->font_size));
- char_height = ch->char_height;
- }
- }
- if (ch->style != charstyle) {
- aribcc_charstyle_t diff = ch->style ^ charstyle;
- if (diff & ARIBCC_CHARSTYLE_STROKE) {
- if (charstyle & ARIBCC_CHARSTYLE_STROKE) {
- if (ctx->force_stroke_text)
- av_bprintf(&buf, "{\\bord%d}",
- (int)(ctx->stroke_width * 4.0 / 3.0));
- else
- av_bprintf(&buf, "{\\bord0}");
- } else
- av_bprintf(&buf, "{\\bord3}");
- }
- if (diff & ARIBCC_CHARSTYLE_BOLD) {
- if (charstyle & ARIBCC_CHARSTYLE_BOLD)
- av_bprintf(&buf, "{\\b0}");
- else
- av_bprintf(&buf, "{\\b1}");
- }
- if (diff & ARIBCC_CHARSTYLE_ITALIC) {
- if (charstyle & ARIBCC_CHARSTYLE_ITALIC)
- av_bprintf(&buf, "{\\i0}");
- else
- av_bprintf(&buf, "{\\i1}");
- }
- if (diff & ARIBCC_CHARSTYLE_UNDERLINE) {
- if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE)
- av_bprintf(&buf, "{\\u0}");
- else
- av_bprintf(&buf, "{\\u1}");
- }
- charstyle = ch->style;
- }
- if (ch->text_color != text_color) {
- set_ass_color(&buf, 1, ch->text_color, text_color);
- text_color = ch->text_color;
- }
- if (ch->stroke_color != stroke_color) {
- set_ass_color(&buf, 3, ch->stroke_color, stroke_color);
- stroke_color = ch->stroke_color;
- }
- if (ch->back_color != back_color) {
- if (ctx->border_style == 4)
- set_ass_color(&buf, 4, ch->back_color, back_color);
- else
- set_ass_color(&buf, 3, ch->back_color, back_color);
- back_color = ch->back_color;
- }
- if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS)
- av_bprintf(&buf, "\xe3\x80\x93"); /* Geta Mark */
- else
- ff_ass_bprint_text_event(&buf, ch->u8str, strlen(ch->u8str), "", 0);
- }
- if (single_rect) {
- if (i + 1 < ctx->caption.region_count)
- av_bprintf(&buf, "{\\r}\\N");
- ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]\n",
- (region->is_ruby) ? " (ruby)" : "",
- region->x, region->y, region->width, region->height,
- rect_idx);
- } else {
- if (!av_bprint_is_complete(&buf)) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n",
- (region->is_ruby) ? " (ruby)" : "",
- region->x, region->y, region->width, region->height,
- rect_idx, buf.str);
- ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
- if (ret != 0)
- goto fail;
- rect_idx++;
- }
- }
- if (single_rect) {
- if (!av_bprint_is_complete(&buf)) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- ff_dlog(ctx, "ASS subtitle: %s\n", buf.str);
- ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL);
- if (ret != 0)
- goto fail;
- rect_idx++;
- }
- av_bprint_finalize(&buf, NULL);
- return rect_idx;
- fail:
- if (sub->rects) {
- for (int i = 0; i < ctx->caption.region_count; i++) {
- if (sub->rects[i]) {
- av_freep(&sub->rects[i]->ass);
- av_freep(&sub->rects[i]);
- }
- }
- av_freep(&sub->rects);
- }
- sub->num_rects = 0;
- av_bprint_finalize(&buf, NULL);
- return ret;
- }
- static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx)
- {
- AVSubtitle *sub = ctx->sub;
- AVSubtitleRect *rect;
- int ret = 0;
- const char *text;
- sub->rects = av_calloc(ctx->caption.region_count, sizeof(*sub->rects));
- if (!sub->rects) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- sub->num_rects = 1;
- sub->rects[0] = av_mallocz(sizeof(*sub->rects[0]));
- if (!sub->rects[0]) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- rect = sub->rects[0];
- if (ctx->caption.region_count == 0)
- text = ""; /* clear previous caption */
- else {
- text = ctx->caption.text;
- ff_dlog(ctx, "TEXT subtitle: %s\n", text);
- }
- rect->text = av_strdup(text);
- if (!rect->text) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
- sub->format = 1; /* text */
- rect->type = SUBTITLE_TEXT;
- return 1;
- fail:
- if (sub->rects) {
- rect = sub->rects[0];
- if (rect) {
- av_freep(&rect->text);
- av_freep(&rect);
- }
- av_freep(&sub->rects);
- }
- sub->num_rects = 0;
- return ret;
- }
- static int aribcaption_decode(AVCodecContext *avctx, AVSubtitle *sub,
- int *got_sub_ptr, const AVPacket *avpkt)
- {
- ARIBCaptionContext *ctx = avctx->priv_data;
- int status;
- ff_dlog(ctx, "ARIB caption packet pts=%"PRIx64":\n", avpkt->pts);
- if (sub->num_rects) {
- avpriv_request_sample(ctx, "Different Version of Segment asked Twice");
- return AVERROR_PATCHWELCOME;
- }
- hex_dump_debug(ctx, avpkt->data, avpkt->size);
- ctx->sub = sub;
- ctx->avpkt = avpkt;
- ctx->time_base = avctx->pkt_timebase;
- if (ctx->time_base.num <= 0 || ctx->time_base.den <= 0) {
- av_log(ctx, AV_LOG_VERBOSE, "No timebase set. assuming 90kHz.\n");
- ctx->time_base = av_make_q(1, 90000);
- }
- if (avpkt->pts == AV_NOPTS_VALUE)
- ctx->pts = ARIBCC_PTS_NOPTS;
- else
- ctx->pts = av_rescale_q(avpkt->pts, ctx->time_base, (AVRational){1, 1000});
- status = aribcc_decoder_decode(ctx->decoder, avpkt->data, avpkt->size,
- ctx->pts, &ctx->caption);
- if (status == ARIBCC_DECODE_STATUS_ERROR) {
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_decoder_decode() returned with error.\n");
- return AVERROR(EAGAIN);
- }
- if (status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
- ff_dlog(ctx, "No caption.\n");
- return avpkt->size;
- } else {
- ff_dlog(ctx, "type=%02x, flags=%x, lang=%03x\n",
- ctx->caption.type, ctx->caption.flags, ctx->caption.iso6392_language_code);
- ff_dlog(ctx, "region count = %d, start=%d.%d, duration=%d.%d\n",
- ctx->caption.region_count,
- (int)(ctx->caption.pts / 1000), (int)(ctx->caption.pts % 1000),
- (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
- -1 : ctx->caption.wait_duration / 1000),
- (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ?
- 0 : ctx->caption.wait_duration % 1000));
- }
- switch ((enum AVSubtitleType) ctx->subtitle_type) {
- case SUBTITLE_TEXT:
- status = aribcaption_trans_text_subtitle(ctx);
- break;
- case SUBTITLE_ASS:
- status = aribcaption_trans_ass_subtitle(ctx);
- break;
- case SUBTITLE_BITMAP:
- status = aribcaption_trans_bitmap_subtitle(ctx);
- break;
- case SUBTITLE_NONE:
- default:
- status = 0;
- }
- if (status < 0) {
- av_log(ctx, AV_LOG_ERROR, "Failed to set Subtitle: %s\n",
- av_err2str(status));
- aribcc_caption_cleanup(&ctx->caption);
- return status;
- }
- if (status > 0) {
- *got_sub_ptr = 1;
- if (ctx->avpkt->pts != AV_NOPTS_VALUE)
- sub->pts = av_rescale_q(ctx->avpkt->pts,
- ctx->time_base, AV_TIME_BASE_Q);
- if (ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE)
- sub->end_display_time = UINT32_MAX;
- else
- sub->end_display_time = (uint32_t)ctx->caption.wait_duration;
- }
- aribcc_caption_cleanup(&ctx->caption);
- return avpkt->size;
- }
- static void aribcaption_flush(AVCodecContext *avctx)
- {
- ARIBCaptionContext *ctx = avctx->priv_data;
- if (ctx->decoder)
- aribcc_decoder_flush(ctx->decoder);
- if (ctx->renderer)
- aribcc_renderer_flush(ctx->renderer);
- if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
- ctx->readorder = 0;
- }
- static av_cold int aribcaption_close(AVCodecContext *avctx)
- {
- ARIBCaptionContext *ctx = avctx->priv_data;
- av_freep(&ctx->clut);
- if (ctx->renderer)
- aribcc_renderer_free(ctx->renderer);
- if (ctx->decoder)
- aribcc_decoder_free(ctx->decoder);
- if (ctx->context)
- aribcc_context_free(ctx->context);
- return 0;
- }
- static av_cold int aribcaption_init(AVCodecContext *avctx)
- {
- ARIBCaptionContext *ctx = avctx->priv_data;
- aribcc_profile_t profile;
- int ret = 0;
- ctx->avctx = avctx;
- switch (avctx->profile) {
- case AV_PROFILE_ARIB_PROFILE_A:
- profile = ARIBCC_PROFILE_A;
- /* assume 960x540 at initial state */
- ctx->plane_width = 960;
- ctx->plane_height = 540;
- ctx->font_size = 36;
- break;
- case AV_PROFILE_ARIB_PROFILE_C:
- profile = ARIBCC_PROFILE_C;
- ctx->plane_width = 320;
- ctx->plane_height = 180;
- ctx->font_size = 16;
- break;
- default:
- av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n");
- return AVERROR(EINVAL);
- }
- /* determine BorderStyle of ASS header */
- if (ctx->ignore_background)
- ctx->border_style = 1;
- else
- ctx->border_style = 4;
- ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT;
- if (ctx->force_stroke_text || ctx->ignore_background)
- ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE;
- if (!(ctx->context = aribcc_context_alloc())) {
- av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption context.\n");
- return AVERROR_EXTERNAL;
- }
- aribcc_context_set_logcat_callback(ctx->context, logcat_callback, avctx);
- if (!(ctx->decoder = aribcc_decoder_alloc(ctx->context))) {
- av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption decoder.\n");
- return AVERROR_EXTERNAL;
- }
- if (!aribcc_decoder_initialize(ctx->decoder,
- (enum aribcc_encoding_scheme_t) ctx->encoding_scheme,
- ARIBCC_CAPTIONTYPE_CAPTION,
- profile,
- ARIBCC_LANGUAGEID_FIRST)) {
- av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption decoder.\n");
- return AVERROR_EXTERNAL;
- }
- aribcc_decoder_set_replace_msz_fullwidth_ascii(ctx->decoder,
- ctx->replace_msz_ascii);
- aribcc_decoder_set_replace_msz_fullwidth_japanese(ctx->decoder,
- ctx->replace_msz_japanese);
- /* Similar behavior as ffmpeg tool to set canvas size */
- if (ctx->canvas_width > 0 && ctx->canvas_height > 0 &&
- (ctx->avctx->width == 0 || ctx->avctx->height == 0)) {
- ctx->avctx->width = ctx->canvas_width;
- ctx->avctx->height = ctx->canvas_height;
- }
- switch ((enum AVSubtitleType) ctx->subtitle_type) {
- case SUBTITLE_ASS:
- ret = set_ass_header(ctx);
- if (ret != 0) {
- av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n",
- av_err2str(ret));
- return ret;
- }
- break;
- case SUBTITLE_BITMAP:
- if(!(ctx->renderer = aribcc_renderer_alloc(ctx->context))) {
- av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption renderer.\n");
- return AVERROR_EXTERNAL;
- }
- if(!aribcc_renderer_initialize(ctx->renderer,
- ARIBCC_CAPTIONTYPE_CAPTION,
- ARIBCC_FONTPROVIDER_TYPE_AUTO,
- ARIBCC_TEXTRENDERER_TYPE_AUTO)) {
- av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption renderer.\n");
- return AVERROR_EXTERNAL;
- }
- estimate_video_frame_size(ctx);
- ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n",
- ctx->avctx->width, ctx->avctx->height,
- ctx->plane_width, ctx->plane_height,
- ctx->bitmap_plane_width, ctx->bitmap_plane_height,
- ctx->frame_width, ctx->frame_height);
- if (!aribcc_renderer_set_frame_size(ctx->renderer,
- ctx->frame_width, ctx->frame_height)) {
- av_log(ctx, AV_LOG_ERROR,
- "aribcc_renderer_set_frame_size() returned with error.\n");
- return AVERROR_EXTERNAL;
- }
- if (!(ctx->clut = av_mallocz(AVPALETTE_SIZE)))
- return AVERROR(ENOMEM);
- aribcc_renderer_set_storage_policy(ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0);
- aribcc_renderer_set_replace_drcs(ctx->renderer, ctx->replace_drcs);
- aribcc_renderer_set_force_stroke_text(ctx->renderer, ctx->force_stroke_text);
- aribcc_renderer_set_force_no_background(ctx->renderer, ctx->ignore_background);
- aribcc_renderer_set_force_no_ruby(ctx->renderer, ctx->ignore_ruby);
- aribcc_renderer_set_stroke_width(ctx->renderer, ctx->stroke_width);
- aribcc_renderer_set_replace_msz_halfwidth_glyph(ctx->renderer,
- ctx->replace_msz_glyph);
- if (ctx->font) {
- int is_nomem = 0;
- size_t count = 0;
- const char **font_families = NULL;
- const char *fonts = ctx->font;
- while (*fonts) {
- const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families));
- if (!ff) {
- is_nomem = 1;
- break;
- } else {
- font_families = ff;
- ff[count++] = av_get_token(&fonts, ",");
- if (!ff[count - 1]) {
- is_nomem = 1;
- break;
- } else if (*fonts)
- fonts++;
- }
- }
- if (!is_nomem && count)
- aribcc_renderer_set_default_font_family(ctx->renderer, font_families, count, true);
- while (count)
- av_freep(&font_families[--count]);
- av_freep(&font_families);
- if (is_nomem)
- return AVERROR(ENOMEM);
- }
- break;
- case SUBTITLE_TEXT:
- case SUBTITLE_NONE:
- default:
- /* do nothing */ ;
- }
- ctx->readorder = 0;
- return 0;
- }
- #if !defined(ASS_SINGLE_RECT)
- # define ASS_SINGLE_RECT 0
- #endif
- #define OFFSET(x) offsetof(ARIBCaptionContext, x)
- #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
- static const AVOption options[] = {
- { "sub_type", "subtitle rendering type",
- OFFSET(subtitle_type), AV_OPT_TYPE_INT,
- { .i64 = SUBTITLE_ASS }, SUBTITLE_NONE, SUBTITLE_ASS, SD, .unit = "type" },
- { "none", "do nothing", 0, AV_OPT_TYPE_CONST,
- { .i64 = SUBTITLE_NONE }, .flags = SD, .unit = "type" },
- { "bitmap", "bitmap rendering", 0, AV_OPT_TYPE_CONST,
- { .i64 = SUBTITLE_BITMAP }, .flags = SD, .unit = "type" },
- { "text", "plain text", 0, AV_OPT_TYPE_CONST,
- { .i64 = SUBTITLE_TEXT }, .flags = SD, .unit = "type" },
- { "ass", "formatted text", 0, AV_OPT_TYPE_CONST,
- { .i64 = SUBTITLE_ASS }, .flags = SD, .unit = "type" },
- { "caption_encoding", "encoding scheme of subtitle text",
- OFFSET(encoding_scheme), AV_OPT_TYPE_INT, { .i64 = ARIBCC_ENCODING_SCHEME_AUTO },
- ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN, SD, .unit = "encoding" },
- { "auto", "automatically detect encoding scheme", 0, AV_OPT_TYPE_CONST,
- { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags = SD, .unit = "encoding" },
- { "jis", "8bit-char JIS encoding (Japanese ISDB captions)", 0, AV_OPT_TYPE_CONST,
- { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags = SD, .unit = "encoding" },
- { "utf8", "UTF-8 encoding (Philippines ISDB-T captions)", 0, AV_OPT_TYPE_CONST,
- { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags = SD, .unit = "encoding" },
- { "latin", "latin characters (SBTVD / ISDB-Tb captions used in South America)", 0, AV_OPT_TYPE_CONST,
- { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags = SD, .unit = "encoding" },
- { "ass_single_rect", "workaround of ASS subtitle for players which can't handle multi-rectangle [ass]",
- OFFSET(ass_single_rect), AV_OPT_TYPE_BOOL, { .i64 = ASS_SINGLE_RECT }, 0, 1, SD },
- { "font", "comma-separated font family [ass, bitmap]",
- OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
- { "force_outline_text", "always render characters with outline [(ass), bitmap]",
- OFFSET(force_stroke_text), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
- { "ignore_background", "ignore rendering caption background [(ass), bitmap]",
- OFFSET(ignore_background), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
- { "ignore_ruby", "ignore ruby-like characters [ass, bitmap]",
- OFFSET(ignore_ruby), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD },
- { "outline_width", "outline width of text [(ass), bitmap]",
- OFFSET(stroke_width), AV_OPT_TYPE_FLOAT, { .dbl = 1.5 }, 0.0, 3.0, SD },
- { "replace_drcs", "replace known DRCS [bitmap]",
- OFFSET(replace_drcs), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
- { "replace_msz_ascii", "replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]",
- OFFSET(replace_msz_ascii), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
- { "replace_msz_japanese", "replace MSZ fullwidth Japanese with halfwidth [ass, bitmap]",
- OFFSET(replace_msz_japanese), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
- { "replace_msz_glyph", "replace MSZ characters with halfwidth glyphs [bitmap]",
- OFFSET(replace_msz_glyph), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
- {"canvas_size", "set input video size (WxH or abbreviation) [bitmap]",
- OFFSET(canvas_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, SD },
- { NULL }
- };
- static const AVClass aribcaption_class = {
- .class_name = "aribcaption decoder",
- .item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
- };
- const FFCodec ff_libaribcaption_decoder = {
- .p.name = "libaribcaption",
- .p.long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption decoder"),
- .p.type = AVMEDIA_TYPE_SUBTITLE,
- .p.id = AV_CODEC_ID_ARIB_CAPTION,
- .priv_data_size = sizeof(ARIBCaptionContext),
- .init = aribcaption_init,
- .close = aribcaption_close,
- FF_CODEC_DECODE_SUB_CB(aribcaption_decode),
- .flush = aribcaption_flush,
- .p.priv_class = &aribcaption_class,
- .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
- };
|