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_charset.h>
37 #include "substext.h" /* required for font scaling / updater */
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open (vlc_object_t
*);
43 static void Close(vlc_object_t
*);
46 set_description(N_("EBU STL subtitles decoder"))
47 set_category(CAT_INPUT
)
48 set_subcategory(SUBCAT_INPUT_SCODEC
)
49 set_capability("spu decoder", 10)
50 set_callbacks(Open
, Close
)
53 /*****************************************************************************
54 * Local definitions/prototypes
55 *****************************************************************************/
56 #define GSI_BLOCK_SIZE 1024
58 #define STL_GROUPS_MAX 255
60 #define STL_TEXTFIELD_SIZE 112
61 #define STL_TTI_HEADER_SIZE 16
62 #define STL_TTI_SIZE (STL_TTI_HEADER_SIZE + STL_TEXTFIELD_SIZE)
64 #define STL_TF_TELETEXT_FIRST 0x00
65 #define STL_TF_TELETEXT_LAST 0x1f
66 #define STL_TF_CHARCODE1_FIRST 0x20
67 #define STL_TF_CHARCODE1_LAST 0x7f
68 #define STL_TF_ITALICS_ON 0x80
69 #define STL_TF_ITALICS_OFF 0x81
70 #define STL_TF_UNDERLINE_ON 0x82
71 #define STL_TF_UNDERLINE_OFF 0x83
72 #define STL_TF_BOXING_ON 0x84
73 #define STL_TF_BOXING_OFF 0x85
74 #define STL_TF_LINEBREAK 0x8a
75 #define STL_TF_END_FILL 0x8f
76 #define STL_TF_CHARCODE2_FIRST 0xa1
79 CCT_ISO_6937_2
= 0x3030, CCT_BEGIN
= CCT_ISO_6937_2
,
80 CCT_ISO_8859_5
= 0x3031,
81 CCT_ISO_8859_6
= 0x3032,
82 CCT_ISO_8859_7
= 0x3033,
83 CCT_ISO_8859_8
= 0x3034, CCT_END
= CCT_ISO_8859_8
88 uint8_t i_accumulating
;
92 text_style_t
*p_style
;
93 text_segment_t
*p_segment
;
94 text_segment_t
**pp_segment_last
;
98 cct_number_value_t value
;
104 stl_sg_t groups
[STL_GROUPS_MAX
+ 1];
105 cct_number_value_t cct
;
109 static cct_number_t cct_nums
[] = { {CCT_ISO_6937_2
, "ISO_6937-2"},
110 {CCT_ISO_8859_5
, "ISO_8859-5"},
111 {CCT_ISO_8859_6
, "ISO_8859-6"},
112 {CCT_ISO_8859_7
, "ISO_8859-7"},
113 {CCT_ISO_8859_8
, "ISO_8859-8"} };
115 static text_style_t
* CreateGroupStyle()
117 text_style_t
*p_style
= text_style_Create(STYLE_NO_DEFAULTS
);
120 p_style
->i_features
= STYLE_HAS_FLAGS
|STYLE_HAS_BACKGROUND_ALPHA
|STYLE_HAS_BACKGROUND_COLOR
;
121 /* Teletext needs default background to black */
122 p_style
->i_background_alpha
= STYLE_ALPHA_OPAQUE
;
123 p_style
->i_background_color
= 0x000000;
124 p_style
->i_font_size
= 0;
125 p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
130 static void TextBufferFlush(stl_sg_t
*p_group
, uint8_t *p_buf
, uint8_t *pi_buf
,
131 const char *psz_charset
)
136 char *psz_utf8
= FromCharset(psz_charset
, p_buf
, *pi_buf
);
139 *p_group
->pp_segment_last
= text_segment_New(psz_utf8
);
140 if(*p_group
->pp_segment_last
)
143 (*p_group
->pp_segment_last
)->style
= text_style_Duplicate(p_group
->p_style
);
144 p_group
->pp_segment_last
= &((*p_group
->pp_segment_last
)->p_next
);
152 static void GroupParseTeletext(stl_sg_t
*p_group
, uint8_t code
)
154 if(p_group
->p_style
== NULL
&&
155 !(p_group
->p_style
= CreateGroupStyle()))
158 /* See ETS 300 706 Table 26 as EBU 3264 does only name values
159 and does not explain at all */
161 static const uint32_t colors
[] =
173 /* Teletext data received, so we need to enable background */
174 p_group
->p_style
->i_style_flags
|= STYLE_BACKGROUND
;
179 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
180 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
183 case 0x0d: /* double height */
184 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
* 2;
185 p_group
->p_style
->i_style_flags
&= ~STYLE_DOUBLEWIDTH
;
186 p_group
->p_style
->i_style_flags
|= STYLE_HALFWIDTH
;
189 case 0x0e: /* double width */
190 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
191 p_group
->p_style
->i_style_flags
&= ~STYLE_HALFWIDTH
;
192 p_group
->p_style
->i_style_flags
|= STYLE_DOUBLEWIDTH
;
195 case 0x0f: /* double size */
196 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
* 2;
197 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
201 p_group
->p_style
->i_background_color
= p_group
->p_style
->i_font_color
;
202 p_group
->p_style
->i_features
&= ~STYLE_HAS_FONT_COLOR
;
206 p_group
->p_style
->i_background_color
= colors
[0];
212 p_group
->p_style
->i_font_color
= colors
[code
];
213 p_group
->p_style
->i_features
|= STYLE_HAS_FONT_COLOR
;
216 /* Need to handle Mosaic ? Really ? */
222 static void GroupApplyStyle(stl_sg_t
*p_group
, uint8_t code
)
224 if(p_group
->p_style
== NULL
&&
225 !(p_group
->p_style
= CreateGroupStyle()))
230 case STL_TF_ITALICS_ON
:
231 p_group
->p_style
->i_style_flags
|= STYLE_ITALIC
;
233 case STL_TF_ITALICS_OFF
:
234 p_group
->p_style
->i_style_flags
&= ~STYLE_ITALIC
;
236 case STL_TF_UNDERLINE_ON
:
237 p_group
->p_style
->i_style_flags
|= STYLE_UNDERLINE
;
239 case STL_TF_UNDERLINE_OFF
:
240 p_group
->p_style
->i_style_flags
&= ~STYLE_UNDERLINE
;
242 case STL_TF_BOXING_ON
:
243 case STL_TF_BOXING_OFF
:
249 static vlc_tick_t
ParseTimeCode(const uint8_t *data
, double fps
)
251 return vlc_tick_from_sec( data
[0] * 3600 +
257 static void ClearTeletextStyles(stl_sg_t
*p_group
)
261 p_group
->p_style
->i_features
&= ~STYLE_HAS_FONT_COLOR
;
262 p_group
->p_style
->i_background_color
= 0x000000;
263 p_group
->p_style
->f_font_relsize
= STYLE_DEFAULT_REL_FONT_SIZE
;
264 p_group
->p_style
->i_style_flags
&= ~(STYLE_DOUBLEWIDTH
|STYLE_HALFWIDTH
);
268 /* Returns true if group is we need to output group */
269 static bool ParseTTI(stl_sg_t
*p_group
, const uint8_t *p_data
, const char *psz_charset
, double fps
)
271 uint8_t p_buffer
[STL_TEXTFIELD_SIZE
];
272 uint8_t i_buffer
= 0;
275 uint8_t ebn
= p_data
[3];
276 if(ebn
> 0xef && ebn
!= 0xff)
279 if(p_data
[15] != 0x00) /* comment flag */
282 if(p_data
[14] > 0x00)
283 p_group
->i_justify
= p_data
[14];
285 /* Accumulating started or continuing.
286 * We must not flush current segments on output and continue on next block */
287 p_group
->i_accumulating
= (p_data
[4] == 0x01 || p_data
[4] == 0x02);
289 p_group
->i_start
= ParseTimeCode( &p_data
[5], fps
);
290 p_group
->i_end
= ParseTimeCode( &p_data
[9], fps
);
293 for (size_t i
= STL_TTI_HEADER_SIZE
; i
< STL_TTI_SIZE
; i
++)
295 const uint8_t code
= p_data
[i
];
298 case STL_TF_LINEBREAK
:
299 p_buffer
[i_buffer
++] = '\n';
300 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
301 /* Clear teletext styles on each new row */
302 ClearTeletextStyles(p_group
);
305 case STL_TF_END_FILL
:
306 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
307 ClearTeletextStyles(p_group
);
311 if(code
<= STL_TF_TELETEXT_LAST
)
313 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
314 GroupParseTeletext(p_group
, code
);
316 else if((code
>= STL_TF_CHARCODE1_FIRST
&& code
<= STL_TF_CHARCODE1_LAST
) ||
317 code
>= STL_TF_CHARCODE2_FIRST
)
319 p_buffer
[i_buffer
++] = code
;
321 else if(code
>= STL_TF_ITALICS_ON
&& code
<= STL_TF_BOXING_OFF
)
323 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
324 GroupApplyStyle(p_group
, code
);
330 TextBufferFlush(p_group
, p_buffer
, &i_buffer
, psz_charset
);
335 static void FillSubpictureUpdater(stl_sg_t
*p_group
, subtext_updater_sys_t
*p_spu_sys
)
337 if(p_group
->i_accumulating
)
339 p_spu_sys
->region
.p_segments
= text_segment_Copy(p_group
->p_segment
);
343 p_spu_sys
->region
.p_segments
= p_group
->p_segment
;
344 p_group
->p_segment
= NULL
;
345 p_group
->pp_segment_last
= &p_group
->p_segment
;
348 p_spu_sys
->region
.align
= SUBPICTURE_ALIGN_BOTTOM
;
349 if(p_group
->i_justify
== 0x01)
350 p_spu_sys
->region
.inner_align
= SUBPICTURE_ALIGN_LEFT
;
351 else if(p_group
->i_justify
== 0x03)
352 p_spu_sys
->region
.inner_align
= SUBPICTURE_ALIGN_RIGHT
;
355 static void ResetGroups(decoder_sys_t
*p_sys
)
357 for(size_t i
=0; i
<=STL_GROUPS_MAX
; i
++)
359 stl_sg_t
*p_group
= &p_sys
->groups
[i
];
360 if(p_group
->p_segment
)
362 text_segment_ChainDelete(p_group
->p_segment
);
363 p_group
->p_segment
= NULL
;
364 p_group
->pp_segment_last
= &p_group
->p_segment
;
369 text_style_Delete(p_group
->p_style
);
370 p_group
->p_style
= NULL
;
373 p_group
->i_accumulating
= false;
374 p_group
->i_end
= VLC_TICK_INVALID
;
375 p_group
->i_start
= VLC_TICK_INVALID
;
376 p_group
->i_justify
= 0;
380 static int Decode(decoder_t
*p_dec
, block_t
*p_block
)
382 if (p_block
== NULL
) /* No Drain */
383 return VLCDEC_SUCCESS
;
385 decoder_sys_t
*p_sys
= p_dec
->p_sys
;
387 if(p_block
->i_buffer
< STL_TTI_SIZE
)
388 p_block
->i_flags
|= BLOCK_FLAG_CORRUPTED
;
390 if(p_block
->i_flags
& (BLOCK_FLAG_CORRUPTED
|BLOCK_FLAG_DISCONTINUITY
))
392 ResetGroups(p_dec
->p_sys
);
394 if(p_block
->i_flags
& BLOCK_FLAG_CORRUPTED
)
396 block_Release(p_block
);
397 return VLCDEC_SUCCESS
;
401 const char *psz_charset
= cct_nums
[p_sys
->cct
- CCT_BEGIN
].str
;
402 for (size_t i
= 0; i
< p_block
->i_buffer
/ STL_TTI_SIZE
; i
++)
404 stl_sg_t
*p_group
= &p_sys
->groups
[p_block
->p_buffer
[0]];
405 if(ParseTTI(p_group
, &p_block
->p_buffer
[i
* STL_TTI_SIZE
], psz_charset
, p_sys
->i_fps
) &&
406 p_group
->p_segment
!= NULL
)
409 subpicture_t
*p_sub
= decoder_NewSubpictureText(p_dec
);
412 FillSubpictureUpdater(p_group
, p_sub
->updater
.p_sys
);
414 p_sub
->b_absolute
= false;
416 if(p_group
->i_end
!= VLC_TICK_INVALID
&& p_group
->i_start
>= p_block
->i_dts
)
418 p_sub
->i_start
= VLC_TICK_0
+ p_group
->i_start
;
419 p_sub
->i_stop
= VLC_TICK_0
+ p_group
->i_end
;
423 p_sub
->i_start
= p_block
->i_pts
;
424 p_sub
->i_stop
= p_block
->i_pts
+ p_block
->i_length
;
425 p_sub
->b_ephemer
= (p_block
->i_length
== VLC_TICK_INVALID
);
427 decoder_QueueSub(p_dec
, p_sub
);
434 block_Release(p_block
);
435 return VLCDEC_SUCCESS
;
438 static int ParseGSI(const decoder_t
*dec
, decoder_sys_t
*p_sys
)
440 uint8_t *header
= dec
->fmt_in
.p_extra
;
442 msg_Err(dec
, "NULL EBU header (GSI block)\n");
446 if (GSI_BLOCK_SIZE
!= dec
->fmt_in
.i_extra
) {
447 msg_Err(dec
, "EBU header is not in expected size (%d)\n", dec
->fmt_in
.i_extra
);
451 char dfc_fps_str
[] = { header
[6], header
[7], '\0' };
452 int fps
= strtol(dfc_fps_str
, NULL
, 10);
453 if (1 > fps
|| 60 < fps
) {
454 msg_Warn(dec
, "EBU header contains unsupported DFC fps ('%s'); falling back to 25\n", dfc_fps_str
);
458 int cct
= (header
[12] << 8) | header
[13];
459 if (CCT_BEGIN
> cct
|| CCT_END
< cct
) {
460 msg_Err(dec
, "EBU header contains illegal CCT (0x%x)\n", cct
);
464 msg_Dbg(dec
, "DFC fps=%d, CCT=0x%x", fps
, cct
);
471 static int Open(vlc_object_t
*object
)
473 decoder_t
*dec
= (decoder_t
*)object
;
475 if (dec
->fmt_in
.i_codec
!= VLC_CODEC_EBU_STL
)
478 decoder_sys_t
*sys
= calloc(1, sizeof(*sys
));
482 int rc
= ParseGSI(dec
, sys
);
483 if (VLC_SUCCESS
!= rc
)
486 for(size_t i
=0; i
<=STL_GROUPS_MAX
; i
++)
487 sys
->groups
[i
].pp_segment_last
= &sys
->groups
[i
].p_segment
;
490 dec
->pf_decode
= Decode
;
491 dec
->fmt_out
.i_codec
= 0;
495 static void Close(vlc_object_t
*object
)
497 decoder_t
*dec
= (decoder_t
*)object
;
498 decoder_sys_t
*p_sys
= dec
->p_sys
;