• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38     int read_order;
39 
40     char        *aribb24_base_path;
41     unsigned int aribb24_skip_ruby;
42 } Libaribb24Context;
43 
get_profile_font_size(int profile)44 static unsigned int get_profile_font_size(int profile)
45 {
46     switch (profile) {
47     case FF_PROFILE_ARIB_PROFILE_A:
48         return 36;
49     case FF_PROFILE_ARIB_PROFILE_C:
50         return 18;
51     default:
52         return 0;
53     }
54 }
55 
libaribb24_log(void * p,const char * msg)56 static void libaribb24_log(void *p, const char *msg)
57 {
58     av_log((AVCodecContext *)p, AV_LOG_INFO, "%s\n", msg);
59 }
60 
libaribb24_generate_ass_header(AVCodecContext * avctx)61 static int libaribb24_generate_ass_header(AVCodecContext *avctx)
62 {
63     unsigned int plane_width = 0;
64     unsigned int plane_height = 0;
65     unsigned int font_size = 0;
66 
67     switch (avctx->profile) {
68     case FF_PROFILE_ARIB_PROFILE_A:
69         plane_width = 960;
70         plane_height = 540;
71         font_size = get_profile_font_size(avctx->profile);
72         break;
73     case FF_PROFILE_ARIB_PROFILE_C:
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 
83     avctx->subtitle_header = av_asprintf(
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",
117              !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "",
118              plane_width, plane_height,
119              ASS_DEFAULT_FONT, font_size, ASS_DEFAULT_COLOR,
120              ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR,
121              -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE,
122              ASS_DEFAULT_BORDERSTYLE, ASS_DEFAULT_ALIGNMENT);
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 
libaribb24_init(AVCodecContext * avctx)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) {
161     case FF_PROFILE_ARIB_PROFILE_A:
162         arib_dec_init = arib_initialize_decoder_a_profile;
163         break;
164     case FF_PROFILE_ARIB_PROFILE_C:
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 
libaribb24_close(AVCodecContext * avctx)192 static int libaribb24_close(AVCodecContext *avctx)
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 
libaribb24_handle_regions(AVCodecContext * avctx,AVSubtitle * sub)207 static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub)
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 
215     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
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 
libaribb24_decode(AVCodecContext * avctx,void * data,int * got_sub_ptr,AVPacket * pkt)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,
331                                          AV_TIME_BASE_Q,
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 
libaribb24_flush(AVCodecContext * avctx)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 
383 AVCodec ff_libaribb24_decoder = {
384     .name      = "libaribb24",
385     .long_name = NULL_IF_CONFIG_SMALL("libaribb24 ARIB STD-B24 caption decoder"),
386     .type      = AVMEDIA_TYPE_SUBTITLE,
387     .id        = AV_CODEC_ID_ARIB_CAPTION,
388     .priv_data_size = sizeof(Libaribb24Context),
389     .init      = libaribb24_init,
390     .close     = libaribb24_close,
391     .decode    = libaribb24_decode,
392     .flush     = libaribb24_flush,
393     .priv_class= &aribb24_class,
394     .wrapper_name = "libaribb24",
395 };
396