demux: heif: send extradata with avif
[vlc.git] / modules / codec / scte27.c
blobe8385faf5145c3a0e55325427d1c1124e7094798
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( "spu 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 typedef struct
55 int segment_id;
56 int segment_size;
57 uint8_t *segment_buffer;
58 vlc_tick_t segment_date;
59 } decoder_sys_t;
61 typedef struct {
62 uint8_t y, u, v;
63 uint8_t alpha;
64 } scte27_color_t;
66 static const scte27_color_t scte27_color_transparent = {
67 .y = 0x00,
68 .u = 0x80,
69 .v = 0x80,
70 .alpha = 0x00,
73 static scte27_color_t bs_read_color(bs_t *bs)
75 scte27_color_t color;
77 /* XXX it's unclear if a value of 0 in Y/U/V means a transparent pixel */
78 color.y = bs_read(bs, 5) << 3;
79 color.alpha = bs_read1(bs) ? 0xff : 0x80;
80 color.v = bs_read(bs, 5) << 3;
81 color.u = bs_read(bs, 5) << 3;
83 return color;
86 static inline void SetYUVPPixel(picture_t *picture, int x, int y, int value)
88 picture->p->p_pixels[y * picture->p->i_pitch + x] = value;
91 static subpicture_region_t *DecodeSimpleBitmap(decoder_t *dec,
92 const uint8_t *data, int size)
94 VLC_UNUSED(dec);
95 /* Parse the bitmap and its properties */
96 bs_t bs;
97 bs_init(&bs, data, size);
99 bs_skip(&bs, 5);
100 int is_framed = bs_read(&bs, 1);
101 int outline_style = bs_read(&bs, 2);
102 scte27_color_t character_color = bs_read_color(&bs);
103 int top_h = bs_read(&bs, 12);
104 int top_v = bs_read(&bs, 12);
105 int bottom_h = bs_read(&bs, 12);
106 int bottom_v = bs_read(&bs, 12);
107 if (top_h >= bottom_h || top_v >= bottom_v)
108 return NULL;
109 int frame_top_h = top_h;
110 int frame_top_v = top_v;
111 int frame_bottom_h = bottom_h;
112 int frame_bottom_v = bottom_v;
113 scte27_color_t frame_color = scte27_color_transparent;
114 if (is_framed) {
115 frame_top_h = bs_read(&bs, 12);
116 frame_top_v = bs_read(&bs, 12);
117 frame_bottom_h = bs_read(&bs, 12);
118 frame_bottom_v = bs_read(&bs, 12);
119 frame_color = bs_read_color(&bs);
120 if (frame_top_h > top_h ||
121 frame_top_v > top_v ||
122 frame_bottom_h < bottom_h ||
123 frame_bottom_v < bottom_v)
124 return NULL;
126 int outline_thickness = 0;
127 scte27_color_t outline_color = scte27_color_transparent;
128 int shadow_right = 0;
129 int shadow_bottom = 0;
130 scte27_color_t shadow_color = scte27_color_transparent;
131 if (outline_style == 1) {
132 bs_skip(&bs, 4);
133 outline_thickness = bs_read(&bs, 4);
134 outline_color = bs_read_color(&bs);
135 } else if (outline_style == 2) {
136 shadow_right = bs_read(&bs, 4);
137 shadow_bottom = bs_read(&bs, 4);
138 shadow_color = bs_read_color(&bs);
139 } else if (outline_style == 3) {
140 bs_skip(&bs, 24);
142 bs_skip(&bs, 16); // bitmap_compressed_length
143 int bitmap_h = bottom_h - top_h;
144 int bitmap_v = bottom_v - top_v;
145 int bitmap_size = bitmap_h * bitmap_v;
146 bool *bitmap = vlc_alloc(bitmap_size, sizeof(*bitmap));
147 if (!bitmap)
148 return NULL;
149 for (int position = 0; position < bitmap_size;) {
150 if (bs_eof(&bs)) {
151 for (; position < bitmap_size; position++)
152 bitmap[position] = false;
153 break;
156 int run_on_length = 0;
157 int run_off_length = 0;
158 if (!bs_read1(&bs)) {
159 if (!bs_read1(&bs)) {
160 if (!bs_read1(&bs)) {
161 if (bs_read(&bs, 2) == 1) {
162 int next = __MIN((position / bitmap_h + 1) * bitmap_h,
163 bitmap_size);
164 for (; position < next; position++)
165 bitmap[position] = false;
167 } else {
168 run_on_length = 4;
170 } else {
171 run_off_length = 6;
173 } else {
174 run_on_length = 3;
175 run_off_length = 5;
178 if (run_on_length > 0) {
179 int run = bs_read(&bs, run_on_length);
180 if (!run)
181 run = 1 << run_on_length;
182 for (; position < bitmap_size && run > 0; position++, run--)
183 bitmap[position] = true;
185 if (run_off_length > 0) {
186 int run = bs_read(&bs, run_off_length);
187 if (!run)
188 run = 1 << run_off_length;
189 for (; position < bitmap_size && run > 0; position++, run--)
190 bitmap[position] = false;
194 /* Render the bitmap into a subpicture_region_t */
196 /* Reserve the place for the style
197 * FIXME It's unclear if it is needed or if the bitmap should already include
198 * the needed margin (I think the samples I have do both). */
199 int margin_h = 0;
200 int margin_v = 0;
201 if (outline_style == 1) {
202 margin_h =
203 margin_v = outline_thickness;
204 } else if (outline_style == 2) {
205 margin_h = shadow_right;
206 margin_v = shadow_bottom;
208 frame_top_h -= margin_h;
209 frame_top_v -= margin_v;
210 frame_bottom_h += margin_h;
211 frame_bottom_v += margin_v;
213 const int frame_h = frame_bottom_h - frame_top_h;
214 const int frame_v = frame_bottom_v - frame_top_v;
215 const int bitmap_oh = top_h - frame_top_h;
216 const int bitmap_ov = top_v - frame_top_v;
218 enum {
219 COLOR_FRAME,
220 COLOR_CHARACTER,
221 COLOR_OUTLINE,
222 COLOR_SHADOW,
224 video_palette_t palette = {
225 .i_entries = 4,
226 .palette = {
227 [COLOR_FRAME] = {
228 frame_color.y,
229 frame_color.u,
230 frame_color.v,
231 frame_color.alpha
233 [COLOR_CHARACTER] = {
234 character_color.y,
235 character_color.u,
236 character_color.v,
237 character_color.alpha
239 [COLOR_OUTLINE] = {
240 outline_color.y,
241 outline_color.u,
242 outline_color.v,
243 outline_color.alpha
245 [COLOR_SHADOW] = {
246 shadow_color.y,
247 shadow_color.u,
248 shadow_color.v,
249 shadow_color.alpha
253 video_format_t fmt = {
254 .i_chroma = VLC_CODEC_YUVP,
255 .i_width = frame_h,
256 .i_visible_width = frame_h,
257 .i_height = frame_v,
258 .i_visible_height = frame_v,
259 .i_sar_num = 0, /* Use video AR */
260 .i_sar_den = 1,
261 .p_palette = &palette,
263 subpicture_region_t *r = subpicture_region_New(&fmt);
264 if (!r) {
265 free(bitmap);
266 return NULL;
268 r->i_x = frame_top_h;
269 r->i_y = frame_top_v;
271 /* Fill up with frame (background) color */
272 for (int y = 0; y < frame_v; y++)
273 memset(&r->p_picture->p->p_pixels[y * r->p_picture->p->i_pitch],
274 COLOR_FRAME,
275 frame_h);
277 /* Draw the outline/shadow if requested */
278 if (outline_style == 1) {
279 /* Draw an outline
280 * XXX simple but slow and of low quality (no anti-aliasing) */
281 bool circle[16][16];
282 for (int dy = 0; dy <= 15; dy++) {
283 for (int dx = 0; dx <= 15; dx++)
284 circle[dy][dx] = (dx > 0 || dy > 0) &&
285 dx * dx + dy * dy <= outline_thickness * outline_thickness;
287 for (int by = 0; by < bitmap_v; by++) {
288 for (int bx = 0; bx < bitmap_h; bx++) {
289 if (!bitmap[by * bitmap_h + bx])
290 continue;
291 for (int dy = 0; dy <= outline_thickness; dy++) {
292 for (int dx = 0; dx <= outline_thickness; dx++) {
293 if (circle[dy][dx]) {
294 SetYUVPPixel(r->p_picture,
295 bx + bitmap_oh + dx, by + bitmap_ov + dy, COLOR_OUTLINE);
296 SetYUVPPixel(r->p_picture,
297 bx + bitmap_oh - dx, by + bitmap_ov + dy, COLOR_OUTLINE);
298 SetYUVPPixel(r->p_picture,
299 bx + bitmap_oh + dx, by + bitmap_ov - dy, COLOR_OUTLINE);
300 SetYUVPPixel(r->p_picture,
301 bx + bitmap_oh - dx, by + bitmap_ov - dy, COLOR_OUTLINE);
307 } else if (outline_style == 2) {
308 /* Draw a shadow by drawing the character shifted by shaddow right/bottom */
309 for (int by = 0; by < bitmap_v; by++) {
310 for (int bx = 0; bx < bitmap_h; bx++) {
311 if (bitmap[by * bitmap_h + bx])
312 SetYUVPPixel(r->p_picture,
313 bx + bitmap_oh + shadow_right,
314 by + bitmap_ov + shadow_bottom,
315 COLOR_SHADOW);
320 /* Draw the character */
321 for (int by = 0; by < bitmap_v; by++) {
322 for (int bx = 0; bx < bitmap_h; bx++) {
323 if (bitmap[by * bitmap_h + bx])
324 SetYUVPPixel(r->p_picture,
325 bx + bitmap_oh, by + bitmap_ov, COLOR_CHARACTER);
328 free(bitmap);
329 return r;
332 static subpicture_t *DecodeSubtitleMessage(decoder_t *dec,
333 const uint8_t *data, int size,
334 vlc_tick_t date)
336 if (size < 12)
337 goto error;
339 /* Parse the header */
340 bool pre_clear_display = data[3] & 0x80;
341 int display_standard = data[3] & 0x1f;
342 int subtitle_type = data[8] >> 4;
343 int display_duration = ((data[8] & 0x07) << 8) | data[9];
344 int block_length = GetWBE(&data[10]);
346 size -= 12;
347 data += 12;
349 if (block_length > size)
350 goto error;
352 if (subtitle_type == 1) {
353 subpicture_region_t *region = DecodeSimpleBitmap(dec, data, block_length);
354 if (!region)
355 goto error;
356 subpicture_t *sub = decoder_NewSubpicture(dec, NULL);
357 if (!sub) {
358 subpicture_region_Delete(region);
359 return NULL;
361 vlc_tick_t frame_duration;
362 switch (display_standard) {
363 case 0:
364 sub->i_original_picture_width = 720;
365 sub->i_original_picture_height = 480;
366 frame_duration = VLC_TICK_FROM_US(33367);
367 break;
368 case 1:
369 sub->i_original_picture_width = 720;
370 sub->i_original_picture_height = 576;
371 frame_duration = VLC_TICK_FROM_MS(40);
372 break;
373 case 2:
374 sub->i_original_picture_width = 1280;
375 sub->i_original_picture_height = 720;
376 frame_duration = VLC_TICK_FROM_US(16683);
377 break;
378 case 3:
379 sub->i_original_picture_width = 1920;
380 sub->i_original_picture_height = 1080;
381 frame_duration = VLC_TICK_FROM_US(16683);
382 break;
383 default:
384 msg_Warn(dec, "Unknown display standard");
385 sub->i_original_picture_width = 0;
386 sub->i_original_picture_height = 0;
387 frame_duration = VLC_TICK_FROM_MS(40);
388 break;
390 sub->b_absolute = true;
391 if (!pre_clear_display)
392 msg_Warn(dec, "SCTE-27 subtitles without pre_clear_display flag are not well supported");
393 sub->b_ephemer = true;
394 sub->i_start = date;
395 sub->i_stop = date + display_duration * frame_duration;
396 sub->p_region = region;
398 return sub;
399 } else {
400 /* Reserved */
401 return NULL;
404 error:
405 msg_Err(dec, "corrupted subtitle_message");
406 return NULL;
409 static int Decode(decoder_t *dec, block_t *b)
411 decoder_sys_t *sys = dec->p_sys;
413 if (b == NULL ) /* No Drain */
414 return VLCDEC_SUCCESS;
416 if (b->i_flags & (BLOCK_FLAG_CORRUPTED))
417 goto exit;
419 while (b->i_buffer > 3) {
420 const int table_id = b->p_buffer[0];
421 if (table_id != 0xc6) {
422 //if (table_id != 0xff)
423 // msg_Err(dec, "Invalid SCTE-27 table id (0x%x)", table_id);
424 break;
426 const int section_length = ((b->p_buffer[1] & 0xf) << 8) | b->p_buffer[2];
427 if (section_length <= 1 + 4 || b->i_buffer < 3 + (unsigned)section_length) {
428 msg_Err(dec, "Invalid SCTE-27 section length");
429 break;
431 const int protocol_version = b->p_buffer[3] & 0x3f;
432 if (protocol_version != 0) {
433 msg_Err(dec, "Unsupported SCTE-27 protocol version (%d)", protocol_version);
434 break;
436 const bool segmentation_overlay = b->p_buffer[3] & 0x40;
438 subpicture_t *sub = NULL;
439 if (segmentation_overlay) {
440 if (section_length < 1 + 5 + 4)
441 break;
442 int id = GetWBE(&b->p_buffer[4]);
443 int last = (b->p_buffer[6] << 4) | (b->p_buffer[7] >> 4);
444 int index = ((b->p_buffer[7] & 0x0f) << 8) | b->p_buffer[8];
445 if (index > last)
446 break;
447 if (index == 0) {
448 sys->segment_id = id;
449 sys->segment_size = 0;
450 sys->segment_date = b->i_pts != VLC_TICK_INVALID ? b->i_pts : b->i_dts;
451 } else {
452 if (sys->segment_id != id || sys->segment_size <= 0) {
453 sys->segment_id = -1;
454 break;
458 int segment_size = section_length - 1 - 5 - 4;
460 sys->segment_buffer = xrealloc(sys->segment_buffer,
461 sys->segment_size + segment_size);
462 memcpy(&sys->segment_buffer[sys->segment_size],
463 &b->p_buffer[9], segment_size);
464 sys->segment_size += segment_size;
466 if (index == last) {
467 sub = DecodeSubtitleMessage(dec,
468 sys->segment_buffer,
469 sys->segment_size,
470 sys->segment_date);
471 sys->segment_size = 0;
473 } else {
474 sub = DecodeSubtitleMessage(dec,
475 &b->p_buffer[4],
476 section_length - 1 - 4,
477 b->i_pts != VLC_TICK_INVALID ? b->i_pts : b->i_dts);
479 if (sub != NULL)
480 decoder_QueueSub(dec, sub);
482 b->i_buffer -= 3 + section_length;
483 b->p_buffer += 3 + section_length;
484 break;
487 exit:
488 block_Release(b);
489 return VLCDEC_SUCCESS;
492 static int Open(vlc_object_t *object)
494 decoder_t *dec = (decoder_t *)object;
496 if (dec->fmt_in.i_codec != VLC_CODEC_SCTE_27)
497 return VLC_EGENERIC;
499 decoder_sys_t *sys = dec->p_sys = malloc(sizeof(*sys));
500 if (!sys)
501 return VLC_ENOMEM;
502 sys->segment_id = -1;
503 sys->segment_size = 0;
504 sys->segment_buffer = NULL;
506 dec->pf_decode = Decode;
507 dec->fmt_out.i_codec = VLC_CODEC_YUVP;
509 return VLC_SUCCESS;
512 static void Close(vlc_object_t *object)
514 decoder_t *dec = (decoder_t *)object;
515 decoder_sys_t *sys = dec->p_sys;
517 free(sys->segment_buffer);
518 free(sys);