1 /*****************************************************************************
2 * scte27.c : SCTE-27 subtitles decoder
3 *****************************************************************************
4 * Copyright (C) Laurent Aimar
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 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_codec.h>
35 /*****************************************************************************
37 *****************************************************************************/
38 static int Open (vlc_object_t
*);
39 static void Close(vlc_object_t
*);
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
)
50 /****************************************************************************
52 ****************************************************************************/
57 uint8_t *segment_buffer
;
58 vlc_tick_t segment_date
;
66 static const scte27_color_t scte27_color_transparent
= {
73 static scte27_color_t
bs_read_color(bs_t
*bs
)
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;
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
)
95 /* Parse the bitmap and its properties */
97 bs_init(&bs
, data
, size
);
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
)
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
;
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
)
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) {
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) {
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
));
149 for (int position
= 0; position
< bitmap_size
;) {
151 for (; position
< bitmap_size
; position
++)
152 bitmap
[position
] = false;
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
,
164 for (; position
< next
; position
++)
165 bitmap
[position
] = false;
178 if (run_on_length
> 0) {
179 int run
= bs_read(&bs
, run_on_length
);
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
);
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). */
201 if (outline_style
== 1) {
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
;
224 video_palette_t palette
= {
233 [COLOR_CHARACTER
] = {
237 character_color
.alpha
253 video_format_t fmt
= {
254 .i_chroma
= VLC_CODEC_YUVP
,
256 .i_visible_width
= frame_h
,
258 .i_visible_height
= frame_v
,
259 .i_sar_num
= 0, /* Use video AR */
261 .p_palette
= &palette
,
263 subpicture_region_t
*r
= subpicture_region_New(&fmt
);
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
],
277 /* Draw the outline/shadow if requested */
278 if (outline_style
== 1) {
280 * XXX simple but slow and of low quality (no anti-aliasing) */
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
])
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
,
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
);
332 static subpicture_t
*DecodeSubtitleMessage(decoder_t
*dec
,
333 const uint8_t *data
, int size
,
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]);
349 if (block_length
> size
)
352 if (subtitle_type
== 1) {
353 subpicture_region_t
*region
= DecodeSimpleBitmap(dec
, data
, block_length
);
356 subpicture_t
*sub
= decoder_NewSubpicture(dec
, NULL
);
358 subpicture_region_Delete(region
);
361 vlc_tick_t frame_duration
;
362 switch (display_standard
) {
364 sub
->i_original_picture_width
= 720;
365 sub
->i_original_picture_height
= 480;
366 frame_duration
= VLC_TICK_FROM_US(33367);
369 sub
->i_original_picture_width
= 720;
370 sub
->i_original_picture_height
= 576;
371 frame_duration
= VLC_TICK_FROM_MS(40);
374 sub
->i_original_picture_width
= 1280;
375 sub
->i_original_picture_height
= 720;
376 frame_duration
= VLC_TICK_FROM_US(16683);
379 sub
->i_original_picture_width
= 1920;
380 sub
->i_original_picture_height
= 1080;
381 frame_duration
= VLC_TICK_FROM_US(16683);
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);
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;
395 sub
->i_stop
= date
+ display_duration
* frame_duration
;
396 sub
->p_region
= region
;
405 msg_Err(dec
, "corrupted subtitle_message");
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
))
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);
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");
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
);
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)
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];
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
;
452 if (sys
->segment_id
!= id
|| sys
->segment_size
<= 0) {
453 sys
->segment_id
= -1;
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
;
467 sub
= DecodeSubtitleMessage(dec
,
471 sys
->segment_size
= 0;
474 sub
= DecodeSubtitleMessage(dec
,
476 section_length
- 1 - 4,
477 b
->i_pts
!= VLC_TICK_INVALID
? b
->i_pts
: b
->i_dts
);
480 decoder_QueueSub(dec
, sub
);
482 b
->i_buffer
-= 3 + section_length
;
483 b
->p_buffer
+= 3 + section_length
;
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
)
499 decoder_sys_t
*sys
= dec
->p_sys
= malloc(sizeof(*sys
));
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
;
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
);