1 /*****************************************************************************
2 * stl.c: EBU STL decoder
3 *****************************************************************************
4 * Copyright (C) 2010 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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_codec.h>
35 #include <vlc_memory.h>
36 #include <vlc_charset.h>
38 #include "substext.h" /* required for font scaling / updater */
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Open (vlc_object_t
*);
44 static void Close(vlc_object_t
*);
47 set_description(N_("EBU STL subtitles decoder"))
48 set_category(CAT_INPUT
)
49 set_subcategory(SUBCAT_INPUT_SCODEC
)
50 set_capability("decoder", 10)
51 set_callbacks(Open
, Close
)
54 /*****************************************************************************
55 * Local definitions/prototypes
56 *****************************************************************************/
57 #define GSI_BLOCK_SIZE 1024
59 #define STL_GROUPS_MAX 255
61 #define STL_TEXTFIELD_SIZE 112
62 #define STL_TTI_HEADER_SIZE 16
63 #define STL_TTI_SIZE (STL_TTI_HEADER_SIZE + STL_TEXTFIELD_SIZE)
65 #define STL_TF_TELETEXT_FIRST 0x00
66 #define STL_TF_TELETEXT_LAST 0x1f
67 #define STL_TF_CHARCODE1_FIRST 0x20
68 #define STL_TF_CHARCODE1_LAST 0x7f
69 #define STL_TF_ITALICS_ON 0x80
70 #define STL_TF_ITALICS_OFF 0x81
71 #define STL_TF_UNDERLINE_ON 0x82
72 #define STL_TF_UNDERLINE_OFF 0x83
73 #define STL_TF_BOXING_ON 0x84
74 #define STL_TF_BOXING_OFF 0x85
75 #define STL_TF_LINEBREAK 0x8a
76 #define STL_TF_END_FILL 0x8f
77 #define STL_TF_CHARCODE2_FIRST 0xa1
80 CCT_ISO_6937_2
= 0x3030, CCT_BEGIN
= CCT_ISO_6937_2
,
81 CCT_ISO_8859_5
= 0x3031,
82 CCT_ISO_8859_6
= 0x3032,
83 CCT_ISO_8859_7
= 0x3033,
84 CCT_ISO_8859_8
= 0x3034, CCT_END
= CCT_ISO_8859_8
89 uint8_t i_accumulating
;
93 text_style_t
*p_style
;
94 text_segment_t
*p_segment
;
95 text_segment_t
**pp_segment_last
;
99 cct_number_value_t value
;
103 struct decoder_sys_t
{
104 stl_sg_t groups
[STL_GROUPS_MAX
+ 1];
105 cct_number_value_t cct
;
108 static cct_number_t cct_nums
[] = { {CCT_ISO_6937_2
, "ISO_6937-2"},
109 {CCT_ISO_8859_5
, "ISO_8859-5"},
110 {CCT_ISO_8859_6
, "ISO_8859-6"},
111 {CCT_ISO_8859_7
, "ISO_8859-7"},
112 {CCT_ISO_8859_8
, "ISO_8859-8"} };
114 static text_style_t
* CreateGroupStyle()
116 text_style_t
*p_style
= text_style_Create(STYLE_NO_DEFAULTS
);
119 p_style
->i_features
= STYLE_HAS_FLAGS
|STYLE_HAS_BACKGROUND_ALPHA
|STYLE_HAS_BACKGROUND_COLOR
;
120 /* Teletext needs default background to black */
121 p_style
->i_background_alpha
= STYLE_ALPHA_OPAQUE
;
122 p_style
->i_background_color
= 0x000000;
123 p_style
->i_font_size
= 0;
124 p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
129 static void TextBufferFlush(stl_sg_t
*p_group
, uint8_t *p_buf
, uint8_t *pi_buf
,
130 const char *psz_charset
)
135 char *psz_utf8
= FromCharset(psz_charset
, p_buf
, *pi_buf
);
138 *p_group
->pp_segment_last
= text_segment_New(psz_utf8
);
139 if(*p_group
->pp_segment_last
)
142 (*p_group
->pp_segment_last
)->style
= text_style_Duplicate(p_group
->p_style
);
143 p_group
->pp_segment_last
= &((*p_group
->pp_segment_last
)->p_next
);
151 static void GroupParseTeletext(stl_sg_t
*p_group
, uint8_t code
)
153 if(p_group
->p_style
== NULL
&&
154 !(p_group
->p_style
= CreateGroupStyle()))
157 /* See ETS 300 706 Table 26 as EBU 3264 does only name values
158 and does not explain at all */
160 static const uint32_t colors
[] =
172 /* Teletext data received, so we need to enable background */
173 p_group
->p_style
->i_style_flags
|= STYLE_BACKGROUND
;
178 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
179 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
182 case 0x0d: /* double height */
183 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
* 2;
184 p_group
->p_style
->i_style_flags
&= ~STYLE_DOUBLEWIDTH
;
185 p_group
->p_style
->i_style_flags
|= STYLE_HALFWIDTH
;
188 case 0x0e: /* double width */
189 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
190 p_group
->p_style
->i_style_flags
&= ~STYLE_HALFWIDTH
;
191 p_group
->p_style
->i_style_flags
|= STYLE_DOUBLEWIDTH
;
194 case 0x0f: /* double size */
195 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
* 2;
196 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
200 p_group
->p_style
->i_background_color
= p_group
->p_style
->i_font_color
;
201 p_group
->p_style
->i_features
&= ~STYLE_HAS_FONT_COLOR
;
205 p_group
->p_style
->i_background_color
= colors
[0];
211 p_group
->p_style
->i_font_color
= colors
[code
];
212 p_group
->p_style
->i_features
|= STYLE_HAS_FONT_COLOR
;
215 /* Need to handle Mosaic ? Really ? */
221 static void GroupApplyStyle(stl_sg_t
*p_group
, uint8_t code
)
223 if(p_group
->p_style
== NULL
&&
224 !(p_group
->p_style
= CreateGroupStyle()))
229 case STL_TF_ITALICS_ON
:
230 p_group
->p_style
->i_style_flags
|= STYLE_ITALIC
;
232 case STL_TF_ITALICS_OFF
:
233 p_group
->p_style
->i_style_flags
&= ~STYLE_ITALIC
;
235 case STL_TF_UNDERLINE_ON
:
236 p_group
->p_style
->i_style_flags
|= STYLE_UNDERLINE
;
238 case STL_TF_UNDERLINE_OFF
:
239 p_group
->p_style
->i_style_flags
&= ~STYLE_UNDERLINE
;
241 case STL_TF_BOXING_ON
:
242 case STL_TF_BOXING_OFF
:
248 static int64_t ParseTimeCode(const uint8_t *data
, double fps
)
250 return INT64_C(1000000) * (data
[0] * 3600 +
256 static void ClearTeletextStyles(stl_sg_t
*p_group
)
260 p_group
->p_style
->i_features
&= ~STYLE_HAS_FONT_COLOR
;
261 p_group
->p_style
->i_background_color
= 0x000000;
262 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
263 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
267 /* Returns true if group is we need to output group */
268 static bool ParseTTI(stl_sg_t
*p_group
, const uint8_t *p_data
, const char *psz_charset
)
270 uint8_t p_buffer
[STL_TEXTFIELD_SIZE
];
271 uint8_t i_buffer
= 0;
274 uint8_t ebn
= p_data
[3];
275 if(ebn
> 0xef && ebn
!= 0xff)
278 if(p_data
[15] != 0x00) /* comment flag */
281 if(p_data
[14] > 0x00)
282 p_group
->i_justify
= p_data
[14];
284 /* Accumulating started or continuing.
285 * We must not flush current segments on output and continue on next block */
286 p_group
->i_accumulating
= (p_data
[4] == 0x01 || p_data
[4] == 0x02);
288 p_group
->i_start
= ParseTimeCode( &p_data
[5], 30 );
289 p_group
->i_end
= ParseTimeCode( &p_data
[9], 30 );
292 for (size_t i
= STL_TTI_HEADER_SIZE
; i
< STL_TTI_SIZE
; i
++)
294 const uint8_t code
= p_data
[i
];
297 case STL_TF_LINEBREAK
:
298 p_buffer
[i_buffer
++] = '\n';
299 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
300 /* Clear teletext styles on each new row */
301 ClearTeletextStyles(p_group
);
304 case STL_TF_END_FILL
:
305 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
306 ClearTeletextStyles(p_group
);
310 if(code
<= STL_TF_TELETEXT_LAST
)
312 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
313 GroupParseTeletext(p_group
, code
);
315 else if((code
>= STL_TF_CHARCODE1_FIRST
&& code
<= STL_TF_CHARCODE1_LAST
) ||
316 code
>= STL_TF_CHARCODE2_FIRST
)
318 p_buffer
[i_buffer
++] = code
;
320 else if(code
>= STL_TF_ITALICS_ON
&& code
<= STL_TF_BOXING_OFF
)
322 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
323 GroupApplyStyle(p_group
, code
);
329 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
334 static void FillSubpictureUpdater(stl_sg_t
*p_group
, subpicture_updater_sys_t
*p_spu_sys
)
336 if(p_group
->i_accumulating
)
338 p_spu_sys
->region
.p_segments
= text_segment_Copy(p_group
->p_segment
);
342 p_spu_sys
->region
.p_segments
= p_group
->p_segment
;
343 p_group
->p_segment
= NULL
;
344 p_group
->pp_segment_last
= &p_group
->p_segment
;
347 p_spu_sys
->region
.align
= SUBPICTURE_ALIGN_BOTTOM
;
348 if(p_group
->i_justify
== 0x01)
349 p_spu_sys
->region
.inner_align
= SUBPICTURE_ALIGN_LEFT
;
350 else if(p_group
->i_justify
== 0x03)
351 p_spu_sys
->region
.inner_align
= SUBPICTURE_ALIGN_RIGHT
;
354 static void ResetGroups(decoder_sys_t
*p_sys
)
356 for(size_t i
=0; i
<=STL_GROUPS_MAX
; i
++)
358 stl_sg_t
*p_group
= &p_sys
->groups
[i
];
359 if(p_group
->p_segment
)
361 text_segment_ChainDelete(p_group
->p_segment
);
362 p_group
->p_segment
= NULL
;
363 p_group
->pp_segment_last
= &p_group
->p_segment
;
368 text_style_Delete(p_group
->p_style
);
369 p_group
->p_style
= NULL
;
372 p_group
->i_accumulating
= false;
374 p_group
->i_start
= 0;
375 p_group
->i_justify
= 0;
379 static int Decode(decoder_t
*p_dec
, block_t
*p_block
)
381 if (p_block
== NULL
) /* No Drain */
382 return VLCDEC_SUCCESS
;
384 if(p_block
->i_buffer
< STL_TTI_SIZE
)
385 p_block
->i_flags
|= BLOCK_FLAG_CORRUPTED
;
387 if(p_block
->i_flags
& (BLOCK_FLAG_CORRUPTED
|BLOCK_FLAG_DISCONTINUITY
))
389 ResetGroups(p_dec
->p_sys
);
391 if(p_block
->i_flags
& BLOCK_FLAG_CORRUPTED
)
393 block_Release(p_block
);
394 return VLCDEC_SUCCESS
;
398 const char *psz_charset
= cct_nums
[p_dec
->p_sys
->cct
- CCT_BEGIN
].str
;
399 for (size_t i
= 0; i
< p_block
->i_buffer
/ STL_TTI_SIZE
; i
++)
401 stl_sg_t
*p_group
= &p_dec
->p_sys
->groups
[p_block
->p_buffer
[0]];
402 if(ParseTTI(p_group
, &p_block
->p_buffer
[i
* STL_TTI_SIZE
], psz_charset
) &&
403 p_group
->p_segment
!= NULL
)
406 subpicture_t
*p_sub
= decoder_NewSubpictureText(p_dec
);
409 FillSubpictureUpdater(p_group
, p_sub
->updater
.p_sys
);
411 p_sub
->b_absolute
= false;
413 if(p_group
->i_end
&& p_group
->i_start
>= p_block
->i_dts
)
415 p_sub
->i_start
= VLC_TS_0
+ p_group
->i_start
;
416 p_sub
->i_stop
= VLC_TS_0
+ p_group
->i_end
;
420 p_sub
->i_start
= p_block
->i_pts
;
421 p_sub
->i_stop
= p_block
->i_pts
+ p_block
->i_length
;
422 p_sub
->b_ephemer
= (p_block
->i_length
== 0);
424 decoder_QueueSub(p_dec
, p_sub
);
429 ResetGroups(p_dec
->p_sys
);
431 block_Release(p_block
);
432 return VLCDEC_SUCCESS
;
435 static int ExtractCCT(const decoder_t
*dec
, cct_number_value_t
*cct_number
)
437 uint8_t *header
= dec
->fmt_in
.p_extra
;
439 msg_Err(dec
, "NULL EBU header (GSI block)\n");
443 if (GSI_BLOCK_SIZE
!= dec
->fmt_in
.i_extra
) {
444 msg_Err(dec
, "EBU header is not in expected size (%d)\n", dec
->fmt_in
.i_extra
);
448 int cct
= (header
[12] << 8) | header
[13];
449 if (CCT_BEGIN
> cct
|| CCT_END
< cct
) {
450 msg_Err(dec
, "EBU header contains illegal CCT (0x%x)\n", cct
);
459 static int Open(vlc_object_t
*object
)
461 decoder_t
*dec
= (decoder_t
*)object
;
463 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_EBU_STL
)
466 cct_number_value_t cct
;
467 int rc
= ExtractCCT(dec
, &cct
);
468 if (VLC_SUCCESS
!= rc
)
471 msg_Dbg(dec
, "CCT=0x%x", cct
);
473 decoder_sys_t
*sys
= calloc(1, sizeof(*sys
));
478 for(size_t i
=0; i
<=STL_GROUPS_MAX
; i
++)
479 sys
->groups
[i
].pp_segment_last
= &sys
->groups
[i
].p_segment
;
482 dec
->pf_decode
= Decode
;
483 dec
->fmt_out
.i_cat
= SPU_ES
;
484 dec
->fmt_out
.i_codec
= 0;
488 static void Close(vlc_object_t
*object
)
490 decoder_t
*dec
= (decoder_t
*)object
;
491 decoder_sys_t
*p_sys
= dec
->p_sys
;