1 /*****************************************************************************
2 * stl.c: EBU STL decoder
3 *****************************************************************************
4 * Copyright (C) 2010 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 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_codec.h>
34 #include <vlc_charset.h>
36 #include "substext.h" /* required for font scaling / updater */
38 /*****************************************************************************
40 *****************************************************************************/
41 static int Open (vlc_object_t
*);
42 static void Close(vlc_object_t
*);
45 set_description(N_("EBU STL subtitles decoder"))
46 set_category(CAT_INPUT
)
47 set_subcategory(SUBCAT_INPUT_SCODEC
)
48 set_capability("spu decoder", 10)
49 set_callbacks(Open
, Close
)
52 /*****************************************************************************
53 * Local definitions/prototypes
54 *****************************************************************************/
55 #define GSI_BLOCK_SIZE 1024
57 #define STL_GROUPS_MAX 255
59 #define STL_TEXTFIELD_SIZE 112
60 #define STL_TTI_HEADER_SIZE 16
61 #define STL_TTI_SIZE (STL_TTI_HEADER_SIZE + STL_TEXTFIELD_SIZE)
63 #define STL_TF_TELETEXT_FIRST 0x00
64 #define STL_TF_TELETEXT_LAST 0x1f
65 #define STL_TF_CHARCODE1_FIRST 0x20
66 #define STL_TF_CHARCODE1_LAST 0x7f
67 #define STL_TF_ITALICS_ON 0x80
68 #define STL_TF_ITALICS_OFF 0x81
69 #define STL_TF_UNDERLINE_ON 0x82
70 #define STL_TF_UNDERLINE_OFF 0x83
71 #define STL_TF_BOXING_ON 0x84
72 #define STL_TF_BOXING_OFF 0x85
73 #define STL_TF_LINEBREAK 0x8a
74 #define STL_TF_END_FILL 0x8f
75 #define STL_TF_CHARCODE2_FIRST 0xa1
78 CCT_ISO_6937_2
= 0x3030, CCT_BEGIN
= CCT_ISO_6937_2
,
79 CCT_ISO_8859_5
= 0x3031,
80 CCT_ISO_8859_6
= 0x3032,
81 CCT_ISO_8859_7
= 0x3033,
82 CCT_ISO_8859_8
= 0x3034, CCT_END
= CCT_ISO_8859_8
87 uint8_t i_accumulating
;
91 text_style_t
*p_style
;
92 text_segment_t
*p_segment
;
93 text_segment_t
**pp_segment_last
;
97 cct_number_value_t value
;
103 stl_sg_t groups
[STL_GROUPS_MAX
+ 1];
104 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 vlc_tick_t
ParseTimeCode(const uint8_t *data
, double fps
)
250 return vlc_tick_from_sec( 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
, double fps
)
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], fps
);
289 p_group
->i_end
= ParseTimeCode( &p_data
[9], fps
);
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
, subtext_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;
373 p_group
->i_end
= VLC_TICK_INVALID
;
374 p_group
->i_start
= VLC_TICK_INVALID
;
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 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
386 if(p_block
->i_buffer
< STL_TTI_SIZE
)
387 p_block
->i_flags
|= BLOCK_FLAG_CORRUPTED
;
389 if(p_block
->i_flags
& (BLOCK_FLAG_CORRUPTED
|BLOCK_FLAG_DISCONTINUITY
))
391 ResetGroups(p_dec
->p_sys
);
393 if(p_block
->i_flags
& BLOCK_FLAG_CORRUPTED
)
395 block_Release(p_block
);
396 return VLCDEC_SUCCESS
;
400 const char *psz_charset
= cct_nums
[p_sys
->cct
- CCT_BEGIN
].str
;
401 for (size_t i
= 0; i
< p_block
->i_buffer
/ STL_TTI_SIZE
; i
++)
403 stl_sg_t
*p_group
= &p_sys
->groups
[p_block
->p_buffer
[0]];
404 if(ParseTTI(p_group
, &p_block
->p_buffer
[i
* STL_TTI_SIZE
], psz_charset
, p_sys
->i_fps
) &&
405 p_group
->p_segment
!= NULL
)
408 subpicture_t
*p_sub
= decoder_NewSubpictureText(p_dec
);
411 FillSubpictureUpdater(p_group
, p_sub
->updater
.p_sys
);
413 p_sub
->b_absolute
= false;
415 if(p_group
->i_end
!= VLC_TICK_INVALID
&& p_group
->i_start
>= p_block
->i_dts
)
417 p_sub
->i_start
= VLC_TICK_0
+ p_group
->i_start
;
418 p_sub
->i_stop
= VLC_TICK_0
+ p_group
->i_end
;
422 p_sub
->i_start
= p_block
->i_pts
;
423 p_sub
->i_stop
= p_block
->i_pts
+ p_block
->i_length
;
424 p_sub
->b_ephemer
= (p_block
->i_length
== VLC_TICK_INVALID
);
426 decoder_QueueSub(p_dec
, p_sub
);
433 block_Release(p_block
);
434 return VLCDEC_SUCCESS
;
437 static int ParseGSI(decoder_t
*dec
, decoder_sys_t
*p_sys
)
439 uint8_t *header
= dec
->fmt_in
.p_extra
;
441 msg_Err(dec
, "NULL EBU header (GSI block)\n");
445 if (GSI_BLOCK_SIZE
!= dec
->fmt_in
.i_extra
) {
446 msg_Err(dec
, "EBU header is not in expected size (%d)\n", dec
->fmt_in
.i_extra
);
450 char dfc_fps_str
[] = { header
[6], header
[7], '\0' };
451 int fps
= strtol(dfc_fps_str
, NULL
, 10);
452 if (1 > fps
|| 60 < fps
) {
453 msg_Warn(dec
, "EBU header contains unsupported DFC fps ('%s'); falling back to 25\n", dfc_fps_str
);
457 int cct
= (header
[12] << 8) | header
[13];
458 if (CCT_BEGIN
> cct
|| CCT_END
< cct
) {
459 msg_Err(dec
, "EBU header contains illegal CCT (0x%x)\n", cct
);
463 msg_Dbg(dec
, "DFC fps=%d, CCT=0x%x", fps
, cct
);
470 static int Open(vlc_object_t
*object
)
472 decoder_t
*dec
= (decoder_t
*)object
;
474 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_EBU_STL
)
477 decoder_sys_t
*sys
= calloc(1, sizeof(*sys
));
481 int rc
= ParseGSI(dec
, sys
);
482 if (VLC_SUCCESS
!= rc
)
485 for(size_t i
=0; i
<=STL_GROUPS_MAX
; i
++)
486 sys
->groups
[i
].pp_segment_last
= &sys
->groups
[i
].p_segment
;
489 dec
->pf_decode
= Decode
;
490 dec
->fmt_out
.i_codec
= 0;
494 static void Close(vlc_object_t
*object
)
496 decoder_t
*dec
= (decoder_t
*)object
;
497 decoder_sys_t
*p_sys
= dec
->p_sys
;