codec/stl: GroupParseTeletext: remove duplicate const
[vlc.git] / modules / codec / stl.c
blob1875678e37249a0b0fa2bdd7ddd742551412c511
1 /*****************************************************************************
2 * stl.c: EBU STL decoder
3 *****************************************************************************
4 * Copyright (C) 2010 Laurent Aimar
5 * $Id$
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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
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 /*****************************************************************************
41 * Module descriptor
42 *****************************************************************************/
43 static int Open (vlc_object_t *);
44 static void Close(vlc_object_t *);
46 vlc_module_begin()
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)
52 vlc_module_end()
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
79 typedef enum {
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
85 } cct_number_value_t;
87 typedef struct
89 uint8_t i_accumulating;
90 uint8_t i_justify;
91 int64_t i_start;
92 int64_t i_end;
93 text_style_t *p_style;
94 text_segment_t *p_segment;
95 text_segment_t **pp_segment_last;
96 } stl_sg_t;
98 typedef struct {
99 cct_number_value_t value;
100 const char *str;
101 } cct_number_t;
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);
117 if(p_style)
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;
126 return p_style;
129 static void TextBufferFlush(stl_sg_t *p_group, uint8_t *p_buf, uint8_t *pi_buf,
130 const char *psz_charset)
132 if(*pi_buf == 0)
133 return;
135 char *psz_utf8 = FromCharset(psz_charset, p_buf, *pi_buf);
136 if(psz_utf8)
138 *p_group->pp_segment_last = text_segment_New(psz_utf8);
139 if(*p_group->pp_segment_last)
141 if(p_group->p_style)
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);
145 free(psz_utf8);
148 *pi_buf = 0;
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()))
155 return;
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[] =
162 0x000000,
163 0xFF0000,
164 0x00FF00,
165 0xFFFF00,
166 0x0000FF,
167 0xFF00FF,
168 0x00FFFF,
169 0xFFFFFF,
172 /* Teletext data received, so we need to enable background */
173 p_group->p_style->i_style_flags |= STYLE_BACKGROUND;
175 switch(code)
177 case 0x0c:
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);
180 break;
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;
186 break;
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;
192 break;
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);
197 break;
199 case 0x1d:
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;
202 break;
204 case 0x1c:
205 p_group->p_style->i_background_color = colors[0];
206 break;
208 default:
209 if(code < 8)
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 ? */
216 break;
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()))
225 return;
227 switch(code)
229 case STL_TF_ITALICS_ON:
230 p_group->p_style->i_style_flags |= STYLE_ITALIC;
231 break;
232 case STL_TF_ITALICS_OFF:
233 p_group->p_style->i_style_flags &= ~STYLE_ITALIC;
234 break;
235 case STL_TF_UNDERLINE_ON:
236 p_group->p_style->i_style_flags |= STYLE_UNDERLINE;
237 break;
238 case STL_TF_UNDERLINE_OFF:
239 p_group->p_style->i_style_flags &= ~STYLE_UNDERLINE;
240 break;
241 case STL_TF_BOXING_ON:
242 case STL_TF_BOXING_OFF:
243 default:
244 break;
248 static int64_t ParseTimeCode(const uint8_t *data, double fps)
250 return INT64_C(1000000) * (data[0] * 3600 +
251 data[1] * 60 +
252 data[2] * 1 +
253 data[3] / fps);
256 static void ClearTeletextStyles(stl_sg_t *p_group)
258 if(p_group->p_style)
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;
273 /* Header */
274 uint8_t ebn = p_data[3];
275 if(ebn > 0xef && ebn != 0xff)
276 return false;
278 if(p_data[15] != 0x00) /* comment flag */
279 return false;
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 );
291 /* Text Field */
292 for (size_t i = STL_TTI_HEADER_SIZE; i < STL_TTI_SIZE; i++)
294 const uint8_t code = p_data[i];
295 switch(code)
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);
302 break;
304 case STL_TF_END_FILL:
305 TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
306 ClearTeletextStyles(p_group);
307 return true;
309 default:
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);
325 break;
329 TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
331 return false;
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);
340 else
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;
366 if(p_group->p_style)
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 = 0;
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 )
405 /* output */
406 subpicture_t *p_sub = decoder_NewSubpictureText(p_dec);
407 if( p_sub )
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;
418 else
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;
438 if (!header) {
439 msg_Err(dec, "NULL EBU header (GSI block)\n");
440 return VLC_EGENERIC;
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);
445 return VLC_EGENERIC;
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);
451 return VLC_EGENERIC;
454 *cct_number = cct;
456 return VLC_SUCCESS;
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)
464 return VLC_EGENERIC;
466 cct_number_value_t cct;
467 int rc = ExtractCCT(dec, &cct);
468 if (VLC_SUCCESS != rc)
469 return rc;
471 msg_Dbg(dec, "CCT=0x%x", cct);
473 decoder_sys_t *sys = calloc(1, sizeof(*sys));
474 if (!sys)
475 return VLC_ENOMEM;
477 sys->cct = cct;
478 for(size_t i=0; i<=STL_GROUPS_MAX; i++)
479 sys->groups[i].pp_segment_last = &sys->groups[i].p_segment;
481 dec->p_sys = sys;
482 dec->pf_decode = Decode;
483 dec->fmt_out.i_cat = SPU_ES;
484 dec->fmt_out.i_codec = 0;
485 return VLC_SUCCESS;
488 static void Close(vlc_object_t *object)
490 decoder_t *dec = (decoder_t*)object;
491 decoder_sys_t *p_sys = dec->p_sys;
493 ResetGroups(p_sys);
494 free(p_sys);