FFmpeg  4.4.4
libaribb24.c
Go to the documentation of this file.
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 "libavutil/log.h"
25 #include "libavutil/opt.h"
26 
27 #include <aribb24/aribb24.h>
28 #include <aribb24/parser.h>
29 #include <aribb24/decoder.h>
30 
31 typedef struct Libaribb24Context {
32  AVClass *class;
33 
34  arib_instance_t *lib_instance;
35  arib_parser_t *parser;
36  arib_decoder_t *decoder;
37 
39 
41  unsigned int aribb24_skip_ruby;
43 
44 static unsigned int get_profile_font_size(int profile)
45 {
46  switch (profile) {
48  return 36;
50  return 18;
51  default:
52  return 0;
53  }
54 }
55 
56 static void libaribb24_log(void *p, const char *msg)
57 {
58  av_log((AVCodecContext *)p, AV_LOG_INFO, "%s\n", msg);
59 }
60 
62 {
63  unsigned int plane_width = 0;
64  unsigned int plane_height = 0;
65  unsigned int font_size = 0;
66 
67  switch (avctx->profile) {
69  plane_width = 960;
70  plane_height = 540;
71  font_size = get_profile_font_size(avctx->profile);
72  break;
74  plane_width = 320;
75  plane_height = 180;
76  font_size = get_profile_font_size(avctx->profile);
77  break;
78  default:
79  av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set!\n");
80  return AVERROR(EINVAL);
81  }
82 
84  "[Script Info]\r\n"
85  "; Script generated by FFmpeg/Lavc%s\r\n"
86  "ScriptType: v4.00+\r\n"
87  "PlayResX: %d\r\n"
88  "PlayResY: %d\r\n"
89  "\r\n"
90  "[V4+ Styles]\r\n"
91 
92  /* ASSv4 header */
93  "Format: Name, "
94  "Fontname, Fontsize, "
95  "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
96  "Bold, Italic, Underline, StrikeOut, "
97  "ScaleX, ScaleY, "
98  "Spacing, Angle, "
99  "BorderStyle, Outline, Shadow, "
100  "Alignment, MarginL, MarginR, MarginV, "
101  "Encoding\r\n"
102 
103  "Style: "
104  "Default," /* Name */
105  "%s,%d," /* Font{name,size} */
106  "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */
107  "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */
108  "100,100," /* Scale{X,Y} */
109  "0,0," /* Spacing, Angle */
110  "%d,1,0," /* BorderStyle, Outline, Shadow */
111  "%d,10,10,10," /* Alignment, Margin[LRV] */
112  "0\r\n" /* Encoding */
113 
114  "\r\n"
115  "[Events]\r\n"
116  "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
118  plane_width, plane_height,
123 
124  if (!avctx->subtitle_header)
125  return AVERROR(ENOMEM);
126 
127  avctx->subtitle_header_size = strlen(avctx->subtitle_header);
128 
129  return 0;
130 }
131 
132 static int libaribb24_init(AVCodecContext *avctx)
133 {
134  Libaribb24Context *b24 = avctx->priv_data;
135  void(* arib_dec_init)(arib_decoder_t* decoder) = NULL;
136  int ret_code = AVERROR_EXTERNAL;
137 
138  if (!(b24->lib_instance = arib_instance_new(avctx))) {
139  av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24!\n");
140  goto init_fail;
141  }
142 
143  if (b24->aribb24_base_path) {
144  av_log(avctx, AV_LOG_INFO, "Setting the libaribb24 base path to '%s'\n",
145  b24->aribb24_base_path);
146  arib_set_base_path(b24->lib_instance, b24->aribb24_base_path);
147  }
148 
149  arib_register_messages_callback(b24->lib_instance, libaribb24_log);
150 
151  if (!(b24->parser = arib_get_parser(b24->lib_instance))) {
152  av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 PES parser!\n");
153  goto init_fail;
154  }
155  if (!(b24->decoder = arib_get_decoder(b24->lib_instance))) {
156  av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 decoder!\n");
157  goto init_fail;
158  }
159 
160  switch (avctx->profile) {
162  arib_dec_init = arib_initialize_decoder_a_profile;
163  break;
165  arib_dec_init = arib_initialize_decoder_c_profile;
166  break;
167  default:
168  av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set!\n");
169  ret_code = AVERROR(EINVAL);
170  goto init_fail;
171  }
172 
173  arib_dec_init(b24->decoder);
174 
175  if (libaribb24_generate_ass_header(avctx) < 0) {
176  ret_code = AVERROR(ENOMEM);
177  goto init_fail;
178  }
179 
180  return 0;
181 
182 init_fail:
183  if (b24->decoder)
184  arib_finalize_decoder(b24->decoder);
185 
186  if (b24->lib_instance)
187  arib_instance_destroy(b24->lib_instance);
188 
189  return ret_code;
190 }
191 
193 {
194  Libaribb24Context *b24 = avctx->priv_data;
195 
196  if (b24->decoder)
197  arib_finalize_decoder(b24->decoder);
198 
199  if (b24->lib_instance)
200  arib_instance_destroy(b24->lib_instance);
201 
202  return 0;
203 }
204 
205 #define RGB_TO_BGR(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff))
206 
208 {
209  Libaribb24Context *b24 = avctx->priv_data;
210  const arib_buf_region_t *region = arib_decoder_get_regions(b24->decoder);
211  unsigned int profile_font_size = get_profile_font_size(avctx->profile);
212  AVBPrint buf = { 0 };
213  int ret = 0;
214 
216 
217  while (region) {
218  ptrdiff_t region_length = region->p_end - region->p_start;
219  unsigned int ruby_region =
220  region->i_fontheight == (profile_font_size / 2);
221 
222  // ASS requires us to make the colors BGR, so we convert here
223  int foreground_bgr_color = RGB_TO_BGR(region->i_foreground_color);
224  int background_bgr_color = RGB_TO_BGR(region->i_background_color);
225 
226  if (region_length < 0) {
227  av_log(avctx, AV_LOG_ERROR, "Invalid negative region length!\n");
228  ret = AVERROR_INVALIDDATA;
229  break;
230  }
231 
232  if (region_length == 0 || (ruby_region && b24->aribb24_skip_ruby)) {
233  goto next_region;
234  }
235 
236  // color and alpha
237  if (foreground_bgr_color != ASS_DEFAULT_COLOR)
238  av_bprintf(&buf, "{\\1c&H%06x&}", foreground_bgr_color);
239 
240  if (region->i_foreground_alpha != 0)
241  av_bprintf(&buf, "{\\1a&H%02x&}", region->i_foreground_alpha);
242 
243  if (background_bgr_color != ASS_DEFAULT_BACK_COLOR)
244  av_bprintf(&buf, "{\\3c&H%06x&}", background_bgr_color);
245 
246  if (region->i_background_alpha != 0)
247  av_bprintf(&buf, "{\\3a&H%02x&}", region->i_background_alpha);
248 
249  // font size
250  if (region->i_fontwidth != profile_font_size ||
251  region->i_fontheight != profile_font_size) {
252  av_bprintf(&buf, "{\\fscx%"PRId64"\\fscy%"PRId64"}",
253  av_rescale(region->i_fontwidth, 100,
254  profile_font_size),
255  av_rescale(region->i_fontheight, 100,
256  profile_font_size));
257  }
258 
259  // TODO: positioning
260 
261  av_bprint_append_data(&buf, region->p_start, region_length);
262 
263  av_bprintf(&buf, "{\\r}");
264 
265 next_region:
266  region = region->p_next;
267  }
268 
269  if (!av_bprint_is_complete(&buf))
270  ret = AVERROR(ENOMEM);
271 
272  if (ret == 0) {
273  av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n",
274  buf.str);
275 
276  ret = ff_ass_add_rect(sub, buf.str, b24->read_order++,
277  0, NULL, NULL);
278  }
279 
280  av_bprint_finalize(&buf, NULL);
281 
282  return ret;
283 }
284 
285 static int libaribb24_decode(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt)
286 {
287  Libaribb24Context *b24 = avctx->priv_data;
288  AVSubtitle *sub = data;
289  size_t parsed_data_size = 0;
290  size_t decoded_subtitle_size = 0;
291  const unsigned char *parsed_data = NULL;
292  char *decoded_subtitle = NULL;
293  time_t subtitle_duration = 0;
294  int ret = 0;
295 
296  if (pkt->size <= 0)
297  return pkt->size;
298 
299  arib_parse_pes(b24->parser, pkt->data, pkt->size);
300 
301  parsed_data = arib_parser_get_data(b24->parser,
302  &parsed_data_size);
303  if (!parsed_data || !parsed_data_size) {
304  av_log(avctx, AV_LOG_DEBUG, "No decode'able data was received from "
305  "packet (dts: %"PRId64", pts: %"PRId64").\n",
306  pkt->dts, pkt->pts);
307  return pkt->size;
308  }
309 
310  decoded_subtitle_size = parsed_data_size * 4;
311  if (!(decoded_subtitle = av_mallocz(decoded_subtitle_size + 1))) {
312  av_log(avctx, AV_LOG_ERROR,
313  "Failed to allocate buffer for decoded subtitle!\n");
314  return AVERROR(ENOMEM);
315  }
316 
317  decoded_subtitle_size = arib_decode_buffer(b24->decoder,
318  parsed_data,
319  parsed_data_size,
320  decoded_subtitle,
321  decoded_subtitle_size);
322 
323  subtitle_duration = arib_decoder_get_time(b24->decoder);
324 
325  if (avctx->pkt_timebase.num && pkt->pts != AV_NOPTS_VALUE)
326  sub->pts = av_rescale_q(pkt->pts,
327  avctx->pkt_timebase, AV_TIME_BASE_Q);
328 
329  sub->end_display_time = subtitle_duration ?
330  av_rescale_q(subtitle_duration,
332  (AVRational){1, 1000}) :
333  UINT32_MAX;
334 
335  av_log(avctx, AV_LOG_DEBUG,
336  "Result: '%s' (size: %zu, pkt_pts: %"PRId64", sub_pts: %"PRId64" "
337  "duration: %"PRIu32", pkt_timebase: %d/%d, time_base: %d/%d')\n",
338  decoded_subtitle ? decoded_subtitle : "<no subtitle>",
339  decoded_subtitle_size,
340  pkt->pts, sub->pts,
341  sub->end_display_time,
342  avctx->pkt_timebase.num, avctx->pkt_timebase.den,
343  avctx->time_base.num, avctx->time_base.den);
344 
345  if (decoded_subtitle)
346  ret = libaribb24_handle_regions(avctx, sub);
347 
348  *got_sub_ptr = sub->num_rects > 0;
349 
350  av_free(decoded_subtitle);
351 
352  // flush the region buffers, otherwise the linked list keeps getting
353  // longer and longer...
354  arib_finalize_decoder(b24->decoder);
355 
356  return ret < 0 ? ret : pkt->size;
357 }
358 
359 static void libaribb24_flush(AVCodecContext *avctx)
360 {
361  Libaribb24Context *b24 = avctx->priv_data;
362  if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
363  b24->read_order = 0;
364 }
365 
366 #define OFFSET(x) offsetof(Libaribb24Context, x)
367 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
368 static const AVOption options[] = {
369  { "aribb24-base-path", "set the base path for the libaribb24 library",
370  OFFSET(aribb24_base_path), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD },
371  { "aribb24-skip-ruby-text", "skip ruby text blocks during decoding",
372  OFFSET(aribb24_skip_ruby), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD },
373  { NULL }
374 };
375 
376 static const AVClass aribb24_class = {
377  .class_name = "libaribb24 decoder",
378  .item_name = av_default_item_name,
379  .option = options,
380  .version = LIBAVUTIL_VERSION_INT,
381 };
382 
384  .name = "libaribb24",
385  .long_name = NULL_IF_CONFIG_SMALL("libaribb24 ARIB STD-B24 caption decoder"),
386  .type = AVMEDIA_TYPE_SUBTITLE,
388  .priv_data_size = sizeof(Libaribb24Context),
390  .close = libaribb24_close,
393  .priv_class= &aribb24_class,
394  .wrapper_name = "libaribb24",
395 };
static void flush(AVCodecContext *avctx)
int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, const char *speaker)
Add an ASS dialog to a subtitle.
Definition: ass.c:118
#define ASS_DEFAULT_ALIGNMENT
Definition: ass.h:42
#define ASS_DEFAULT_COLOR
Definition: ass.h:37
#define ASS_DEFAULT_BORDERSTYLE
Definition: ass.h:43
#define ASS_DEFAULT_BACK_COLOR
Definition: ass.h:38
#define ASS_DEFAULT_FONT
Definition: ass.h:35
#define ASS_DEFAULT_BOLD
Definition: ass.h:39
#define ASS_DEFAULT_UNDERLINE
Definition: ass.h:41
#define ASS_DEFAULT_ITALIC
Definition: ass.h:40
Libavcodec external API header.
#define FF_PROFILE_ARIB_PROFILE_C
Definition: avcodec.h:1974
#define FF_PROFILE_ARIB_PROFILE_A
Definition: avcodec.h:1973
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:31
char * av_asprintf(const char *fmt,...)
Definition: avstring.c:113
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
Append data to a print buffer.
Definition: bprint.c:158
#define AV_BPRINT_SIZE_UNLIMITED
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:185
#define NULL
Definition: coverity.c:32
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile)
Definition: decode_audio.c:71
static float sub(float src0, float src1)
@ AV_OPT_TYPE_BOOL
Definition: opt.h:242
@ AV_OPT_TYPE_STRING
Definition: opt.h:229
#define AV_CODEC_FLAG2_RO_FLUSH_NOOP
Do not reset ASS ReadOrder field on flush (subtitles decoding)
Definition: avcodec.h:388
#define AV_CODEC_FLAG_BITEXACT
Use only bitexact stuff (except (I)DCT).
Definition: avcodec.h:333
@ AV_CODEC_ID_ARIB_CAPTION
Definition: codec_id.h:549
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
#define AVERROR(e)
Definition: error.h:43
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:215
#define AV_LOG_INFO
Standard information.
Definition: log.h:205
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:235
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
Definition: mathematics.c:129
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:142
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:237
@ AVMEDIA_TYPE_SUBTITLE
Definition: avutil.h:204
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:260
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
#define AV_STRINGIFY(s)
Definition: macros.h:36
#define RGB_TO_BGR(c)
Definition: libaribb24.c:205
static void libaribb24_flush(AVCodecContext *avctx)
Definition: libaribb24.c:359
static int libaribb24_generate_ass_header(AVCodecContext *avctx)
Definition: libaribb24.c:61
static const AVOption options[]
Definition: libaribb24.c:368
static unsigned int get_profile_font_size(int profile)
Definition: libaribb24.c:44
static void libaribb24_log(void *p, const char *msg)
Definition: libaribb24.c:56
static int libaribb24_decode(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt)
Definition: libaribb24.c:285
#define SD
Definition: libaribb24.c:367
static int libaribb24_init(AVCodecContext *avctx)
Definition: libaribb24.c:132
AVCodec ff_libaribb24_decoder
Definition: libaribb24.c:383
static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub)
Definition: libaribb24.c:207
#define OFFSET(x)
Definition: libaribb24.c:366
static const AVClass aribb24_class
Definition: libaribb24.c:376
static int libaribb24_close(AVCodecContext *avctx)
Definition: libaribb24.c:192
static const chunk_decoder decoder[8]
Definition: dfa.c:330
#define LIBAVCODEC_VERSION
Definition: version.h:37
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
const char data[16]
Definition: mxf.c:142
AVOptions.
mfxU16 profile
Definition: qsvenc.c:45
typedef void(RENAME(mix_any_func_type))
Describe the class of an AVClass context structure.
Definition: log.h:67
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
main external API structure.
Definition: avcodec.h:536
int subtitle_header_size
Definition: avcodec.h:2017
int flags2
AV_CODEC_FLAG2_*.
Definition: avcodec.h:623
AVRational pkt_timebase
Timebase in which pkt_dts/pts and AVPacket.dts/pts are.
Definition: avcodec.h:2085
int profile
profile
Definition: avcodec.h:1858
uint8_t * subtitle_header
Header containing style information for text subtitles.
Definition: avcodec.h:2016
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition: avcodec.h:659
int flags
AV_CODEC_FLAG_*.
Definition: avcodec.h:616
void * priv_data
Definition: avcodec.h:563
AVCodec.
Definition: codec.h:197
const char * name
Name of the codec implementation.
Definition: codec.h:204
AVOption.
Definition: opt.h:248
This structure stores compressed data.
Definition: packet.h:346
int size
Definition: packet.h:370
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:362
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed.
Definition: packet.h:368
uint8_t * data
Definition: packet.h:369
Rational number (pair of numerator and denominator).
Definition: rational.h:58
int num
Numerator.
Definition: rational.h:59
int den
Denominator.
Definition: rational.h:60
arib_instance_t * lib_instance
Definition: libaribb24.c:34
char * aribb24_base_path
Definition: libaribb24.c:40
arib_parser_t * parser
Definition: libaribb24.c:35
arib_decoder_t * decoder
Definition: libaribb24.c:36
unsigned int aribb24_skip_ruby
Definition: libaribb24.c:41
#define av_free(p)
#define av_log(a,...)
AVPacket * pkt
Definition: movenc.c:59