]> git.sesse.net Git - vlc/blob - modules/codec/scte27.c
upnp: change item b_net and i_type
[vlc] / modules / codec / scte27.c
1 /*****************************************************************************
2  * scte27.c : SCTE-27 subtitles decoder
3  *****************************************************************************
4  * Copyright (C) Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_codec.h>
31 #include <vlc_bits.h>
32
33 #include <assert.h>
34
35 /*****************************************************************************
36  * Module descriptor.
37  *****************************************************************************/
38 static int  Open (vlc_object_t *);
39 static void Close(vlc_object_t *);
40
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 ()
49
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;
58 };
59
60 typedef struct {
61     uint8_t y, u, v;
62     uint8_t alpha;
63 } scte27_color_t;
64
65 static const scte27_color_t scte27_color_transparent = {
66     .y     = 0x00,
67     .u     = 0x80,
68     .v     = 0x80,
69     .alpha = 0x00,
70 };
71
72 static scte27_color_t bs_read_color(bs_t *bs)
73 {
74     scte27_color_t color;
75
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;
81
82     return color;
83 }
84
85 static inline void SetYUVPPixel(picture_t *picture, int x, int y, int value)
86 {
87     picture->p->p_pixels[y * picture->p->i_pitch + x] = value;
88 }
89
90 static subpicture_region_t *DecodeSimpleBitmap(decoder_t *dec,
91                                                const uint8_t *data, int size)
92 {
93     VLC_UNUSED(dec);
94     /* Parse the bitmap and its properties */
95     bs_t bs;
96     bs_init(&bs, data, size);
97
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;
124     }
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);
140     }
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;
153         }
154
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;
165                     }
166                 } else {
167                     run_on_length = 4;
168                 }
169             } else {
170                 run_off_length = 6;
171             }
172         } else {
173             run_on_length = 3;
174             run_off_length = 5;
175         }
176
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;
183         }
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;
190         }
191     }
192
193     /* Render the bitmap into a subpicture_region_t */
194
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;
206     }
207     frame_top_h -= margin_h;
208     frame_top_v -= margin_v;
209     frame_bottom_h += margin_h;
210     frame_bottom_v += margin_v;
211
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;
216
217     enum {
218         COLOR_FRAME,
219         COLOR_CHARACTER,
220         COLOR_OUTLINE,
221         COLOR_SHADOW,
222     };
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
231             },
232             [COLOR_CHARACTER] = {
233                 character_color.y,
234                 character_color.u,
235                 character_color.v,
236                 character_color.alpha
237             },
238             [COLOR_OUTLINE] = {
239                 outline_color.y,
240                 outline_color.u,
241                 outline_color.v,
242                 outline_color.alpha
243             },
244             [COLOR_SHADOW] = {
245                 shadow_color.y,
246                 shadow_color.u,
247                 shadow_color.v,
248                 shadow_color.alpha
249             },
250         },
251     };
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,
261     };
262     subpicture_region_t *r = subpicture_region_New(&fmt);
263     if (!r) {
264         free(bitmap);
265         return NULL;
266     }
267     r->i_x = frame_top_h;
268     r->i_y = frame_top_v;
269
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);
275
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;
285         }
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);
301                         }
302                     }
303                 }
304             }
305         }
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);
315             }
316         }
317     }
318
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);
325         }
326     }
327     free(bitmap);
328     return r;
329 }
330
331 static subpicture_t *DecodeSubtitleMessage(decoder_t *dec,
332                                            const uint8_t *data, int size,
333                                            mtime_t date)
334 {
335     if (size < 12)
336         goto error;
337
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]);
344
345     size -= 12;
346     data += 12;
347
348     if (block_length > size)
349         goto error;
350
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;
359         }
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;
388         }
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;
396
397         return sub;
398     } else {
399         /* Reserved */
400         return NULL;
401     }
402
403 error:
404     msg_Err(dec, "corrupted subtitle_message");
405     return NULL;
406 }
407
408 static subpicture_t *Decode(decoder_t *dec, block_t **block)
409 {
410     decoder_sys_t *sys = dec->p_sys;
411
412     if (block == NULL || *block == NULL)
413         return NULL;
414     block_t *b = *block; *block = NULL;
415
416     subpicture_t *sub_first = NULL;
417     subpicture_t **sub_last = &sub_first;
418
419     if (b->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
420         goto exit;
421
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;
428         }
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;
433         }
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;
438         }
439         const bool segmentation_overlay = b->p_buffer[3] & 0x40;
440
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;
458                 }
459             }
460
461             int segment_size = section_length - 1 - 5 - 4;
462
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;
468
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;
475             }
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);
481         }
482         *sub_last = sub;
483         if (*sub_last)
484             sub_last = &(*sub_last)->p_next;
485
486         b->i_buffer -= 3 + section_length;
487         b->p_buffer += 3 + section_length;
488         break;
489     }
490
491 exit:
492     block_Release(b);
493     return sub_first;
494 }
495
496 static int Open(vlc_object_t *object)
497 {
498     decoder_t *dec = (decoder_t *)object;
499
500     if (dec->fmt_in.i_codec != VLC_CODEC_SCTE_27)
501         return VLC_EGENERIC;
502
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;
509
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;
513
514     return VLC_SUCCESS;
515 }
516
517 static void Close(vlc_object_t *object)
518 {
519     decoder_t *dec = (decoder_t *)object;
520     decoder_sys_t *sys = dec->p_sys;
521
522     free(sys->segment_buffer);
523     free(sys);
524 }
525