1 /*****************************************************************************
2 * scte27.c : SCTE-27 subtitles decoder
3 *****************************************************************************
4 * Copyright (C) Laurent Aimar
6 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <vlc_plugin.h>
29 #include <vlc_codec.h>
34 /*****************************************************************************
36 *****************************************************************************/
37 static int Open (vlc_object_t
*);
38 static void Close(vlc_object_t
*);
41 set_description(N_("SCTE-27 decoder"))
42 set_shortname(N_("SCTE-27"))
43 set_capability( "spu decoder", 51)
44 set_category(CAT_INPUT
)
45 set_subcategory(SUBCAT_INPUT_SCODEC
)
46 set_callbacks(Open
, Close
)
49 /****************************************************************************
51 ****************************************************************************/
56 uint8_t *segment_buffer
;
57 vlc_tick_t segment_date
;
65 static const scte27_color_t scte27_color_transparent
= {
72 static scte27_color_t
bs_read_color(bs_t
*bs
)
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
.v
= bs_read(bs
, 5) << 3;
80 color
.u
= bs_read(bs
, 5) << 3;
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
)
94 /* Parse the bitmap and its properties */
96 bs_init(&bs
, data
, size
);
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
)
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
;
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
)
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) {
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) {
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
= vlc_alloc(bitmap_size
, sizeof(*bitmap
));
148 for (int position
= 0; position
< bitmap_size
;) {
150 for (; position
< bitmap_size
; position
++)
151 bitmap
[position
] = false;
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
,
163 for (; position
< next
; position
++)
164 bitmap
[position
] = false;
177 if (run_on_length
> 0) {
178 int run
= bs_read(&bs
, run_on_length
);
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
);
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). */
200 if (outline_style
== 1) {
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
;
223 video_palette_t palette
= {
232 [COLOR_CHARACTER
] = {
236 character_color
.alpha
252 video_format_t fmt
= {
253 .i_chroma
= VLC_CODEC_YUVP
,
255 .i_visible_width
= frame_h
,
257 .i_visible_height
= frame_v
,
258 .i_sar_num
= 0, /* Use video AR */
260 .p_palette
= &palette
,
262 subpicture_region_t
*r
= subpicture_region_New(&fmt
);
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
],
276 /* Draw the outline/shadow if requested */
277 if (outline_style
== 1) {
279 * XXX simple but slow and of low quality (no anti-aliasing) */
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
])
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
,
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
);
331 static subpicture_t
*DecodeSubtitleMessage(decoder_t
*dec
,
332 const uint8_t *data
, int size
,
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]);
348 if (block_length
> size
)
351 if (subtitle_type
== 1) {
352 subpicture_region_t
*region
= DecodeSimpleBitmap(dec
, data
, block_length
);
355 subpicture_t
*sub
= decoder_NewSubpicture(dec
, NULL
);
357 subpicture_region_Delete(region
);
360 vlc_tick_t frame_duration
;
361 switch (display_standard
) {
363 sub
->i_original_picture_width
= 720;
364 sub
->i_original_picture_height
= 480;
365 frame_duration
= VLC_TICK_FROM_US(33367);
368 sub
->i_original_picture_width
= 720;
369 sub
->i_original_picture_height
= 576;
370 frame_duration
= VLC_TICK_FROM_MS(40);
373 sub
->i_original_picture_width
= 1280;
374 sub
->i_original_picture_height
= 720;
375 frame_duration
= VLC_TICK_FROM_US(16683);
378 sub
->i_original_picture_width
= 1920;
379 sub
->i_original_picture_height
= 1080;
380 frame_duration
= VLC_TICK_FROM_US(16683);
383 msg_Warn(dec
, "Unknown display standard");
384 sub
->i_original_picture_width
= 0;
385 sub
->i_original_picture_height
= 0;
386 frame_duration
= VLC_TICK_FROM_MS(40);
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;
394 sub
->i_stop
= date
+ display_duration
* frame_duration
;
395 sub
->p_region
= region
;
404 msg_Err(dec
, "corrupted subtitle_message");
408 static int Decode(decoder_t
*dec
, block_t
*b
)
410 decoder_sys_t
*sys
= dec
->p_sys
;
412 if (b
== NULL
) /* No Drain */
413 return VLCDEC_SUCCESS
;
415 if (b
->i_flags
& (BLOCK_FLAG_CORRUPTED
))
418 while (b
->i_buffer
> 3) {
419 const int table_id
= b
->p_buffer
[0];
420 if (table_id
!= 0xc6) {
421 //if (table_id != 0xff)
422 // msg_Err(dec, "Invalid SCTE-27 table id (0x%x)", table_id);
425 const int section_length
= ((b
->p_buffer
[1] & 0xf) << 8) | b
->p_buffer
[2];
426 if (section_length
<= 1 + 4 || b
->i_buffer
< 3 + (unsigned)section_length
) {
427 msg_Err(dec
, "Invalid SCTE-27 section length");
430 const int protocol_version
= b
->p_buffer
[3] & 0x3f;
431 if (protocol_version
!= 0) {
432 msg_Err(dec
, "Unsupported SCTE-27 protocol version (%d)", protocol_version
);
435 const bool segmentation_overlay
= b
->p_buffer
[3] & 0x40;
437 subpicture_t
*sub
= NULL
;
438 if (segmentation_overlay
) {
439 if (section_length
< 1 + 5 + 4)
441 int id
= GetWBE(&b
->p_buffer
[4]);
442 int last
= (b
->p_buffer
[6] << 4) | (b
->p_buffer
[7] >> 4);
443 int index
= ((b
->p_buffer
[7] & 0x0f) << 8) | b
->p_buffer
[8];
447 sys
->segment_id
= id
;
448 sys
->segment_size
= 0;
449 sys
->segment_date
= b
->i_pts
!= VLC_TICK_INVALID
? b
->i_pts
: b
->i_dts
;
451 if (sys
->segment_id
!= id
|| sys
->segment_size
<= 0) {
452 sys
->segment_id
= -1;
457 int segment_size
= section_length
- 1 - 5 - 4;
459 sys
->segment_buffer
= xrealloc(sys
->segment_buffer
,
460 sys
->segment_size
+ segment_size
);
461 memcpy(&sys
->segment_buffer
[sys
->segment_size
],
462 &b
->p_buffer
[9], segment_size
);
463 sys
->segment_size
+= segment_size
;
466 sub
= DecodeSubtitleMessage(dec
,
470 sys
->segment_size
= 0;
473 sub
= DecodeSubtitleMessage(dec
,
475 section_length
- 1 - 4,
476 b
->i_pts
!= VLC_TICK_INVALID
? b
->i_pts
: b
->i_dts
);
479 decoder_QueueSub(dec
, sub
);
481 b
->i_buffer
-= 3 + section_length
;
482 b
->p_buffer
+= 3 + section_length
;
488 return VLCDEC_SUCCESS
;
491 static int Open(vlc_object_t
*object
)
493 decoder_t
*dec
= (decoder_t
*)object
;
495 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_SCTE_27
)
498 decoder_sys_t
*sys
= dec
->p_sys
= malloc(sizeof(*sys
));
501 sys
->segment_id
= -1;
502 sys
->segment_size
= 0;
503 sys
->segment_buffer
= NULL
;
505 dec
->pf_decode
= Decode
;
506 dec
->fmt_out
.i_codec
= VLC_CODEC_YUVP
;
511 static void Close(vlc_object_t
*object
)
513 decoder_t
*dec
= (decoder_t
*)object
;
514 decoder_sys_t
*sys
= dec
->p_sys
;
516 free(sys
->segment_buffer
);