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( "decoder", 51)
45 set_category(CAT_INPUT
)
46 set_subcategory(SUBCAT_INPUT_SCODEC
)
47 set_callbacks(Open
, Close
)
50 /****************************************************************************
52 ****************************************************************************/
53 struct decoder_sys_t
{
56 uint8_t *segment_buffer
;
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
.u
= bs_read(bs
, 5) << 3;
80 color
.v
= 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
= malloc(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
);
361 switch (display_standard
) {
363 sub
->i_original_picture_width
= 720;
364 sub
->i_original_picture_height
= 480;
365 frame_duration
= 33367;
368 sub
->i_original_picture_width
= 720;
369 sub
->i_original_picture_height
= 576;
370 frame_duration
= 40000;
373 sub
->i_original_picture_width
= 1280;
374 sub
->i_original_picture_height
= 720;
375 frame_duration
= 16683;
378 sub
->i_original_picture_width
= 1920;
379 sub
->i_original_picture_height
= 1080;
380 frame_duration
= 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
= 40000;
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 subpicture_t
*Decode(decoder_t
*dec
, block_t
**block
)
410 decoder_sys_t
*sys
= dec
->p_sys
;
412 if (block
== NULL
|| *block
== NULL
)
414 block_t
*b
= *block
; *block
= NULL
;
416 subpicture_t
*sub_first
= NULL
;
417 subpicture_t
**sub_last
= &sub_first
;
419 if (b
->i_flags
& (BLOCK_FLAG_DISCONTINUITY
|BLOCK_FLAG_CORRUPTED
))
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);
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");
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
);
439 const bool segmentation_overlay
= b
->p_buffer
[3] & 0x40;
441 subpicture_t
*sub
= NULL
;
442 if (segmentation_overlay
) {
443 if (section_length
< 1 + 5 + 4)
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];
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
;
455 if (sys
->segment_id
!= id
|| sys
->segment_size
<= 0) {
456 sys
->segment_id
= -1;
461 int segment_size
= section_length
- 1 - 5 - 4;
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
;
470 sub
= DecodeSubtitleMessage(dec
,
474 sys
->segment_size
= 0;
477 sub
= DecodeSubtitleMessage(dec
,
479 section_length
- 1 - 4,
480 b
->i_pts
> VLC_TS_INVALID
? b
->i_pts
: b
->i_dts
);
484 sub_last
= &(*sub_last
)->p_next
;
486 b
->i_buffer
-= 3 + section_length
;
487 b
->p_buffer
+= 3 + section_length
;
496 static int Open(vlc_object_t
*object
)
498 decoder_t
*dec
= (decoder_t
*)object
;
500 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_SCTE_27
)
503 decoder_sys_t
*sys
= dec
->p_sys
= malloc(sizeof(*sys
));
506 sys
->segment_id
= -1;
507 sys
->segment_size
= 0;
508 sys
->segment_buffer
= NULL
;
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
;
517 static void Close(vlc_object_t
*object
)
519 decoder_t
*dec
= (decoder_t
*)object
;
520 decoder_sys_t
*sys
= dec
->p_sys
;
522 free(sys
->segment_buffer
);