codec: ass: Don't force fonts that aren't shipped anymore in the winstore app
[vlc.git] / modules / codec / scte27.c
blob869671040131c0a5a734199e49f58ca92c698a4c
1 /*****************************************************************************
2 * scte27.c : SCTE-27 subtitles decoder
3 *****************************************************************************
4 * Copyright (C) Laurent Aimar
5 * $Id$
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_codec.h>
31 #include <vlc_bits.h>
33 #include <assert.h>
35 /*****************************************************************************
36 * Module descriptor.
37 *****************************************************************************/
38 static int Open (vlc_object_t *);
39 static void Close(vlc_object_t *);
41 vlc_module_begin ()
42 set_description(N_("SCTE-27 decoder"))
43 set_shortname(N_("SCTE-27"))
44 set_capability( "decoder", 51)
45 set_category(CAT_INPUT)
46 set_subcategory(SUBCAT_INPUT_SCODEC)
47 set_callbacks(Open, Close)
48 vlc_module_end ()
50 /****************************************************************************
51 * Local prototypes
52 ****************************************************************************/
53 struct decoder_sys_t {
54 int segment_id;
55 int segment_size;
56 uint8_t *segment_buffer;
57 mtime_t segment_date;
60 typedef struct {
61 uint8_t y, u, v;
62 uint8_t alpha;
63 } scte27_color_t;
65 static const scte27_color_t scte27_color_transparent = {
66 .y = 0x00,
67 .u = 0x80,
68 .v = 0x80,
69 .alpha = 0x00,
72 static scte27_color_t bs_read_color(bs_t *bs)
74 scte27_color_t color;
76 /* XXX it's unclear if a value of 0 in Y/U/V means a transparent pixel */
77 color.y = bs_read(bs, 5) << 3;
78 color.alpha = bs_read1(bs) ? 0xff : 0x80;
79 color.u = bs_read(bs, 5) << 3;
80 color.v = bs_read(bs, 5) << 3;
82 return color;
85 static inline void SetYUVPPixel(picture_t *picture, int x, int y, int value)
87 picture->p->p_pixels[y * picture->p->i_pitch + x] = value;
90 static subpicture_region_t *DecodeSimpleBitmap(decoder_t *dec,
91 const uint8_t *data, int size)
93 VLC_UNUSED(dec);
94 /* Parse the bitmap and its properties */
95 bs_t bs;
96 bs_init(&bs, data, size);
98 bs_skip(&bs, 5);
99 int is_framed = bs_read(&bs, 1);
100 int outline_style = bs_read(&bs, 2);
101 scte27_color_t character_color = bs_read_color(&bs);
102 int top_h = bs_read(&bs, 12);
103 int top_v = bs_read(&bs, 12);
104 int bottom_h = bs_read(&bs, 12);
105 int bottom_v = bs_read(&bs, 12);
106 if (top_h >= bottom_h || top_v >= bottom_v)
107 return NULL;
108 int frame_top_h = top_h;
109 int frame_top_v = top_v;
110 int frame_bottom_h = bottom_h;
111 int frame_bottom_v = bottom_v;
112 scte27_color_t frame_color = scte27_color_transparent;
113 if (is_framed) {
114 frame_top_h = bs_read(&bs, 12);
115 frame_top_v = bs_read(&bs, 12);
116 frame_bottom_h = bs_read(&bs, 12);
117 frame_bottom_v = bs_read(&bs, 12);
118 frame_color = bs_read_color(&bs);
119 if (frame_top_h > top_h ||
120 frame_top_v > top_v ||
121 frame_bottom_h < bottom_h ||
122 frame_bottom_v < bottom_v)
123 return NULL;
125 int outline_thickness = 0;
126 scte27_color_t outline_color = scte27_color_transparent;
127 int shadow_right = 0;
128 int shadow_bottom = 0;
129 scte27_color_t shadow_color = scte27_color_transparent;
130 if (outline_style == 1) {
131 bs_skip(&bs, 4);
132 outline_thickness = bs_read(&bs, 4);
133 outline_color = bs_read_color(&bs);
134 } else if (outline_style == 2) {
135 shadow_right = bs_read(&bs, 4);
136 shadow_bottom = bs_read(&bs, 4);
137 shadow_color = bs_read_color(&bs);
138 } else if (outline_style == 3) {
139 bs_skip(&bs, 24);
141 bs_skip(&bs, 16); // bitmap_compressed_length
142 int bitmap_h = bottom_h - top_h;
143 int bitmap_v = bottom_v - top_v;
144 int bitmap_size = bitmap_h * bitmap_v;
145 bool *bitmap = malloc(bitmap_size * sizeof(*bitmap));
146 if (!bitmap)
147 return NULL;
148 for (int position = 0; position < bitmap_size;) {
149 if (bs_eof(&bs)) {
150 for (; position < bitmap_size; position++)
151 bitmap[position] = false;
152 break;
155 int run_on_length = 0;
156 int run_off_length = 0;
157 if (!bs_read1(&bs)) {
158 if (!bs_read1(&bs)) {
159 if (!bs_read1(&bs)) {
160 if (bs_read(&bs, 2) == 1) {
161 int next = __MIN((position / bitmap_h + 1) * bitmap_h,
162 bitmap_size);
163 for (; position < next; position++)
164 bitmap[position] = false;
166 } else {
167 run_on_length = 4;
169 } else {
170 run_off_length = 6;
172 } else {
173 run_on_length = 3;
174 run_off_length = 5;
177 if (run_on_length > 0) {
178 int run = bs_read(&bs, run_on_length);
179 if (!run)
180 run = 1 << run_on_length;
181 for (; position < bitmap_size && run > 0; position++, run--)
182 bitmap[position] = true;
184 if (run_off_length > 0) {
185 int run = bs_read(&bs, run_off_length);
186 if (!run)
187 run = 1 << run_off_length;
188 for (; position < bitmap_size && run > 0; position++, run--)
189 bitmap[position] = false;
193 /* Render the bitmap into a subpicture_region_t */
195 /* Reserve the place for the style
196 * FIXME It's unclear if it is needed or if the bitmap should already include
197 * the needed margin (I think the samples I have do both). */
198 int margin_h = 0;
199 int margin_v = 0;
200 if (outline_style == 1) {
201 margin_h =
202 margin_v = outline_thickness;
203 } else if (outline_style == 2) {
204 margin_h = shadow_right;
205 margin_v = shadow_bottom;
207 frame_top_h -= margin_h;
208 frame_top_v -= margin_v;
209 frame_bottom_h += margin_h;
210 frame_bottom_v += margin_v;
212 const int frame_h = frame_bottom_h - frame_top_h;
213 const int frame_v = frame_bottom_v - frame_top_v;
214 const int bitmap_oh = top_h - frame_top_h;
215 const int bitmap_ov = top_v - frame_top_v;
217 enum {
218 COLOR_FRAME,
219 COLOR_CHARACTER,
220 COLOR_OUTLINE,
221 COLOR_SHADOW,
223 video_palette_t palette = {
224 .i_entries = 4,
225 .palette = {
226 [COLOR_FRAME] = {
227 frame_color.y,
228 frame_color.u,
229 frame_color.v,
230 frame_color.alpha
232 [COLOR_CHARACTER] = {
233 character_color.y,
234 character_color.u,
235 character_color.v,
236 character_color.alpha
238 [COLOR_OUTLINE] = {
239 outline_color.y,
240 outline_color.u,
241 outline_color.v,
242 outline_color.alpha
244 [COLOR_SHADOW] = {
245 shadow_color.y,
246 shadow_color.u,
247 shadow_color.v,
248 shadow_color.alpha
252 video_format_t fmt = {
253 .i_chroma = VLC_CODEC_YUVP,
254 .i_width = frame_h,
255 .i_visible_width = frame_h,
256 .i_height = frame_v,
257 .i_visible_height = frame_v,
258 .i_sar_num = 0, /* Use video AR */
259 .i_sar_den = 1,
260 .p_palette = &palette,
262 subpicture_region_t *r = subpicture_region_New(&fmt);
263 if (!r) {
264 free(bitmap);
265 return NULL;
267 r->i_x = frame_top_h;
268 r->i_y = frame_top_v;
270 /* Fill up with frame (background) color */
271 for (int y = 0; y < frame_v; y++)
272 memset(&r->p_picture->p->p_pixels[y * r->p_picture->p->i_pitch],
273 COLOR_FRAME,
274 frame_h);
276 /* Draw the outline/shadow if requested */
277 if (outline_style == 1) {
278 /* Draw an outline
279 * XXX simple but slow and of low quality (no anti-aliasing) */
280 bool circle[16][16];
281 for (int dy = 0; dy <= 15; dy++) {
282 for (int dx = 0; dx <= 15; dx++)
283 circle[dy][dx] = (dx > 0 || dy > 0) &&
284 dx * dx + dy * dy <= outline_thickness * outline_thickness;
286 for (int by = 0; by < bitmap_v; by++) {
287 for (int bx = 0; bx < bitmap_h; bx++) {
288 if (!bitmap[by * bitmap_h + bx])
289 continue;
290 for (int dy = 0; dy <= outline_thickness; dy++) {
291 for (int dx = 0; dx <= outline_thickness; dx++) {
292 if (circle[dy][dx]) {
293 SetYUVPPixel(r->p_picture,
294 bx + bitmap_oh + dx, by + bitmap_ov + dy, COLOR_OUTLINE);
295 SetYUVPPixel(r->p_picture,
296 bx + bitmap_oh - dx, by + bitmap_ov + dy, COLOR_OUTLINE);
297 SetYUVPPixel(r->p_picture,
298 bx + bitmap_oh + dx, by + bitmap_ov - dy, COLOR_OUTLINE);
299 SetYUVPPixel(r->p_picture,
300 bx + bitmap_oh - dx, by + bitmap_ov - dy, COLOR_OUTLINE);
306 } else if (outline_style == 2) {
307 /* Draw a shadow by drawing the character shifted by shaddow right/bottom */
308 for (int by = 0; by < bitmap_v; by++) {
309 for (int bx = 0; bx < bitmap_h; bx++) {
310 if (bitmap[by * bitmap_h + bx])
311 SetYUVPPixel(r->p_picture,
312 bx + bitmap_oh + shadow_right,
313 by + bitmap_ov + shadow_bottom,
314 COLOR_SHADOW);
319 /* Draw the character */
320 for (int by = 0; by < bitmap_v; by++) {
321 for (int bx = 0; bx < bitmap_h; bx++) {
322 if (bitmap[by * bitmap_h + bx])
323 SetYUVPPixel(r->p_picture,
324 bx + bitmap_oh, by + bitmap_ov, COLOR_CHARACTER);
327 free(bitmap);
328 return r;
331 static subpicture_t *DecodeSubtitleMessage(decoder_t *dec,
332 const uint8_t *data, int size,
333 mtime_t date)
335 if (size < 12)
336 goto error;
338 /* Parse the header */
339 bool pre_clear_display = data[3] & 0x80;
340 int display_standard = data[3] & 0x1f;
341 int subtitle_type = data[8] >> 4;
342 int display_duration = ((data[8] & 0x07) << 8) | data[9];
343 int block_length = GetWBE(&data[10]);
345 size -= 12;
346 data += 12;
348 if (block_length > size)
349 goto error;
351 if (subtitle_type == 1) {
352 subpicture_region_t *region = DecodeSimpleBitmap(dec, data, block_length);
353 if (!region)
354 goto error;
355 subpicture_t *sub = decoder_NewSubpicture(dec, NULL);
356 if (!sub) {
357 subpicture_region_Delete(region);
358 return NULL;
360 int frame_duration;
361 switch (display_standard) {
362 case 0:
363 sub->i_original_picture_width = 720;
364 sub->i_original_picture_height = 480;
365 frame_duration = 33367;
366 break;
367 case 1:
368 sub->i_original_picture_width = 720;
369 sub->i_original_picture_height = 576;
370 frame_duration = 40000;
371 break;
372 case 2:
373 sub->i_original_picture_width = 1280;
374 sub->i_original_picture_height = 720;
375 frame_duration = 16683;
376 break;
377 case 3:
378 sub->i_original_picture_width = 1920;
379 sub->i_original_picture_height = 1080;
380 frame_duration = 16683;
381 break;
382 default:
383 msg_Warn(dec, "Unknown display standard");
384 sub->i_original_picture_width = 0;
385 sub->i_original_picture_height = 0;
386 frame_duration = 40000;
387 break;
389 sub->b_absolute = true;
390 if (!pre_clear_display)
391 msg_Warn(dec, "SCTE-27 subtitles without pre_clear_display flag are not well supported");
392 sub->b_ephemer = true;
393 sub->i_start = date;
394 sub->i_stop = date + display_duration * frame_duration;
395 sub->p_region = region;
397 return sub;
398 } else {
399 /* Reserved */
400 return NULL;
403 error:
404 msg_Err(dec, "corrupted subtitle_message");
405 return NULL;
408 static subpicture_t *Decode(decoder_t *dec, block_t **block)
410 decoder_sys_t *sys = dec->p_sys;
412 if (block == NULL || *block == NULL)
413 return NULL;
414 block_t *b = *block; *block = NULL;
416 subpicture_t *sub_first = NULL;
417 subpicture_t **sub_last = &sub_first;
419 if (b->i_flags & (BLOCK_FLAG_CORRUPTED))
420 goto exit;
422 while (b->i_buffer > 3) {
423 const int table_id = b->p_buffer[0];
424 if (table_id != 0xc6) {
425 //if (table_id != 0xff)
426 // msg_Err(dec, "Invalid SCTE-27 table id (0x%x)", table_id);
427 break;
429 const int section_length = ((b->p_buffer[1] & 0xf) << 8) | b->p_buffer[2];
430 if (section_length <= 1 + 4 || b->i_buffer < 3 + (unsigned)section_length) {
431 msg_Err(dec, "Invalid SCTE-27 section length");
432 break;
434 const int protocol_version = b->p_buffer[3] & 0x3f;
435 if (protocol_version != 0) {
436 msg_Err(dec, "Unsupported SCTE-27 protocol version (%d)", protocol_version);
437 break;
439 const bool segmentation_overlay = b->p_buffer[3] & 0x40;
441 subpicture_t *sub = NULL;
442 if (segmentation_overlay) {
443 if (section_length < 1 + 5 + 4)
444 break;
445 int id = GetWBE(&b->p_buffer[4]);
446 int last = (b->p_buffer[6] << 4) | (b->p_buffer[7] >> 4);
447 int index = ((b->p_buffer[7] & 0x0f) << 8) | b->p_buffer[8];
448 if (index > last)
449 break;
450 if (index == 0) {
451 sys->segment_id = id;
452 sys->segment_size = 0;
453 sys->segment_date = b->i_pts > VLC_TS_INVALID ? b->i_pts : b->i_dts;
454 } else {
455 if (sys->segment_id != id || sys->segment_size <= 0) {
456 sys->segment_id = -1;
457 break;
461 int segment_size = section_length - 1 - 5 - 4;
463 sys->segment_buffer = xrealloc(sys->segment_buffer,
464 sys->segment_size + segment_size);
465 memcpy(&sys->segment_buffer[sys->segment_size],
466 &b->p_buffer[9], segment_size);
467 sys->segment_size += segment_size;
469 if (index == last) {
470 sub = DecodeSubtitleMessage(dec,
471 sys->segment_buffer,
472 sys->segment_size,
473 sys->segment_date);
474 sys->segment_size = 0;
476 } else {
477 sub = DecodeSubtitleMessage(dec,
478 &b->p_buffer[4],
479 section_length - 1 - 4,
480 b->i_pts > VLC_TS_INVALID ? b->i_pts : b->i_dts);
482 *sub_last = sub;
483 if (*sub_last)
484 sub_last = &(*sub_last)->p_next;
486 b->i_buffer -= 3 + section_length;
487 b->p_buffer += 3 + section_length;
488 break;
491 exit:
492 block_Release(b);
493 return sub_first;
496 static int Open(vlc_object_t *object)
498 decoder_t *dec = (decoder_t *)object;
500 if (dec->fmt_in.i_codec != VLC_CODEC_SCTE_27)
501 return VLC_EGENERIC;
503 decoder_sys_t *sys = dec->p_sys = malloc(sizeof(*sys));
504 if (!sys)
505 return VLC_ENOMEM;
506 sys->segment_id = -1;
507 sys->segment_size = 0;
508 sys->segment_buffer = NULL;
510 dec->pf_decode_sub = Decode;
511 es_format_Init(&dec->fmt_out, SPU_ES, VLC_CODEC_SPU);
512 dec->fmt_out.video.i_chroma = VLC_CODEC_YUVP;
514 return VLC_SUCCESS;
517 static void Close(vlc_object_t *object)
519 decoder_t *dec = (decoder_t *)object;
520 decoder_sys_t *sys = dec->p_sys;
522 free(sys->segment_buffer);
523 free(sys);