]> git.sesse.net Git - vlc/blob - modules/codec/scte27.c
avcodec: simplify picture references
[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_height = frame_v,
256         .i_sar_num = 0, /* Use video AR */
257         .i_sar_den = 1,
258         .p_palette = &palette,
259     };
260     subpicture_region_t *r = subpicture_region_New(&fmt);
261     if (!r) {
262         free(bitmap);
263         return NULL;
264     }
265     r->i_x = frame_top_h;
266     r->i_y = frame_top_v;
267
268     /* Fill up with frame (background) color */
269     for (int y = 0; y < frame_v; y++)
270         memset(&r->p_picture->p->p_pixels[y * r->p_picture->p->i_pitch],
271                COLOR_FRAME,
272                frame_h);
273
274     /* Draw the outline/shadow if requested */
275     if (outline_style == 1) {
276         /* Draw an outline
277          * XXX simple but slow and of low quality (no anti-aliasing) */
278         bool circle[16][16];
279         for (int dy = 0; dy <= 15; dy++) {
280             for (int dx = 0; dx <= 15; dx++)
281                 circle[dy][dx] = (dx > 0 || dy > 0) &&
282                                  dx * dx + dy * dy <= outline_thickness * outline_thickness;
283         }
284         for (int by = 0; by < bitmap_v; by++) {
285             for (int bx = 0; bx < bitmap_h; bx++) {
286                 if (!bitmap[by * bitmap_h + bx])
287                     continue;
288                 for (int dy = 0; dy <= outline_thickness; dy++) {
289                     for (int dx = 0; dx <= outline_thickness; dx++) {
290                         if (circle[dy][dx]) {
291                             SetYUVPPixel(r->p_picture,
292                                          bx + bitmap_oh + dx, by + bitmap_ov + dy, COLOR_OUTLINE);
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                         }
300                     }
301                 }
302             }
303         }
304     } else if (outline_style == 2) {
305         /* Draw a shadow by drawing the character shifted by shaddow right/bottom */
306         for (int by = 0; by < bitmap_v; by++) {
307             for (int bx = 0; bx < bitmap_h; bx++) {
308                 if (bitmap[by * bitmap_h + bx])
309                     SetYUVPPixel(r->p_picture,
310                                  bx + bitmap_oh + shadow_right,
311                                  by + bitmap_ov + shadow_bottom,
312                                  COLOR_SHADOW);
313             }
314         }
315     }
316
317     /* Draw the character */
318     for (int by = 0; by < bitmap_v; by++) {
319         for (int bx = 0; bx < bitmap_h; bx++) {
320             if (bitmap[by * bitmap_h + bx])
321                 SetYUVPPixel(r->p_picture,
322                              bx + bitmap_oh, by + bitmap_ov, COLOR_CHARACTER);
323         }
324     }
325     free(bitmap);
326     return r;
327 }
328
329 static subpicture_t *DecodeSubtitleMessage(decoder_t *dec,
330                                            const uint8_t *data, int size,
331                                            mtime_t date)
332 {
333     if (size < 12)
334         goto error;
335
336     /* Parse the header */
337     bool pre_clear_display = data[3] & 0x80;
338     int  display_standard = data[3] & 0x1f;
339     int subtitle_type = data[8] >> 4;
340     int display_duration = ((data[8] & 0x07) << 8) | data[9];
341     int block_length = GetWBE(&data[10]);
342
343     size -= 12;
344     data += 12;
345
346     if (block_length > size)
347         goto error;
348
349     if (subtitle_type == 1) {
350         subpicture_region_t *region = DecodeSimpleBitmap(dec, data, block_length);
351         if (!region)
352             goto error;
353         subpicture_t *sub = decoder_NewSubpicture(dec, NULL);
354         if (!sub) {
355             subpicture_region_Delete(region);
356             return NULL;
357         }
358         int frame_duration;
359         switch (display_standard) {
360         case 0:
361             sub->i_original_picture_width  = 720;
362             sub->i_original_picture_height = 480;
363             frame_duration = 33367;
364             break;
365         case 1:
366             sub->i_original_picture_width  = 720;
367             sub->i_original_picture_height = 576;
368             frame_duration = 40000;
369             break;
370         case 2:
371             sub->i_original_picture_width  = 1280;
372             sub->i_original_picture_height =  720;
373             frame_duration = 16683;
374             break;
375         case 3:
376             sub->i_original_picture_width  = 1920;
377             sub->i_original_picture_height = 1080;
378             frame_duration = 16683;
379             break;
380         default:
381             msg_Warn(dec, "Unknown display standard");
382             sub->i_original_picture_width  = 0;
383             sub->i_original_picture_height = 0;
384             frame_duration = 40000;
385             break;
386         }
387         sub->b_absolute = true;
388         if (!pre_clear_display)
389             msg_Warn(dec, "SCTE-27 subtitles without pre_clear_display flag are not well supported");
390         sub->b_ephemer = true;
391         sub->i_start = date;
392         sub->i_stop = date + display_duration * frame_duration;
393         sub->p_region = region;
394
395         return sub;
396     } else {
397         /* Reserved */
398         return NULL;
399     }
400
401 error:
402     msg_Err(dec, "corrupted subtitle_message");
403     return NULL;
404 }
405
406 static subpicture_t *Decode(decoder_t *dec, block_t **block)
407 {
408     decoder_sys_t *sys = dec->p_sys;
409
410     if (block == NULL || *block == NULL)
411         return NULL;
412     block_t *b = *block; *block = NULL;
413
414     subpicture_t *sub_first = NULL;
415     subpicture_t **sub_last = &sub_first;
416
417     if (b->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
418         goto exit;
419
420     while (b->i_buffer > 3) {
421         const int table_id =  b->p_buffer[0];
422         if (table_id != 0xc6) {
423             //if (table_id != 0xff)
424             //    msg_Err(dec, "Invalid SCTE-27 table id (0x%x)", table_id);
425             break;
426         }
427         const int section_length = ((b->p_buffer[1] & 0xf) << 8) | b->p_buffer[2];
428         if (section_length <= 1 + 4 || b->i_buffer < 3 + (unsigned)section_length) {
429             msg_Err(dec, "Invalid SCTE-27 section length");
430             break;
431         }
432         const int protocol_version = b->p_buffer[3] & 0x3f;
433         if (protocol_version != 0) {
434             msg_Err(dec, "Unsupported SCTE-27 protocol version (%d)", protocol_version);
435             break;
436         }
437         const bool segmentation_overlay = b->p_buffer[3] & 0x40;
438
439         subpicture_t *sub = NULL;
440         if (segmentation_overlay) {
441             if (section_length < 1 + 5 + 4)
442                 break;
443             int id = GetWBE(&b->p_buffer[4]);
444             int last = (b->p_buffer[6] << 4) | (b->p_buffer[7] >> 4);
445             int index = ((b->p_buffer[7] & 0x0f) << 8) | b->p_buffer[8];
446             if (index > last)
447                 break;
448             if (index == 0) {
449                 sys->segment_id = id;
450                 sys->segment_size = 0;
451                 sys->segment_date = b->i_pts > VLC_TS_INVALID ? b->i_pts : b->i_dts;
452             } else {
453                 if (sys->segment_id != id || sys->segment_size <= 0) {
454                     sys->segment_id = -1;
455                     break;
456                 }
457             }
458
459             int segment_size = section_length - 1 - 5 - 4;
460
461             sys->segment_buffer = xrealloc(sys->segment_buffer,
462                                            sys->segment_size + segment_size);
463             memcpy(&sys->segment_buffer[sys->segment_size],
464                    &b->p_buffer[9], segment_size);
465             sys->segment_size += segment_size;
466
467             if (index == last) {
468                 sub = DecodeSubtitleMessage(dec,
469                                             sys->segment_buffer,
470                                             sys->segment_size,
471                                             sys->segment_date);
472                 sys->segment_size = 0;
473             }
474         } else {
475             sub = DecodeSubtitleMessage(dec,
476                                         &b->p_buffer[4],
477                                         section_length - 1 - 4,
478                                         b->i_pts > VLC_TS_INVALID ? b->i_pts : b->i_dts);
479         }
480         *sub_last = sub;
481         if (*sub_last)
482             sub_last = &(*sub_last)->p_next;
483
484         b->i_buffer -= 3 + section_length;
485         b->p_buffer += 3 + section_length;
486         break;
487     }
488
489 exit:
490     block_Release(b);
491     return sub_first;
492 }
493
494 static int Open(vlc_object_t *object)
495 {
496     decoder_t *dec = (decoder_t *)object;
497
498     if (dec->fmt_in.i_codec != VLC_CODEC_SCTE_27)
499         return VLC_EGENERIC;
500
501     decoder_sys_t *sys = dec->p_sys = malloc(sizeof(*sys));
502     if (!sys)
503         return VLC_ENOMEM;
504     sys->segment_id = -1;
505     sys->segment_size = 0;
506     sys->segment_buffer = NULL;
507
508     dec->pf_decode_sub = Decode;
509     es_format_Init(&dec->fmt_out, SPU_ES, VLC_CODEC_SPU);
510     dec->fmt_out.video.i_chroma = VLC_CODEC_YUVP;
511
512     return VLC_SUCCESS;
513 }
514
515 static void Close(vlc_object_t *object)
516 {
517     decoder_t *dec = (decoder_t *)object;
518     decoder_sys_t *sys = dec->p_sys;
519
520     free(sys->segment_buffer);
521     free(sys);
522 }
523