1/* 2 * ARIB STD-B24 caption decoder using the libaribb24 library 3 * Copyright (c) 2019 Jan Ekström 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 "libavcodec/ass.h" 24#include "codec_internal.h" 25#include "libavutil/log.h" 26#include "libavutil/opt.h" 27 28#include <aribb24/aribb24.h> 29#include <aribb24/parser.h> 30#include <aribb24/decoder.h> 31 32typedef struct Libaribb24Context { 33 AVClass *class; 34 35 arib_instance_t *lib_instance; 36 arib_parser_t *parser; 37 arib_decoder_t *decoder; 38 39 int read_order; 40 41 char *aribb24_base_path; 42 unsigned int aribb24_skip_ruby; 43} Libaribb24Context; 44 45static unsigned int get_profile_font_size(int profile) 46{ 47 switch (profile) { 48 case FF_PROFILE_ARIB_PROFILE_A: 49 return 36; 50 case FF_PROFILE_ARIB_PROFILE_C: 51 return 18; 52 default: 53 return 0; 54 } 55} 56 57static void libaribb24_log(void *p, const char *msg) 58{ 59 av_log((AVCodecContext *)p, AV_LOG_INFO, "%s\n", msg); 60} 61 62static int libaribb24_generate_ass_header(AVCodecContext *avctx) 63{ 64 unsigned int plane_width = 0; 65 unsigned int plane_height = 0; 66 unsigned int font_size = 0; 67 68 switch (avctx->profile) { 69 case FF_PROFILE_ARIB_PROFILE_A: 70 plane_width = 960; 71 plane_height = 540; 72 font_size = get_profile_font_size(avctx->profile); 73 break; 74 case FF_PROFILE_ARIB_PROFILE_C: 75 plane_width = 320; 76 plane_height = 180; 77 font_size = get_profile_font_size(avctx->profile); 78 break; 79 default: 80 av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set!\n"); 81 return AVERROR(EINVAL); 82 } 83 84 avctx->subtitle_header = av_asprintf( 85 "[Script Info]\r\n" 86 "; Script generated by FFmpeg/Lavc%s\r\n" 87 "ScriptType: v4.00+\r\n" 88 "PlayResX: %d\r\n" 89 "PlayResY: %d\r\n" 90 "\r\n" 91 "[V4+ Styles]\r\n" 92 93 /* ASSv4 header */ 94 "Format: Name, " 95 "Fontname, Fontsize, " 96 "PrimaryColour, SecondaryColour, OutlineColour, BackColour, " 97 "Bold, Italic, Underline, StrikeOut, " 98 "ScaleX, ScaleY, " 99 "Spacing, Angle, " 100 "BorderStyle, Outline, Shadow, " 101 "Alignment, MarginL, MarginR, MarginV, " 102 "Encoding\r\n" 103 104 "Style: " 105 "Default," /* Name */ 106 "%s,%d," /* Font{name,size} */ 107 "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */ 108 "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */ 109 "100,100," /* Scale{X,Y} */ 110 "0,0," /* Spacing, Angle */ 111 "%d,1,0," /* BorderStyle, Outline, Shadow */ 112 "%d,10,10,10," /* Alignment, Margin[LRV] */ 113 "0\r\n" /* Encoding */ 114 115 "\r\n" 116 "[Events]\r\n" 117 "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", 118 !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", 119 plane_width, plane_height, 120 ASS_DEFAULT_FONT, font_size, ASS_DEFAULT_COLOR, 121 ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, 122 -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE, 123 ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT); 124 125 if (!avctx->subtitle_header) 126 return AVERROR(ENOMEM); 127 128 avctx->subtitle_header_size = strlen(avctx->subtitle_header); 129 130 return 0; 131} 132 133static int libaribb24_init(AVCodecContext *avctx) 134{ 135 Libaribb24Context *b24 = avctx->priv_data; 136 void(* arib_dec_init)(arib_decoder_t* decoder) = NULL; 137 int ret_code = AVERROR_EXTERNAL; 138 139 if (!(b24->lib_instance = arib_instance_new(avctx))) { 140 av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24!\n"); 141 goto init_fail; 142 } 143 144 if (b24->aribb24_base_path) { 145 av_log(avctx, AV_LOG_INFO, "Setting the libaribb24 base path to '%s'\n", 146 b24->aribb24_base_path); 147 arib_set_base_path(b24->lib_instance, b24->aribb24_base_path); 148 } 149 150 arib_register_messages_callback(b24->lib_instance, libaribb24_log); 151 152 if (!(b24->parser = arib_get_parser(b24->lib_instance))) { 153 av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 PES parser!\n"); 154 goto init_fail; 155 } 156 if (!(b24->decoder = arib_get_decoder(b24->lib_instance))) { 157 av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 decoder!\n"); 158 goto init_fail; 159 } 160 161 switch (avctx->profile) { 162 case FF_PROFILE_ARIB_PROFILE_A: 163 arib_dec_init = arib_initialize_decoder_a_profile; 164 break; 165 case FF_PROFILE_ARIB_PROFILE_C: 166 arib_dec_init = arib_initialize_decoder_c_profile; 167 break; 168 default: 169 av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set!\n"); 170 ret_code = AVERROR(EINVAL); 171 goto init_fail; 172 } 173 174 arib_dec_init(b24->decoder); 175 176 if (libaribb24_generate_ass_header(avctx) < 0) { 177 ret_code = AVERROR(ENOMEM); 178 goto init_fail; 179 } 180 181 return 0; 182 183init_fail: 184 if (b24->decoder) 185 arib_finalize_decoder(b24->decoder); 186 187 if (b24->lib_instance) 188 arib_instance_destroy(b24->lib_instance); 189 190 return ret_code; 191} 192 193static int libaribb24_close(AVCodecContext *avctx) 194{ 195 Libaribb24Context *b24 = avctx->priv_data; 196 197 if (b24->decoder) 198 arib_finalize_decoder(b24->decoder); 199 200 if (b24->lib_instance) 201 arib_instance_destroy(b24->lib_instance); 202 203 return 0; 204} 205 206#define RGB_TO_BGR(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff)) 207 208static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub) 209{ 210 Libaribb24Context *b24 = avctx->priv_data; 211 const arib_buf_region_t *region = arib_decoder_get_regions(b24->decoder); 212 unsigned int profile_font_size = get_profile_font_size(avctx->profile); 213 AVBPrint buf = { 0 }; 214 int ret = 0; 215 216 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); 217 218 while (region) { 219 ptrdiff_t region_length = region->p_end - region->p_start; 220 unsigned int ruby_region = 221 region->i_fontheight == (profile_font_size / 2); 222 223 // ASS requires us to make the colors BGR, so we convert here 224 int foreground_bgr_color = RGB_TO_BGR(region->i_foreground_color); 225 int background_bgr_color = RGB_TO_BGR(region->i_background_color); 226 227 if (region_length < 0) { 228 av_log(avctx, AV_LOG_ERROR, "Invalid negative region length!\n"); 229 ret = AVERROR_INVALIDDATA; 230 break; 231 } 232 233 if (region_length == 0 || (ruby_region && b24->aribb24_skip_ruby)) { 234 goto next_region; 235 } 236 237 // color and alpha 238 if (foreground_bgr_color != ASS_DEFAULT_COLOR) 239 av_bprintf(&buf, "{\\1c&H%06x&}", foreground_bgr_color); 240 241 if (region->i_foreground_alpha != 0) 242 av_bprintf(&buf, "{\\1a&H%02x&}", region->i_foreground_alpha); 243 244 if (background_bgr_color != ASS_DEFAULT_BACK_COLOR) 245 av_bprintf(&buf, "{\\3c&H%06x&}", background_bgr_color); 246 247 if (region->i_background_alpha != 0) 248 av_bprintf(&buf, "{\\3a&H%02x&}", region->i_background_alpha); 249 250 // font size 251 if (region->i_fontwidth != profile_font_size || 252 region->i_fontheight != profile_font_size) { 253 av_bprintf(&buf, "{\\fscx%"PRId64"\\fscy%"PRId64"}", 254 av_rescale(region->i_fontwidth, 100, 255 profile_font_size), 256 av_rescale(region->i_fontheight, 100, 257 profile_font_size)); 258 } 259 260 // TODO: positioning 261 262 av_bprint_append_data(&buf, region->p_start, region_length); 263 264 av_bprintf(&buf, "{\\r}"); 265 266next_region: 267 region = region->p_next; 268 } 269 270 if (!av_bprint_is_complete(&buf)) 271 ret = AVERROR(ENOMEM); 272 273 if (ret == 0) { 274 av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n", 275 buf.str); 276 277 ret = ff_ass_add_rect(sub, buf.str, b24->read_order++, 278 0, NULL, NULL); 279 } 280 281 av_bprint_finalize(&buf, NULL); 282 283 return ret; 284} 285 286static int libaribb24_decode(AVCodecContext *avctx, AVSubtitle *sub, 287 int *got_sub_ptr, const AVPacket *pkt) 288{ 289 Libaribb24Context *b24 = avctx->priv_data; 290 size_t parsed_data_size = 0; 291 size_t decoded_subtitle_size = 0; 292 const unsigned char *parsed_data = NULL; 293 char *decoded_subtitle = NULL; 294 time_t subtitle_duration = 0; 295 int ret = 0; 296 297 if (pkt->size <= 0) 298 return pkt->size; 299 300 arib_parse_pes(b24->parser, pkt->data, pkt->size); 301 302 parsed_data = arib_parser_get_data(b24->parser, 303 &parsed_data_size); 304 if (!parsed_data || !parsed_data_size) { 305 av_log(avctx, AV_LOG_DEBUG, "No decode'able data was received from " 306 "packet (dts: %"PRId64", pts: %"PRId64").\n", 307 pkt->dts, pkt->pts); 308 return pkt->size; 309 } 310 311 decoded_subtitle_size = parsed_data_size * 4; 312 if (!(decoded_subtitle = av_mallocz(decoded_subtitle_size + 1))) { 313 av_log(avctx, AV_LOG_ERROR, 314 "Failed to allocate buffer for decoded subtitle!\n"); 315 return AVERROR(ENOMEM); 316 } 317 318 decoded_subtitle_size = arib_decode_buffer(b24->decoder, 319 parsed_data, 320 parsed_data_size, 321 decoded_subtitle, 322 decoded_subtitle_size); 323 324 subtitle_duration = arib_decoder_get_time(b24->decoder); 325 326 if (avctx->pkt_timebase.num && pkt->pts != AV_NOPTS_VALUE) 327 sub->pts = av_rescale_q(pkt->pts, 328 avctx->pkt_timebase, AV_TIME_BASE_Q); 329 330 sub->end_display_time = subtitle_duration ? 331 av_rescale_q(subtitle_duration, 332 AV_TIME_BASE_Q, 333 (AVRational){1, 1000}) : 334 UINT32_MAX; 335 336 av_log(avctx, AV_LOG_DEBUG, 337 "Result: '%s' (size: %zu, pkt_pts: %"PRId64", sub_pts: %"PRId64" " 338 "duration: %"PRIu32", pkt_timebase: %d/%d, time_base: %d/%d')\n", 339 decoded_subtitle ? decoded_subtitle : "<no subtitle>", 340 decoded_subtitle_size, 341 pkt->pts, sub->pts, 342 sub->end_display_time, 343 avctx->pkt_timebase.num, avctx->pkt_timebase.den, 344 avctx->time_base.num, avctx->time_base.den); 345 346 if (decoded_subtitle) 347 ret = libaribb24_handle_regions(avctx, sub); 348 349 *got_sub_ptr = sub->num_rects > 0; 350 351 av_free(decoded_subtitle); 352 353 // flush the region buffers, otherwise the linked list keeps getting 354 // longer and longer... 355 arib_finalize_decoder(b24->decoder); 356 357 return ret < 0 ? ret : pkt->size; 358} 359 360static void libaribb24_flush(AVCodecContext *avctx) 361{ 362 Libaribb24Context *b24 = avctx->priv_data; 363 if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) 364 b24->read_order = 0; 365} 366 367#define OFFSET(x) offsetof(Libaribb24Context, x) 368#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM 369static const AVOption options[] = { 370 { "aribb24-base-path", "set the base path for the libaribb24 library", 371 OFFSET(aribb24_base_path), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD }, 372 { "aribb24-skip-ruby-text", "skip ruby text blocks during decoding", 373 OFFSET(aribb24_skip_ruby), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, 374 { NULL } 375}; 376 377static const AVClass aribb24_class = { 378 .class_name = "libaribb24 decoder", 379 .item_name = av_default_item_name, 380 .option = options, 381 .version = LIBAVUTIL_VERSION_INT, 382}; 383 384const FFCodec ff_libaribb24_decoder = { 385 .p.name = "libaribb24", 386 .p.long_name = NULL_IF_CONFIG_SMALL("libaribb24 ARIB STD-B24 caption decoder"), 387 .p.type = AVMEDIA_TYPE_SUBTITLE, 388 .p.id = AV_CODEC_ID_ARIB_CAPTION, 389 .p.priv_class = &aribb24_class, 390 .p.wrapper_name = "libaribb24", 391 .priv_data_size = sizeof(Libaribb24Context), 392 .init = libaribb24_init, 393 .close = libaribb24_close, 394 FF_CODEC_DECODE_SUB_CB(libaribb24_decode), 395 .flush = libaribb24_flush, 396}; 397