FFmpeg
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 };
AVCodec ff_libaribb24_decoder
Definition: libaribb24.c:383
#define NULL
Definition: coverity.c:32
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
#define RGB_TO_BGR(c)
Definition: libaribb24.c:205
AVOption.
Definition: opt.h:246
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:100
static void flush(AVCodecContext *avctx)
#define ASS_DEFAULT_BORDERSTYLE
Definition: ass.h:43
static const AVOption options[]
Definition: libaribb24.c:368
arib_instance_t * lib_instance
Definition: libaribb24.c:34
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
static const AVClass aribb24_class
Definition: libaribb24.c:376
int num
Numerator.
Definition: rational.h:59
int size
Definition: avcodec.h:1478
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
unsigned num_rects
Definition: avcodec.h:3937
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:101
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
static AVPacket pkt
int profile
profile
Definition: avcodec.h:2898
AVCodec.
Definition: avcodec.h:3481
static int libaribb24_init(AVCodecContext *avctx)
Definition: libaribb24.c:132
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile)
Definition: decode_audio.c:42
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
static void libaribb24_flush(AVCodecContext *avctx)
Definition: libaribb24.c:359
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avcodec.h:1688
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
#define ASS_DEFAULT_ALIGNMENT
Definition: ass.h:42
AVOptions.
int subtitle_header_size
Definition: avcodec.h:3051
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
Append data to a print buffer.
Definition: bprint.c:158
#define OFFSET(x)
Definition: libaribb24.c:366
uint8_t * data
Definition: avcodec.h:1477
#define FF_PROFILE_ARIB_PROFILE_C
Definition: avcodec.h:3011
#define AV_CODEC_FLAG2_RO_FLUSH_NOOP
Do not reset ASS ReadOrder field on flush (subtitles decoding)
Definition: avcodec.h:963
#define av_log(a,...)
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
AVRational pkt_timebase
Timebase in which pkt_dts/pts and AVPacket.dts/pts are.
Definition: avcodec.h:3119
#define ASS_DEFAULT_BACK_COLOR
Definition: ass.h:38
#define ASS_DEFAULT_UNDERLINE
Definition: ass.h:41
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define AV_BPRINT_SIZE_UNLIMITED
char * aribb24_base_path
Definition: libaribb24.c:40
#define ASS_DEFAULT_FONT
Definition: ass.h:35
static int libaribb24_generate_ass_header(AVCodecContext *avctx)
Definition: libaribb24.c:61
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
int flags
AV_CODEC_FLAG_*.
Definition: avcodec.h:1645
const char * name
Name of the codec implementation.
Definition: avcodec.h:3488
uint32_t end_display_time
Definition: avcodec.h:3936
int64_t pts
Same as packet pts, in AV_TIME_BASE.
Definition: avcodec.h:3939
char * av_asprintf(const char *fmt,...)
Definition: avstring.c:113
static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub)
Definition: libaribb24.c:207
#define AV_CODEC_FLAG_BITEXACT
Use only bitexact stuff (except (I)DCT).
Definition: avcodec.h:908
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
static unsigned int get_profile_font_size(int profile)
Definition: libaribb24.c:44
typedef void(APIENTRY *FF_PFNGLACTIVETEXTUREPROC)(GLenum texture)
#define SD
Definition: libaribb24.c:367
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:185
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
Libavcodec external API header.
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:260
main external API structure.
Definition: avcodec.h:1565
void * buf
Definition: avisynth_c.h:766
#define AV_STRINGIFY(s)
Definition: macros.h:36
Describe the class of an AVClass context structure.
Definition: log.h:67
arib_parser_t * parser
Definition: libaribb24.c:35
Rational number (pair of numerator and denominator).
Definition: rational.h:58
unsigned int aribb24_skip_ruby
Definition: libaribb24.c:41
static int libaribb24_decode(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt)
Definition: libaribb24.c:285
#define LIBAVCODEC_VERSION
Definition: version.h:37
mfxU16 profile
Definition: qsvenc.c:44
#define ASS_DEFAULT_COLOR
Definition: ass.h:37
static int libaribb24_close(AVCodecContext *avctx)
Definition: libaribb24.c:192
#define FF_PROFILE_ARIB_PROFILE_A
Definition: avcodec.h:3010
int den
Denominator.
Definition: rational.h:60
void * priv_data
Definition: avcodec.h:1592
#define av_free(p)
#define ASS_DEFAULT_ITALIC
Definition: ass.h:40
#define ASS_DEFAULT_BOLD
Definition: ass.h:39
static void libaribb24_log(void *p, const char *msg)
Definition: libaribb24.c:56
int flags2
AV_CODEC_FLAG2_*.
Definition: avcodec.h:1652
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed...
Definition: avcodec.h:1476
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
This structure stores compressed data.
Definition: avcodec.h:1454
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: avcodec.h:1470
arib_decoder_t * decoder
Definition: libaribb24.c:36
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
uint8_t * subtitle_header
Header containing style information for text subtitles.
Definition: avcodec.h:3050