input: add input_SetProgramId
[vlc.git] / modules / codec / stl.c
bloba00e0290a1147f208866d4b8f601815c934f3788
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 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include <assert.h>
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 /*****************************************************************************
39 * Module descriptor
40 *****************************************************************************/
41 static int Open (vlc_object_t *);
42 static void Close(vlc_object_t *);
44 vlc_module_begin()
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)
50 vlc_module_end()
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
77 typedef enum {
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
83 } cct_number_value_t;
85 typedef struct
87 uint8_t i_accumulating;
88 uint8_t i_justify;
89 vlc_tick_t i_start;
90 vlc_tick_t i_end;
91 text_style_t *p_style;
92 text_segment_t *p_segment;
93 text_segment_t **pp_segment_last;
94 } stl_sg_t;
96 typedef struct {
97 cct_number_value_t value;
98 const char *str;
99 } cct_number_t;
101 typedef struct
103 stl_sg_t groups[STL_GROUPS_MAX + 1];
104 cct_number_value_t cct;
105 uint8_t i_fps;
106 } decoder_sys_t;
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 vlc_tick_t ParseTimeCode(const uint8_t *data, double fps)
250 return vlc_tick_from_sec( 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, double fps)
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], fps );
289 p_group->i_end = ParseTimeCode( &p_data[9], fps );
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, 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);
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 = 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 )
407 /* output */
408 subpicture_t *p_sub = decoder_NewSubpictureText(p_dec);
409 if( p_sub )
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;
420 else
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);
431 ResetGroups(p_sys);
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;
440 if (!header) {
441 msg_Err(dec, "NULL EBU header (GSI block)\n");
442 return VLC_EGENERIC;
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);
447 return VLC_EGENERIC;
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);
454 fps = 25;
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);
460 return VLC_EGENERIC;
463 msg_Dbg(dec, "DFC fps=%d, CCT=0x%x", fps, cct);
464 p_sys->i_fps = fps;
465 p_sys->cct = cct;
467 return VLC_SUCCESS;
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)
475 return VLC_EGENERIC;
477 decoder_sys_t *sys = calloc(1, sizeof(*sys));
478 if (!sys)
479 return VLC_ENOMEM;
481 int rc = ParseGSI(dec, sys);
482 if (VLC_SUCCESS != rc)
483 return rc;
485 for(size_t i=0; i<=STL_GROUPS_MAX; i++)
486 sys->groups[i].pp_segment_last = &sys->groups[i].p_segment;
488 dec->p_sys = sys;
489 dec->pf_decode = Decode;
490 dec->fmt_out.i_codec = 0;
491 return VLC_SUCCESS;
494 static void Close(vlc_object_t *object)
496 decoder_t *dec = (decoder_t*)object;
497 decoder_sys_t *p_sys = dec->p_sys;
499 ResetGroups(p_sys);
500 free(p_sys);