qml/KeyNavigableTableView: Add 'delegate' and 'contentY' aliases
[vlc.git] / modules / codec / cea708.c
blob01860e165059a5ae9a281028b77c967c957c01da
1 /*****************************************************************************
2 * cea708.c : CEA708 subtitles decoder
3 *****************************************************************************
4 * Copyright © 2017 VideoLabs, VideoLAN and VLC authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include <vlc_common.h>
25 #include <vlc_codec.h>
26 #include <vlc_subpicture.h>
28 #include "cea708.h"
29 #include "substext.h"
31 #include <assert.h>
33 #if 0
34 #define Debug(code) code
35 #else
36 #define Debug(code)
37 #endif
39 /*****************************************************************************
40 * Demuxing / Agreggation
41 *****************************************************************************/
42 struct cea708_demux_t
44 int8_t i_pkt_sequence;
45 uint8_t i_total_data;
46 uint8_t i_data;
47 uint8_t data[CEA708_DTVCC_MAX_PKT_SIZE];
48 vlc_tick_t i_time;
49 service_data_hdlr_t p_callback;
50 void *priv;
53 void CEA708_DTVCC_Demuxer_Flush( cea708_demux_t *h )
55 h->i_pkt_sequence = -1;
56 h->i_total_data = h->i_data = 0;
59 void CEA708_DTVCC_Demuxer_Release( cea708_demux_t *h )
61 free( h );
64 cea708_demux_t * CEA708_DTVCC_Demuxer_New( void *priv, service_data_hdlr_t hdlr )
66 cea708_demux_t *h = malloc( sizeof(cea708_demux_t) );
67 if( h )
69 h->priv = priv;
70 h->p_callback = hdlr;
71 CEA708_DTVCC_Demuxer_Flush( h );
73 return h;
76 static void CEA708_DTVCC_Demux_ServiceBlocks( cea708_demux_t *h, vlc_tick_t i_start,
77 const uint8_t *p_data, size_t i_data )
79 while( i_data >= 2 )
81 uint8_t i_sid = p_data[0] >> 5;
82 const uint8_t i_block_size = p_data[0] & 0x1F;
84 if( i_block_size == 0 || i_block_size > i_data - 1 )
86 return;
88 else if( i_sid == 0x07 )
90 i_sid = p_data[1] & 0x3F;
91 if( i_sid < 0x07 )
92 return;
93 p_data += 1; i_data -= 1;
95 p_data += 1; i_data -= 1;
97 h->p_callback( h->priv, i_sid, i_start, p_data, i_block_size );
99 p_data += i_block_size;
100 i_data -= i_block_size;
104 void CEA708_DTVCC_Demuxer_Push( cea708_demux_t *h, vlc_tick_t i_start, const uint8_t data[3] )
106 if( (data[0] & 0x03) == 3 ) /* Header packet */
108 const int8_t i_pkt_sequence = data[1] >> 6;
110 /* pkt loss/discontinuity, trash buffer */
111 if( i_pkt_sequence > 0 && ((h->i_pkt_sequence + 1) % 4) != i_pkt_sequence )
113 h->i_data = h->i_total_data = 0;
114 h->i_pkt_sequence = i_pkt_sequence;
115 return;
118 uint8_t pktsize = data[1] & 63;
119 if( pktsize == 0 )
120 pktsize = 127;
121 else
122 pktsize = pktsize * 2 - 1;
124 h->i_pkt_sequence = i_pkt_sequence;
125 h->i_total_data = pktsize;
126 h->i_data = 0;
127 h->i_time = i_start;
128 h->data[h->i_data++] = data[2];
130 else if( h->i_total_data > 0 ) /* Not synced to pkt header yet */
132 h->data[h->i_data++] = data[1];
133 h->data[h->i_data++] = data[2];
136 /* pkts assembly finished, we have a service block */
137 if( h->i_data > 0 && h->i_data >= h->i_total_data )
139 if( h->i_data == h->i_total_data ) /* Only if correct */
140 CEA708_DTVCC_Demux_ServiceBlocks( h, h->i_time, h->data, h->i_data );
141 h->i_total_data = h->i_data = 0;
145 /*****************************************************************************
146 * Service Data Decoding
147 *****************************************************************************/
149 #define CEA708_SERVICE_INPUT_BUFFER 128
151 #define CEA708_WINDOWS_COUNT 8
152 #define CEA708_PREDEFINED_STYLES 8
154 #define CEA708_SCREEN_ROWS 75
155 #define CEA708_SCREEN_COLS_43 160
156 #define CEA708_SCREEN_COLS_169 210
157 #define CEA708_SCREEN_SAFE_MARGIN_RATIO 0.10
158 #define CEA708_SAFE_AREA_REL (1.0 - CEA708_SCREEN_SAFE_MARGIN_RATIO)
160 #define CEA708_WINDOW_MAX_COLS 42
161 #define CEA708_WINDOW_MAX_ROWS 15
163 #define CEA708_ROW_HEIGHT_STANDARD (CEA708_SAFE_AREA_REL / \
164 CEA708_WINDOW_MAX_ROWS)
165 #define CEA708_FONT_TO_LINE_HEIGHT_RATIO 1.06
167 #define CEA708_FONTRELSIZE_STANDARD (100.0 * CEA708_ROW_HEIGHT_STANDARD / \
168 CEA708_FONT_TO_LINE_HEIGHT_RATIO)
169 #define CEA708_FONTRELSIZE_SMALL (CEA708_FONTRELSIZE_STANDARD * 0.7)
170 #define CEA708_FONTRELSIZE_LARGE (CEA708_FONTRELSIZE_STANDARD * 1.3)
172 enum cea708_status_e
174 CEA708_STATUS_OK = 1 << 0,
175 CEA708_STATUS_STARVING = 1 << 1,
176 CEA708_STATUS_OUTPUT = 1 << 2,
179 enum cea708_c0_codes
181 CEA708_C0_NUL = 0x00,
182 CEA708_C0_ETX = 0x03,
183 CEA708_C0_BS = 0x08,
184 CEA708_C0_FF = 0x0C,
185 CEA708_C0_CR = 0x0D,
186 CEA708_C0_HCR = 0x0E,
187 CEA708_C0_EXT1 = 0x10,
188 CEA708_C0_P16 = 0x18,
191 enum cea708_c1_codes
193 CEA708_C1_CW0 = 0x80,
194 CEA708_C1_CW7 = 0x87,
195 CEA708_C1_CLW,
196 CEA708_C1_DSW,
197 CEA708_C1_HDW,
198 CEA708_C1_TGW,
199 CEA708_C1_DLW,
200 CEA708_C1_DLY,
201 CEA708_C1_DLC,
202 CEA708_C1_RST,
203 CEA708_C1_SPA = 0x90,
204 CEA708_C1_SPC,
205 CEA708_C1_SPL,
206 CEA708_C1_SWA = 0x97,
207 CEA708_C1_DF0,
208 CEA708_C1_DF7 = 0x9F,
211 typedef struct
213 uint8_t ringbuffer[CEA708_SERVICE_INPUT_BUFFER];
214 uint8_t start;
215 uint8_t capacity;
216 } cea708_input_buffer_t;
218 static void cea708_input_buffer_init(cea708_input_buffer_t *ib)
220 ib->capacity = 0;
221 ib->start = 0;
224 static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t *ib)
226 return ib->capacity;
229 static uint8_t cea708_input_buffer_remain(const cea708_input_buffer_t *ib)
231 return CEA708_SERVICE_INPUT_BUFFER - ib->capacity;
234 static void cea708_input_buffer_add(cea708_input_buffer_t *ib, uint8_t a)
236 if( cea708_input_buffer_remain(ib) > 0 )
237 ib->ringbuffer[(ib->start + ib->capacity++) % CEA708_SERVICE_INPUT_BUFFER] = a;
240 static uint8_t cea708_input_buffer_peek(cea708_input_buffer_t *ib, uint8_t off)
242 if(off + 1 > ib->capacity)
243 return 0;
244 off = (ib->start + off) % CEA708_SERVICE_INPUT_BUFFER;
245 return ib->ringbuffer[off];
248 static uint8_t cea708_input_buffer_get(cea708_input_buffer_t *ib)
250 uint8_t a = cea708_input_buffer_peek( ib, 0 );
251 ib->start = (ib->start + 1) % CEA708_SERVICE_INPUT_BUFFER;
252 ib->capacity--;
253 return a;
256 enum cea708_opacity_e
258 CEA708_OPACITY_SOLID = 0,
259 CEA708_OPACITY_FLASH,
260 CEA708_OPACITY_TRANSLUCENT,
261 CEA708_OPACITY_TRANSPARENT,
264 enum cea708_edge_e
266 CEA708_EDGE_NONE =0,
267 CEA708_EDGE_RAISED,
268 CEA708_EDGE_DEPRESSED,
269 CEA708_EDGE_UNIFORM,
270 CEA708_EDGE_LEFT_DROP_SHADOW,
271 CEA708_EDGE_RIGHT_DROP_SHADOW,
274 typedef struct
276 enum
278 CEA708_PEN_SIZE_SMALL = 0,
279 CEA708_PEN_SIZE_STANDARD,
280 CEA708_PEN_SIZE_LARGE,
281 } size;
282 enum
284 CEA708_FONT_UNDEFINED = 0,
285 CEA708_FONT_MONOSPACED,
286 CEA708_FONT_PROP,
287 CEA708_FONT_MONO_SANS_SERIF,
288 CEA708_FONT_PROP_SANS_SERIF,
289 CEA708_FONT_CASUAL,
290 CEA708_FONT_CURSIVE,
291 CEA708_FONT_SMALL_CAPS,
292 } font;
293 enum
295 CEA708_TAG_DIALOG = 0,
296 CEA708_TAG_SPEAKER,
297 CEA708_TAG_SYNTHETIC_VOICE,
298 CEA708_TAG_DIALOG_SECONDARY_LANG,
299 CEA708_TAG_VOICEOVER,
300 CEA708_TAG_AUDIBLE_TRANSLATION,
301 CEA708_TAG_SUBTITLE_TRANSLATION,
302 CEA708_TAG_VOICE_QUALITY_DESCRIPTION,
303 CEA708_TAG_SONG_LYRICS,
304 CEA708_TAG_FX_DESCRIPTION,
305 CEA708_TAG_SCORE_DESCRIPTION,
306 CEA708_TAG_EXPLETIVE,
307 CEA708_TAG_NOT_TO_BE_DISPLAYED = 15,
308 } text_tag;
309 enum
311 CEA708_PEN_OFFSET_SUBSCRIPT = 0,
312 CEA708_PEN_OFFSET_NORMAL,
313 CEA708_PEN_OFFSET_SUPERSCRIPT,
314 } offset;
315 bool b_italics;
316 bool b_underline;
317 struct
319 uint8_t color;
320 enum cea708_opacity_e opacity;
321 } foreground, background;
322 uint8_t edge_color;
323 enum cea708_edge_e edge_type;
324 } cea708_pen_style_t;
326 typedef struct
328 cea708_pen_style_t style;
329 uint8_t row;
330 uint8_t col;
331 } cea708_pen_t;
333 typedef struct
335 enum
337 CEA708_WA_JUSTIFY_LEFT = 0,
338 CEA708_WA_JUSTIFY_RIGHT,
339 CEA708_WA_JUSTIFY_CENTER,
340 CEA708_WA_JUSTIFY_FULL,
341 } justify;
342 enum
344 CEA708_WA_DIRECTION_LTR = 0,
345 CEA708_WA_DIRECTION_RTL,
346 CEA708_WA_DIRECTION_TB,
347 CEA708_WA_DIRECTION_BT,
348 } print_direction, scroll_direction, effect_direction;
349 bool b_word_wrap;
350 enum
352 CEA708_WA_EFFECT_SNAP = 0,
353 CEA708_WA_EFFECT_FADE,
354 CEA708_WA_EFFECT_WIPE,
355 } display_effect;
356 uint8_t effect_speed;
357 uint8_t fill_color_color;
358 enum cea708_opacity_e fill_opacity;
359 enum cea708_edge_e border_type;
360 uint8_t border_color_color;
361 } cea708_window_style_t;
363 typedef struct cea708_text_row_t cea708_text_row_t;
365 struct cea708_text_row_t
367 uint8_t characters[CEA708_WINDOW_MAX_COLS * 4];
368 cea708_pen_style_t styles[CEA708_WINDOW_MAX_COLS];
369 uint8_t firstcol;
370 uint8_t lastcol;
373 static void cea708_text_row_Delete( cea708_text_row_t *p_row )
375 free( p_row );
378 static cea708_text_row_t * cea708_text_row_New( void )
380 cea708_text_row_t *p_row = malloc( sizeof(*p_row) );
381 if( p_row )
383 p_row->firstcol = CEA708_WINDOW_MAX_COLS;
384 p_row->lastcol = 0;
385 memset(p_row->characters, 0, 4 * CEA708_WINDOW_MAX_COLS);
387 return p_row;
390 typedef struct
392 cea708_text_row_t * rows[CEA708_WINDOW_MAX_ROWS];
393 uint8_t i_firstrow;
394 uint8_t i_lastrow;
396 uint8_t i_priority;
398 enum
400 CEA708_ANCHOR_TOP_LEFT = 0,
401 CEA708_ANCHOR_TOP_CENTER,
402 CEA708_ANCHOR_TOP_RIGHT,
403 CEA708_ANCHOR_CENTER_LEFT,
404 CEA708_ANCHOR_CENTER_CENTER,
405 CEA708_ANCHOR_CENTER_RIGHT,
406 CEA708_ANCHOR_BOTTOM_LEFT,
407 CEA708_ANCHOR_BOTTOM_CENTER,
408 CEA708_ANCHOR_BOTTOM_RIGHT,
409 } anchor_point;
410 uint8_t i_anchor_offset_v;
411 uint8_t i_anchor_offset_h;
413 /* Extras row for window scroll */
414 uint8_t i_row_count;
415 uint8_t i_col_count;
417 /* flags */
418 uint8_t b_relative;
419 uint8_t b_row_lock;
420 uint8_t b_column_lock;
421 uint8_t b_visible;
423 cea708_window_style_t style;
424 cea708_pen_style_t pen;
426 uint8_t row;
427 uint8_t col;
429 bool b_defined;
431 } cea708_window_t;
433 struct cea708_t
435 decoder_t *p_dec;
437 /* Defaults */
438 cea708_window_t window[CEA708_WINDOWS_COUNT];
439 cea708_input_buffer_t input_buffer;
441 /* Decoding context */
442 cea708_window_t *p_cw; /* current window */
443 vlc_tick_t suspended_deadline; /* not VLC_TICK_INVALID when delay is active */
444 vlc_tick_t i_clock;
445 bool b_text_waiting;
448 static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 );
449 static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 );
450 static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 );
451 static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 );
452 static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 );
453 static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 );
455 #define DEFAULT_NTSC_STYLE(font, edge, bgopacity ) \
457 CEA708_PEN_SIZE_STANDARD,\
458 font,\
459 CEA708_TAG_DIALOG,\
460 CEA708_PEN_OFFSET_NORMAL,\
461 false,\
462 false,\
463 { 0x2A, CEA708_OPACITY_SOLID, },\
464 { 0x00, bgopacity, },\
465 0x00,\
466 edge,\
468 static const cea708_pen_style_t cea708_default_pen_styles[CEA708_PREDEFINED_STYLES] =
470 DEFAULT_NTSC_STYLE( CEA708_FONT_UNDEFINED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
471 DEFAULT_NTSC_STYLE( CEA708_FONT_MONOSPACED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
472 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
473 DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
474 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
475 DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
476 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
478 #undef DEFAULT_NTSC_STYLE
480 #define DEFAULT_NTSC_WA_STYLE(just, pd, scroll, wrap, opacity) \
482 just,\
483 pd,\
484 scroll,\
485 CEA708_WA_DIRECTION_LTR,\
486 wrap,\
487 CEA708_WA_EFFECT_SNAP,\
489 0x00,\
490 opacity,\
491 CEA708_EDGE_NONE,\
492 0x00,\
494 static const cea708_window_style_t cea708_default_window_styles[CEA708_PREDEFINED_STYLES] =
496 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
497 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID),
498 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
499 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_TRANSPARENT),
500 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
501 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID),
502 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
503 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID),
504 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
505 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_TRANSPARENT),
506 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
507 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID),
508 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_TB,
509 CEA708_WA_DIRECTION_RTL, false, CEA708_OPACITY_SOLID),
511 #undef DEFAULT_NTSC_WA_STYLE
513 static void CEA708_Window_Init( cea708_window_t *p_w )
515 memset( p_w, 0, sizeof(*p_w) );
516 p_w->style = cea708_default_window_styles[0];
517 p_w->pen = cea708_default_pen_styles[0];
518 p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
519 p_w->b_row_lock = true;
520 p_w->b_column_lock = true;
523 static void CEA708_Window_ClearText( cea708_window_t *p_w )
525 for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ )
527 cea708_text_row_Delete( p_w->rows[i] );
528 p_w->rows[i] = NULL;
530 p_w->i_lastrow = 0;
531 p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
534 static void CEA708_Window_Reset( cea708_window_t *p_w )
536 CEA708_Window_ClearText( p_w );
537 CEA708_Window_Init( p_w );
540 static bool CEA708_Window_BreaksSpace( const cea708_window_t *p_w )
542 return true;
543 if( p_w->style.print_direction == CEA708_WA_DIRECTION_LTR &&
544 p_w->style.justify == CEA708_WA_JUSTIFY_LEFT )
545 return true;
547 if( p_w->style.print_direction == CEA708_WA_DIRECTION_RTL &&
548 p_w->style.justify == CEA708_WA_JUSTIFY_RIGHT )
549 return true;
551 return false;
554 static uint8_t CEA708_Window_MinCol( const cea708_window_t *p_w )
556 uint8_t i_min = CEA708_WINDOW_MAX_COLS;
557 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
559 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
560 if( p_row && p_row->firstcol < i_min )
561 i_min = p_row->firstcol;
563 return i_min;
566 static uint8_t CEA708_Window_MaxCol( const cea708_window_t *p_w )
568 uint8_t i_max = 0;
569 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
571 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
572 if( p_row && p_row->lastcol > i_max )
573 i_max = p_row->lastcol;
575 return i_max;
578 static uint8_t CEA708_Window_ColCount( const cea708_window_t *p_w )
580 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
581 if( !p_row || p_row->firstcol > p_row->lastcol )
582 return 0;
583 return 1 + p_row->lastcol - p_row->firstcol;
586 static uint8_t CEA708_Window_RowCount( const cea708_window_t *p_w )
588 if( p_w->i_firstrow > p_w->i_lastrow )
589 return 0;
590 return 1 + p_w->i_lastrow - p_w->i_firstrow;
593 static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
595 switch( i_direction )
597 case CEA708_WA_DIRECTION_LTR: /* Deletes all most right col */
599 uint8_t i_max = CEA708_Window_MaxCol( p_w );
600 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
602 cea708_text_row_t *row = p_w->rows[i];
603 if( row->lastcol == i_max )
605 if( row->firstcol >= row->lastcol )
607 cea708_text_row_Delete( row );
608 p_w->rows[i] = NULL;
609 if( i == p_w->i_firstrow )
610 p_w->i_firstrow++;
611 else if( i == p_w->i_lastrow )
612 p_w->i_lastrow--;
617 break;
618 case CEA708_WA_DIRECTION_RTL: /* Deletes all most left col */
620 uint8_t i_min = CEA708_Window_MinCol( p_w );
621 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
623 cea708_text_row_t *row = p_w->rows[i];
624 if( row->firstcol == i_min )
626 if( row->firstcol >= row->lastcol )
628 cea708_text_row_Delete( row );
629 p_w->rows[i] = NULL;
630 if( i == p_w->i_firstrow )
631 p_w->i_firstrow++;
632 else if( i == p_w->i_lastrow )
633 p_w->i_lastrow--;
638 break;
639 case CEA708_WA_DIRECTION_TB: /* Deletes LAST row */
640 if( CEA708_Window_RowCount( p_w ) > 0 )
642 cea708_text_row_Delete( p_w->rows[p_w->i_lastrow] );
643 p_w->rows[p_w->i_lastrow--] = NULL;
645 break;
646 case CEA708_WA_DIRECTION_BT: /* Deletes First row */
647 if( CEA708_Window_RowCount( p_w ) > 0 )
649 cea708_text_row_Delete( p_w->rows[p_w->i_firstrow] );
650 p_w->rows[p_w->i_firstrow++] = NULL;
652 break;
656 static void CEA708_Window_Scroll( cea708_window_t *p_w )
658 if( CEA708_Window_RowCount( p_w ) == 0 )
659 return;
661 switch( p_w->style.scroll_direction )
663 case CEA708_WA_DIRECTION_LTR:
664 /* Move RIGHT */
665 if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_ROWS - 1 )
666 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR );
667 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
669 cea708_text_row_t *row = p_w->rows[i];
670 if( row->lastcol < row->firstcol ) /* should not happen */
671 continue;
672 memmove( &row->characters[row->firstcol + 1], &row->characters[row->firstcol],
673 (row->lastcol - row->firstcol + 1) * 4U );
674 memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol],
675 (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
676 row->firstcol++;
677 row->lastcol++;
679 break;
680 case CEA708_WA_DIRECTION_RTL:
681 /* Move LEFT */
682 if( CEA708_Window_MinCol( p_w ) == 0 )
683 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_RTL );
684 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
686 cea708_text_row_t *row = p_w->rows[i];
687 if( row->lastcol < row->firstcol ) /* should not happen */
688 continue;
689 memmove( &row->characters[row->firstcol - 1], &row->characters[row->firstcol],
690 (row->lastcol - row->firstcol + 1) * 4U );
691 memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol],
692 (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
693 row->firstcol--;
694 row->lastcol--;
696 break;
697 case CEA708_WA_DIRECTION_TB:
698 /* Move DOWN */
699 if( p_w->i_lastrow == CEA708_WINDOW_MAX_ROWS - 1 )
700 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_TB );
701 for( int i=p_w->i_lastrow; i >= p_w->i_firstrow; i-- )
702 p_w->rows[i+1] = p_w->rows[i];
703 p_w->rows[p_w->i_firstrow] = NULL;
704 p_w->i_firstrow++;
705 p_w->i_lastrow++;
706 break;
707 case CEA708_WA_DIRECTION_BT:
708 /* Move UP */
709 if( p_w->i_firstrow == 0 )
710 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_BT );
711 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
712 p_w->rows[i-1] = p_w->rows[i];
713 p_w->rows[p_w->i_lastrow] = NULL;
714 p_w->i_firstrow--;
715 p_w->i_lastrow--;
716 break;
720 static void CEA708_Window_CarriageReturn( cea708_window_t *p_w )
722 switch( p_w->style.scroll_direction )
724 case CEA708_WA_DIRECTION_LTR:
725 if( p_w->col > 0 &&
726 CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
727 p_w->col--;
728 else
729 CEA708_Window_Scroll( p_w );
730 p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
731 0 : CEA708_WINDOW_MAX_ROWS - 1;
732 break;
733 case CEA708_WA_DIRECTION_RTL:
734 if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS &&
735 CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
736 p_w->col++;
737 else
738 CEA708_Window_Scroll( p_w );
739 p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
740 0 : CEA708_WINDOW_MAX_ROWS - 1;
741 break;
742 case CEA708_WA_DIRECTION_TB:
743 if( p_w->row > 0 &&
744 CEA708_Window_RowCount( p_w ) < p_w->i_row_count )
745 p_w->row--;
746 else
747 CEA708_Window_Scroll( p_w );
748 p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
749 0 : CEA708_WINDOW_MAX_COLS - 1;
750 break;
751 case CEA708_WA_DIRECTION_BT:
752 if( p_w->row + 1 < p_w->i_row_count )
753 p_w->row++;
754 else
755 CEA708_Window_Scroll( p_w );
756 p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
757 0 : CEA708_WINDOW_MAX_COLS - 1;
758 break;
762 static void CEA708_Window_Forward( cea708_window_t *p_w )
764 switch( p_w->style.print_direction )
766 case CEA708_WA_DIRECTION_LTR:
767 if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS )
768 p_w->col++;
769 else
770 CEA708_Window_CarriageReturn( p_w );
771 break;
772 case CEA708_WA_DIRECTION_RTL:
773 if( p_w->col > 0 )
774 p_w->col--;
775 else
776 CEA708_Window_CarriageReturn( p_w );
777 break;
778 case CEA708_WA_DIRECTION_TB:
779 if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS )
780 p_w->row++;
781 else
782 CEA708_Window_CarriageReturn( p_w );
783 break;
784 case CEA708_WA_DIRECTION_BT:
785 if( p_w->row > 0 )
786 p_w->row--;
787 else
788 CEA708_Window_CarriageReturn( p_w );
789 break;
793 static void CEA708_Window_Backward( cea708_window_t *p_w )
795 static const int reverse[] =
797 [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL,
798 [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR,
799 [CEA708_WA_DIRECTION_TB] = CEA708_WA_DIRECTION_BT,
800 [CEA708_WA_DIRECTION_BT] = CEA708_WA_DIRECTION_TB,
802 int save = p_w->style.print_direction;
803 p_w->style.print_direction = reverse[p_w->style.print_direction];
804 CEA708_Window_Forward( p_w );
805 p_w->style.print_direction = save;
808 static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w )
810 if( !p_w->b_defined )
811 return;
814 if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS ||
815 p_w->col >= CEA708_WINDOW_MAX_COLS ) )
817 assert( p_w->row < CEA708_WINDOW_MAX_ROWS );
818 assert( p_w->col < CEA708_WINDOW_MAX_COLS );
819 return;
822 cea708_text_row_t *p_row = p_w->rows[p_w->row];
823 if( !p_row )
825 p_w->rows[p_w->row] = p_row = cea708_text_row_New();
826 if( !p_row )
827 return;
828 if( p_w->row < p_w->i_firstrow )
829 p_w->i_firstrow = p_w->row;
830 if( p_w->row > p_w->i_lastrow )
831 p_w->i_lastrow = p_w->row;
834 memcpy( &p_row->characters[p_w->col * 4U], c, 4 );
835 p_row->styles[p_w->col] = p_w->pen;
836 if( p_w->col < p_row->firstcol )
837 p_row->firstcol = p_w->col;
838 if( p_w->col > p_row->lastcol )
839 p_row->lastcol = p_w->col;
841 CEA708_Window_Forward( p_w );
843 Debug(printf("\033[0;33m%s\033[0m", c));
846 static uint32_t CEA708ColorConvert( uint8_t c )
848 const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF};
849 c = c & 0x3F;
850 return (value[(c >> 4) & 0x03] << 16) |
851 (value[(c >> 2) & 0x03] << 8) |
852 value[c & 0x03];
855 static uint8_t CEA708AlphaConvert( uint8_t c )
857 if( c == CEA708_OPACITY_TRANSLUCENT )
858 return STYLE_ALPHA_OPAQUE / 2;
859 else if( c == CEA708_OPACITY_TRANSPARENT )
860 return STYLE_ALPHA_TRANSPARENT;
861 else
862 return STYLE_ALPHA_OPAQUE;
865 static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s )
867 if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT )
869 s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity );
870 s->i_style_flags |= STYLE_BACKGROUND;
871 s->i_background_color = CEA708ColorConvert( ps->background.color );
872 s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA;
873 if( ps->background.opacity == CEA708_OPACITY_FLASH )
874 s->i_style_flags |= STYLE_BLINK_BACKGROUND;
876 s->i_font_color = CEA708ColorConvert( ps->foreground.color );
877 s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity );
878 s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR;
879 if( ps->foreground.opacity == CEA708_OPACITY_FLASH )
880 s->i_style_flags |= STYLE_BLINK_FOREGROUND;
882 if( ps->b_italics )
883 s->i_style_flags |= STYLE_ITALIC;
884 if( ps->b_underline )
885 s->i_style_flags |= STYLE_UNDERLINE;
887 switch( ps->font )
889 default:
890 case CEA708_FONT_UNDEFINED:
891 case CEA708_FONT_MONOSPACED:
892 case CEA708_FONT_MONO_SANS_SERIF:
893 s->i_style_flags |= STYLE_MONOSPACED;
894 break;
895 case CEA708_FONT_PROP:
896 case CEA708_FONT_PROP_SANS_SERIF:
897 case CEA708_FONT_CASUAL:
898 case CEA708_FONT_CURSIVE:
899 case CEA708_FONT_SMALL_CAPS:
900 break;
903 switch( ps->size )
905 case CEA708_PEN_SIZE_SMALL:
906 s->f_font_relsize = CEA708_FONTRELSIZE_SMALL;
907 break;
908 case CEA708_PEN_SIZE_LARGE:
909 s->f_font_relsize = CEA708_FONTRELSIZE_LARGE;
910 break;
911 default:
912 s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD;
913 break;
917 static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row,
918 uint8_t i_start, uint8_t i_end,
919 bool b_newline )
921 text_segment_t *p_segment = text_segment_New( NULL );
922 if( !p_segment )
923 return NULL;
925 p_segment->style = text_style_Create( STYLE_NO_DEFAULTS );
926 if( p_segment->style )
927 CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style );
929 p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U );
930 if( !p_segment->psz_text )
932 text_segment_Delete( p_segment );
933 return NULL;
936 size_t offsetw = 0;
937 for( uint8_t i=i_start; i<=i_end; i++ )
939 for( size_t j=0; j<4; j++ )
941 if( p_row->characters[i * 4 + j] != 0 )
942 p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j];
943 else if( j == 0 )
944 p_segment->psz_text[offsetw++] = ' ';
945 else
946 break;
950 if( b_newline )
951 p_segment->psz_text[offsetw++] = '\n';
952 p_segment->psz_text[offsetw] = '\0';
954 return p_segment;
957 static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row,
958 bool b_addnewline )
960 text_segment_t *p_segments = NULL;
961 text_segment_t **pp_last = &p_segments;
963 uint8_t i_start = p_row->firstcol;
964 for( uint8_t i=i_start; i<=p_row->lastcol; i++ )
966 if( i == p_row->lastcol ||
967 memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) )
969 *pp_last = CEA708CharsToSegment( p_row, i_start, i,
970 b_addnewline && (i == p_row->lastcol) );
971 if( *pp_last )
972 pp_last = &((*pp_last)->p_next);
973 i_start = i+1;
977 return p_segments;
980 static void CEA708SpuConvert( const cea708_window_t *p_w,
981 substext_updater_region_t *p_region )
983 if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 )
984 return;
986 if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) )
987 return;
989 int first, last;
991 if (p_w->style.scroll_direction == CEA708_WA_DIRECTION_BT) {
992 /* BT is a bit of a special case since we need to grab the last N
993 rows between first and last, rather than the first... */
994 last = p_w->i_lastrow;
995 if (p_w->i_lastrow - p_w->i_row_count < p_w->i_firstrow)
996 first = p_w->i_firstrow;
997 else
998 first = p_w->i_lastrow - p_w->i_row_count + 1;
1000 } else {
1001 first = p_w->i_firstrow;
1002 if (p_w->i_firstrow + p_w->i_row_count > p_w->i_lastrow)
1003 last = p_w->i_lastrow;
1004 else
1005 last = p_w->i_firstrow + p_w->i_row_count - 1;
1008 text_segment_t **pp_last = &p_region->p_segments;
1009 for( uint8_t i=first; i<=last; i++ )
1011 if( !p_w->rows[i] )
1012 continue;
1014 *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow );
1015 if( *pp_last )
1016 pp_last = &((*pp_last)->p_next);
1019 if( p_w->b_relative )
1021 /* FIXME: take into account left/right anchors */
1022 p_region->origin.x = p_w->i_anchor_offset_h / 100.0;
1024 switch (p_w->anchor_point) {
1025 case CEA708_ANCHOR_TOP_LEFT:
1026 case CEA708_ANCHOR_TOP_CENTER:
1027 case CEA708_ANCHOR_TOP_RIGHT:
1028 p_region->origin.y = p_w->i_anchor_offset_v / 100.0;
1029 break;
1030 case CEA708_ANCHOR_BOTTOM_LEFT:
1031 case CEA708_ANCHOR_BOTTOM_CENTER:
1032 case CEA708_ANCHOR_BOTTOM_RIGHT:
1033 p_region->origin.y = 1.0 - (p_w->i_anchor_offset_v / 100.0);
1034 break;
1035 default:
1036 /* FIXME: for CENTER vertical justified, just position as top */
1037 p_region->origin.y = p_w->i_anchor_offset_v / 100.0;
1038 break;
1041 else
1043 p_region->origin.x = (float)p_w->i_anchor_offset_h / CEA708_SCREEN_COLS_169;
1044 p_region->origin.y = (float)p_w->i_anchor_offset_v /
1045 (CEA708_SCREEN_ROWS * CEA708_FONT_TO_LINE_HEIGHT_RATIO);
1047 p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO;
1049 if( p_w->i_firstrow <= p_w->i_lastrow )
1051 p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD;
1052 /*const uint8_t i_min = CEA708_Window_MinCol( p_w );
1053 if( i_min < CEA708_WINDOW_MAX_COLS )
1054 p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/
1057 if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT )
1059 static const int vlc_subpicture_aligns[] =
1061 [CEA708_ANCHOR_TOP_LEFT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
1062 [CEA708_ANCHOR_TOP_CENTER] = SUBPICTURE_ALIGN_TOP,
1063 [CEA708_ANCHOR_TOP_RIGHT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT,
1064 [CEA708_ANCHOR_CENTER_LEFT] = SUBPICTURE_ALIGN_LEFT,
1065 [CEA708_ANCHOR_CENTER_CENTER] = 0,
1066 [CEA708_ANCHOR_CENTER_RIGHT] = SUBPICTURE_ALIGN_RIGHT,
1067 [CEA708_ANCHOR_BOTTOM_LEFT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT,
1068 [CEA708_ANCHOR_BOTTOM_CENTER] = SUBPICTURE_ALIGN_BOTTOM,
1069 [CEA708_ANCHOR_BOTTOM_RIGHT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
1071 p_region->align = vlc_subpicture_aligns[p_w->anchor_point];
1073 p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT;
1076 static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 )
1078 subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec );
1079 if( !p_spu )
1080 return NULL;
1082 subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
1083 substext_updater_region_t *p_region = &p_spu_sys->region;
1085 p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO;
1087 bool first = true;
1089 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1091 cea708_window_t *p_w = &p_cea708->window[i];
1092 if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) )
1094 if( !first )
1096 substext_updater_region_t *p_newregion =
1097 SubpictureUpdaterSysRegionNew();
1098 if( p_newregion == NULL )
1099 break;
1100 SubpictureUpdaterSysRegionAdd( p_region, p_newregion );
1101 p_region = p_newregion;
1103 first = false;
1105 /* Fill region */
1106 CEA708SpuConvert( p_w, p_region );
1110 p_spu->i_start = p_cea708->i_clock;
1111 p_spu->i_stop = p_cea708->i_clock + VLC_TICK_FROM_SEC(10); /* 10s max */
1113 p_spu->b_ephemer = true;
1114 p_spu->b_absolute = false;
1115 p_spu->b_subtitle = true;
1117 return p_spu;
1120 static void CEA708_Decoder_Init( cea708_t *p_cea708 )
1122 cea708_input_buffer_init( &p_cea708->input_buffer );
1123 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1124 CEA708_Window_Init( &p_cea708->window[i] );
1125 p_cea708->p_cw = &p_cea708->window[0];
1126 p_cea708->suspended_deadline = VLC_TICK_INVALID;
1127 p_cea708->b_text_waiting = false;
1128 p_cea708->i_clock = 0;
1131 static void CEA708_Decoder_Reset( cea708_t *p_cea708 )
1133 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1134 CEA708_Window_Reset( &p_cea708->window[i] );
1135 CEA708_Decoder_Init( p_cea708 );
1138 void CEA708_Decoder_Flush( cea708_t *p_cea708 )
1140 CEA708_Decoder_Reset( p_cea708 );
1143 void CEA708_Decoder_Release( cea708_t *p_cea708 )
1145 CEA708_Decoder_Reset( p_cea708 );
1146 free( p_cea708 );
1149 cea708_t * CEA708_Decoder_New( decoder_t *p_dec )
1151 cea708_t *p_cea708 = malloc( sizeof(cea708_t) );
1152 if( p_cea708 )
1154 CEA708_Decoder_Init( p_cea708 );
1155 p_cea708->p_dec = p_dec;
1157 return p_cea708;
1160 #define POP_COMMAND() (void) cea708_input_buffer_get( ib )
1161 #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND()
1162 #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\
1163 return CEA708_STATUS_STARVING
1164 #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND()
1166 static void CEA708_Output( cea708_t *p_cea708 )
1168 Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708->i_clock)));
1169 subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 );
1170 if( p_spu )
1171 decoder_QueueSub( p_cea708->p_dec, p_spu );
1174 static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 )
1176 uint8_t v, i;
1177 uint16_t u16;
1178 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1179 int i_ret = CEA708_STATUS_OK;
1181 switch( code )
1183 case CEA708_C0_NUL:
1184 POP_COMMAND();
1185 break;
1186 case CEA708_C0_ETX:
1187 POP_COMMAND();
1188 if( p_cea708->b_text_waiting )
1190 i_ret |= CEA708_STATUS_OUTPUT;
1191 p_cea708->b_text_waiting = false;
1193 break;
1194 case CEA708_C0_BS:
1195 POP_COMMAND();
1196 if( !p_cea708->p_cw->b_defined )
1197 break;
1198 CEA708_Window_Backward( p_cea708->p_cw );
1199 p_cea708->b_text_waiting = true;
1200 break;
1201 case CEA708_C0_FF:
1202 POP_COMMAND();
1203 if( !p_cea708->p_cw->b_defined )
1204 break;
1205 CEA708_Window_ClearText( p_cea708->p_cw );
1206 p_cea708->p_cw->col = 0;
1207 p_cea708->p_cw->row = 0;
1208 p_cea708->b_text_waiting = true;
1209 break;
1210 case CEA708_C0_CR:
1211 POP_COMMAND();
1212 if( !p_cea708->p_cw->b_defined )
1213 break;
1214 if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL )
1216 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1217 if( p_cea708->p_cw->b_visible )
1218 i_ret |= CEA708_STATUS_OUTPUT;
1220 break;
1221 case CEA708_C0_HCR:
1222 POP_COMMAND();
1223 if( !p_cea708->p_cw->b_defined )
1224 break;
1225 if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL )
1227 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1228 if( p_cea708->p_cw->b_visible )
1229 i_ret |= CEA708_STATUS_OUTPUT;
1231 break;
1232 case CEA708_C0_EXT1: /* Special extended table case */
1233 if( cea708_input_buffer_size( ib ) >= 2 )
1235 v = cea708_input_buffer_peek( ib, 1 );
1236 /* C2 extended code set */
1237 if( v < 0x20 )
1239 if( v > 0x17 )
1240 i = 3;
1241 else if( v > 0x0f )
1242 i = 2;
1243 else if( v > 0x07 )
1244 i = 1;
1245 else
1246 i = 0;
1247 if( cea708_input_buffer_size( ib ) < 2 + i )
1248 return CEA708_STATUS_STARVING;
1249 POP_COMMAND();
1250 POP_ARGS(1 + i);
1252 /* C3 extended code set */
1253 else if( v > 0x7f && v < 0xa0 )
1255 if( v > 0x87 )
1256 i = 5;
1257 else
1258 i = 4;
1259 if( cea708_input_buffer_size( ib ) < 2 + i )
1260 return CEA708_STATUS_STARVING;
1261 POP_COMMAND();
1262 POP_ARGS(1 + i);
1264 else
1266 POP_COMMAND();
1267 v = cea708_input_buffer_get( ib );
1268 if( p_cea708->p_cw->b_defined )
1269 i_ret |= CEA708_Decode_G2G3( v, p_cea708 );
1272 else return CEA708_STATUS_STARVING;
1273 break;
1274 case CEA708_C0_P16:
1275 REQUIRE_ARGS_AND_POP_COMMAND(2);
1276 u16 = cea708_input_buffer_get( ib ) << 8;
1277 u16 |= cea708_input_buffer_get( ib );
1278 i_ret |= CEA708_Decode_P16( u16, p_cea708 );
1279 Debug(printf("[P16 %x]", u16));
1280 break;
1281 default:
1282 POP_COMMAND();
1283 Debug(printf("[UNK %2.2x]", code));
1284 break;
1286 Debug(printf("[C0 %x]", code));
1287 return i_ret;
1290 static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 )
1292 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1293 POP_COMMAND();
1294 int i_ret = CEA708_STATUS_OK;
1296 if( !p_cea708->p_cw->b_defined )
1297 return i_ret;
1299 uint8_t utf8[4] = {code,0x00,0x00,0x00};
1301 if(code == 0x7F) // Music note
1303 utf8[0] = 0xe2;
1304 utf8[1] = 0x99;
1305 utf8[2] = 0xaa;
1308 CEA708_Window_Write( utf8, p_cea708->p_cw );
1310 if( code == 0x20 &&
1311 p_cea708->b_text_waiting &&
1312 CEA708_Window_BreaksSpace( p_cea708->p_cw ) )
1314 i_ret |= CEA708_STATUS_OUTPUT;
1318 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1320 return i_ret;
1323 static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 )
1325 uint8_t v, i;
1326 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1327 int i_ret = CEA708_STATUS_OK;
1329 if( p_cea708->b_text_waiting )
1331 i_ret |= CEA708_STATUS_OUTPUT;
1332 p_cea708->b_text_waiting = false;
1335 switch( code )
1337 case CEA708_C1_CLW:
1338 REQUIRE_ARGS_AND_POP_COMMAND(1);
1339 Debug(printf("[CLW"));
1340 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1341 if( v & 1 )
1343 if( p_cea708->window[i].b_defined &&
1344 p_cea708->window[i].b_visible )
1345 i_ret |= CEA708_STATUS_OUTPUT;
1346 CEA708_Window_ClearText( &p_cea708->window[i] );
1347 Debug(printf("%d", i));
1349 Debug(printf("]"));
1350 break;
1351 case CEA708_C1_DSW:
1352 REQUIRE_ARGS_AND_POP_COMMAND(1);
1353 Debug(printf("[DSW"));
1354 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1355 if( v & 1 )
1357 if( p_cea708->window[i].b_defined )
1359 if( !p_cea708->window[i].b_visible )
1360 i_ret |= CEA708_STATUS_OUTPUT;
1361 p_cea708->window[i].b_visible = true;
1363 Debug(printf("%d", i));
1365 Debug(printf("]"));
1366 break;
1367 case CEA708_C1_HDW:
1368 REQUIRE_ARGS_AND_POP_COMMAND(1);
1369 Debug(printf("[HDW"));
1370 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1371 if( v & 1 )
1373 if( p_cea708->window[i].b_defined )
1375 if( p_cea708->window[i].b_visible )
1376 i_ret |= CEA708_STATUS_OUTPUT;
1377 p_cea708->window[i].b_visible = false;
1379 Debug(printf("%d", i));
1381 Debug(printf("]"));
1382 break;
1383 case CEA708_C1_TGW:
1384 REQUIRE_ARGS_AND_POP_COMMAND(1);
1385 Debug(printf("[TGW"));
1386 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1387 if( v & 1 )
1389 if( p_cea708->window[i].b_defined )
1391 i_ret |= CEA708_STATUS_OUTPUT;
1392 p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible;
1394 Debug(printf("%d", i));
1396 Debug(printf("]"));
1397 break;
1398 case CEA708_C1_DLW:
1399 REQUIRE_ARGS_AND_POP_COMMAND(1);
1400 Debug(printf("[DLW"));
1401 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1402 if( v & 1 )
1404 if( p_cea708->window[i].b_defined )
1406 if( p_cea708->window[i].b_visible )
1407 i_ret |= CEA708_STATUS_OUTPUT;
1408 CEA708_Window_Reset( &p_cea708->window[i] );
1410 Debug(printf("%d", i));
1412 Debug(printf("]"));
1413 break;
1414 case CEA708_C1_DLY:
1415 REQUIRE_ARGS_AND_POP_COMMAND(1);
1416 p_cea708->suspended_deadline = p_cea708->i_clock +
1417 VLC_TICK_FROM_MS( cea708_input_buffer_get( ib ) * 100 );
1418 Debug(printf("[DLY]"));
1419 break;
1420 case CEA708_C1_DLC:
1421 POP_COMMAND();
1422 p_cea708->suspended_deadline = VLC_TICK_INVALID;
1423 Debug(printf("[DLC]"));
1424 break;
1425 case CEA708_C1_RST:
1426 POP_COMMAND();
1427 i_ret |= CEA708_STATUS_OUTPUT;
1428 /* FIXME */
1429 break;
1430 case CEA708_C1_SPA:
1431 REQUIRE_ARGS_AND_POP_COMMAND(2);
1432 if( !p_cea708->p_cw->b_defined )
1434 POP_ARGS(2);
1435 break;
1437 v = cea708_input_buffer_get( ib );
1438 p_cea708->p_cw->pen.text_tag = v >> 4;
1439 p_cea708->p_cw->pen.offset = (v >> 2) & 0x03;
1440 p_cea708->p_cw->pen.size = v & 0x03;
1441 v = cea708_input_buffer_get( ib );
1442 p_cea708->p_cw->pen.b_italics = v & 0x80;
1443 p_cea708->p_cw->pen.b_underline = v & 0x40;
1444 p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07;
1445 p_cea708->p_cw->pen.font = v & 0x07;
1446 Debug(printf("[SPA]"));
1447 break;
1448 case CEA708_C1_SPC:
1449 REQUIRE_ARGS_AND_POP_COMMAND(3);
1450 if( !p_cea708->p_cw->b_defined )
1452 POP_ARGS(3);
1453 break;
1455 v = cea708_input_buffer_get( ib );
1456 p_cea708->p_cw->pen.foreground.opacity = v >> 6;
1457 p_cea708->p_cw->pen.foreground.color = v & 0x3F;
1458 v = cea708_input_buffer_get( ib );
1459 p_cea708->p_cw->pen.background.opacity = v >> 6;
1460 p_cea708->p_cw->pen.background.color = v & 0x3F;
1461 v = cea708_input_buffer_get( ib );
1462 p_cea708->p_cw->pen.edge_color = v & 0x3F;
1463 Debug(printf("[SPC]"));
1464 break;
1465 case CEA708_C1_SPL:
1466 REQUIRE_ARGS_AND_POP_COMMAND(2);
1467 if( !p_cea708->p_cw->b_defined )
1469 POP_ARGS(2);
1470 break;
1472 v = cea708_input_buffer_get( ib );
1473 p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS;
1474 v = cea708_input_buffer_get( ib );
1475 p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS;
1476 Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col));
1477 break;
1478 case CEA708_C1_SWA:
1479 REQUIRE_ARGS_AND_POP_COMMAND(4);
1480 if( !p_cea708->p_cw->b_defined )
1482 POP_ARGS(4);
1483 break;
1485 v = cea708_input_buffer_get( ib );
1486 p_cea708->p_cw->style.fill_opacity = v >> 6;
1487 p_cea708->p_cw->style.fill_color_color = v & 0x3F;
1488 v = cea708_input_buffer_get( ib );
1489 p_cea708->p_cw->style.border_color_color = v & 0x3F;
1490 p_cea708->p_cw->style.border_type = v >> 6;
1491 v = cea708_input_buffer_get( ib );
1492 p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5);
1493 p_cea708->p_cw->style.b_word_wrap = v & 0x40;
1494 p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03;
1495 p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03;
1496 p_cea708->p_cw->style.justify = v & 0x03;
1497 v = cea708_input_buffer_get( ib );
1498 p_cea708->p_cw->style.effect_speed = v >> 4;
1499 p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03;
1500 p_cea708->p_cw->style.display_effect = v & 0x03;
1501 Debug(printf("[SWA]"));
1502 break;
1504 default:
1505 if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 )
1507 POP_COMMAND();
1508 Debug(printf("[CW%d]", code - CEA708_C1_CW0));
1509 if( p_cea708->window[code - CEA708_C1_CW0].b_defined )
1510 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0];
1512 else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 )
1514 REQUIRE_ARGS_AND_POP_COMMAND(6);
1515 Debug(printf("[DF%d]", code - CEA708_C1_DF0));
1516 /* also sets current window */
1517 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0];
1518 v = cea708_input_buffer_get( ib );
1519 if( p_cea708->p_cw->b_defined &&
1520 !p_cea708->p_cw->b_visible != !(v & 0x20) )
1521 i_ret |= CEA708_STATUS_OUTPUT;
1522 p_cea708->p_cw->b_visible = v & 0x20;
1523 p_cea708->p_cw->b_row_lock = v & 0x10;
1524 p_cea708->p_cw->b_column_lock = v & 0x08;
1525 p_cea708->p_cw->i_priority = v & 0x07;
1526 v = cea708_input_buffer_get( ib );
1527 p_cea708->p_cw->b_relative = v & 0x80;
1528 p_cea708->p_cw->i_anchor_offset_v = v & 0x7F;
1529 v = cea708_input_buffer_get( ib );
1530 p_cea708->p_cw->i_anchor_offset_h = v;
1531 v = cea708_input_buffer_get( ib );
1532 p_cea708->p_cw->anchor_point = v >> 4;
1533 p_cea708->p_cw->i_row_count = (v & 0x0F) + 1;
1534 v = cea708_input_buffer_get( ib );
1535 p_cea708->p_cw->i_col_count = v & 0x3F;
1536 v = cea708_input_buffer_get( ib );
1537 /* zero values style set on init, avoid dealing with updt case */
1538 i = (v >> 3) & 0x07; /* Window style id */
1539 if( i > 0 && !p_cea708->p_cw->b_defined )
1540 p_cea708->p_cw->style = cea708_default_window_styles[i];
1541 i = v & 0x07; /* Pen style id */
1542 if( i > 0 && !p_cea708->p_cw->b_defined )
1543 p_cea708->p_cw->pen = cea708_default_pen_styles[i];
1544 p_cea708->p_cw->b_defined = true;
1546 else
1548 Debug(printf("{%2.2x}", code));
1549 POP_COMMAND();
1553 return i_ret;
1556 static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 )
1558 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1559 POP_COMMAND();
1561 if( !p_cea708->p_cw->b_defined )
1562 return CEA708_STATUS_OK;
1564 uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6,
1565 0x80 | (code & 0x3f),
1566 0, 0};
1568 CEA708_Window_Write( utf8, p_cea708->p_cw );
1569 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1571 return CEA708_STATUS_OK;
1574 static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 )
1576 if( !p_cea708->p_cw->b_defined )
1577 return CEA708_STATUS_OK;
1579 uint8_t out[4] = { '?', 0, 0, 0 };
1580 static const struct {
1581 uint8_t c;
1582 uint8_t utf8[4];
1583 } code2utf8[] = {
1584 /* G2 */
1585 { 0x20, { 0x20 } },// transparent space [*** will need special handling]
1586 { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling]
1587 { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS
1588 { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON
1589 { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE
1590 { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK
1591 { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK
1592 { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK
1593 { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK
1594 { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK
1595 { 0x35, { 0xe2,0x80,0xa2 } },// BULLET
1596 { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM)
1597 { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON
1598 { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE
1599 { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK
1600 { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS
1601 { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH
1602 { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS
1603 { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS
1604 { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS
1605 { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL
1606 { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT
1607 { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT
1608 { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL
1609 { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT
1610 { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT
1611 /* G3 */
1612 { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C)
1615 for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ )
1617 if( code2utf8[i].c == code )
1619 memcpy( out, code2utf8[i].utf8, 4 );
1620 if(out[0] < 0xf0)
1622 if(out[0] < 0x80)
1623 out[1] = 0;
1624 else if(out[0] < 0xe0)
1625 out[2] = 0;
1626 else
1627 out[3] = 0;
1629 break;
1633 CEA708_Window_Write( out, p_cea708->p_cw );
1635 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1637 return CEA708_STATUS_OK;
1640 static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 )
1642 if( !p_cea708->p_cw->b_defined )
1643 return CEA708_STATUS_OK;
1645 uint8_t out[4] = { '?', 0, 0, 0 };
1647 /* adapted from codepoint conversion from strings.h */
1648 if( ucs2 <= 0x7F )
1650 out[0] = ucs2;
1652 else if( ucs2 <= 0x7FF )
1654 out[0] = 0xC0 | (ucs2 >> 6);
1655 out[1] = 0x80 | (ucs2 & 0x3F);
1657 else
1659 out[0] = 0xE0 | (ucs2 >> 12);
1660 out[1] = 0x80 | ((ucs2 >> 6) & 0x3F);
1661 out[2] = 0x80 | (ucs2 & 0x3F);
1664 CEA708_Window_Write( out, p_cea708->p_cw );
1666 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1668 return CEA708_STATUS_OK;
1671 static void CEA708_Decode_ServiceBuffer( cea708_t *h )
1673 for( ;; )
1675 const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer );
1676 if( i_in == 0 )
1677 break;
1679 int i_ret;
1680 uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 );
1682 if( c < 0x20 )
1683 i_ret = CEA708_Decode_C0( c, h );
1684 else if( c <= 0x7F )
1685 i_ret = CEA708_Decode_G0( c, h );
1686 else if( c <= 0x9F )
1687 i_ret = CEA708_Decode_C1( c, h );
1688 else
1689 i_ret = CEA708_Decode_G1( c, h );
1691 if( i_ret & CEA708_STATUS_OUTPUT )
1692 CEA708_Output( h );
1694 if( i_ret & CEA708_STATUS_STARVING )
1695 break;
1697 /* Update internal clock */
1698 const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer );
1699 if( i_consumed )
1700 h->i_clock += vlc_tick_from_samples(1, 9600) * i_consumed;
1704 void CEA708_Decoder_Push( cea708_t *h, vlc_tick_t i_time,
1705 const uint8_t *p_data, size_t i_data )
1707 /* Set new buffer start time */
1708 h->i_clock = i_time;
1710 for( size_t i=0; i<i_data; )
1712 /* Never push more than buffer */
1713 size_t i_push = cea708_input_buffer_remain(&h->input_buffer);
1714 if( (i_data - i) < i_push )
1715 i_push = (i_data - i);
1716 else
1717 h->suspended_deadline = VLC_TICK_INVALID; /* Full buffer cancels pause */
1719 for( size_t j=0; j<i_push; j++ )
1721 uint8_t byte = p_data[i+j];
1722 cea708_input_buffer_add( &h->input_buffer, byte );
1725 if( h->suspended_deadline != VLC_TICK_INVALID )
1727 /* Decoding is paused */
1728 if ( h->suspended_deadline > h->i_clock )
1730 /* Increase internal clock */
1731 if( i_push )
1732 h->i_clock += vlc_tick_from_samples(1, 1200) * i_push;
1733 continue;
1735 h->suspended_deadline = VLC_TICK_INVALID;
1738 /* Decode Buffer */
1739 CEA708_Decode_ServiceBuffer( h );
1741 i += i_push;