codec: cea708: clear warnings
[vlc.git] / modules / codec / cea708.c
blob51951bd0bb04ae032b5f2f184e4727411c10992a
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 mtime_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, mtime_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 if( i_data < 2 )
91 return;
92 i_sid = p_data[1] & 0x3F;
93 if( i_sid < 0x07 )
94 return;
95 p_data += 1; i_data -= 1;
97 p_data += 1; i_data -= 1;
99 h->p_callback( h->priv, i_sid, i_start, p_data, i_block_size );
101 p_data += i_block_size;
102 i_data -= i_block_size;
106 void CEA708_DTVCC_Demuxer_Push( cea708_demux_t *h, mtime_t i_start, const uint8_t data[3] )
108 if( (data[0] & 0x03) == 3 ) /* Header packet */
110 const int8_t i_pkt_sequence = data[1] >> 6;
112 /* pkt loss/discontinuity, trash buffer */
113 if( i_pkt_sequence > 0 && ((h->i_pkt_sequence + 1) % 4) != i_pkt_sequence )
115 h->i_data = h->i_total_data = 0;
116 h->i_pkt_sequence = i_pkt_sequence;
117 return;
120 uint8_t pktsize = data[1] & 63;
121 if( pktsize == 0 )
122 pktsize = 127;
123 else
124 pktsize = pktsize * 2 - 1;
126 h->i_pkt_sequence = i_pkt_sequence;
127 h->i_total_data = pktsize;
128 h->i_data = 0;
129 h->i_time = i_start;
130 h->data[h->i_data++] = data[2];
132 else if( h->i_total_data > 0 ) /* Not synced to pkt header yet */
134 h->data[h->i_data++] = data[1];
135 h->data[h->i_data++] = data[2];
138 /* pkts assembly finished, we have a service block */
139 if( h->i_data > 0 && h->i_data >= h->i_total_data )
141 if( h->i_data == h->i_total_data ) /* Only if correct */
142 CEA708_DTVCC_Demux_ServiceBlocks( h, h->i_time, h->data, h->i_data );
143 h->i_total_data = h->i_data = 0;
147 /*****************************************************************************
148 * Service Data Decoding
149 *****************************************************************************/
151 #define CEA708_SERVICE_INPUT_BUFFER 128
153 #define CEA708_WINDOWS_COUNT 8
154 #define CEA708_PREDEFINED_STYLES 8
156 #define CEA708_SCREEN_ROWS 75
157 #define CEA708_SCREEN_COLS_43 160
158 #define CEA708_SCREEN_COLS_169 210
159 #define CEA708_SCREEN_SAFE_MARGIN_RATIO 0.10
160 #define CEA708_SAFE_AREA_REL (1.0 - CEA708_SCREEN_SAFE_MARGIN_RATIO)
162 #define CEA708_WINDOW_MAX_COLS 42
163 #define CEA708_WINDOW_MAX_ROWS 15
165 #define CEA708_ROW_HEIGHT_STANDARD (CEA708_SAFE_AREA_REL / \
166 CEA708_SCREEN_ROWS)
167 #define CEA708_FONT_TO_LINE_HEIGHT_RATIO 1.06
169 #define CEA708_FONTRELSIZE_STANDARD (CEA708_ROW_HEIGHT_STANDARD / \
170 CEA708_FONT_TO_LINE_HEIGHT_RATIO)
171 #define CEA708_FONTRELSIZE_SMALL (CEA708_FONTRELSIZE_STANDARD * 0.7)
172 #define CEA708_FONTRELSIZE_LARGE (CEA708_FONTRELSIZE_STANDARD * 1.3)
174 enum cea708_status_e
176 CEA708_STATUS_OK = 1 << 0,
177 CEA708_STATUS_STARVING = 1 << 1,
178 CEA708_STATUS_OUTPUT = 1 << 2,
181 enum cea708_c0_codes
183 CEA708_C0_NUL = 0x00,
184 CEA708_C0_ETX = 0x03,
185 CEA708_C0_BS = 0x08,
186 CEA708_C0_FF = 0x0C,
187 CEA708_C0_CR = 0x0D,
188 CEA708_C0_HCR = 0x0E,
189 CEA708_C0_EXT1 = 0x10,
190 CEA708_C0_P16 = 0x18,
193 enum cea708_c1_codes
195 CEA708_C1_CW0 = 0x80,
196 CEA708_C1_CW7 = 0x87,
197 CEA708_C1_CLW,
198 CEA708_C1_DSW,
199 CEA708_C1_HDW,
200 CEA708_C1_TGW,
201 CEA708_C1_DLW,
202 CEA708_C1_DLY,
203 CEA708_C1_DLC,
204 CEA708_C1_RST,
205 CEA708_C1_SPA = 0x90,
206 CEA708_C1_SPC,
207 CEA708_C1_SPL,
208 CEA708_C1_SWA = 0x97,
209 CEA708_C1_DF0,
210 CEA708_C1_DF7 = 0x9F,
213 typedef struct
215 uint8_t ringbuffer[CEA708_SERVICE_INPUT_BUFFER];
216 uint8_t start;
217 uint8_t capacity;
218 } cea708_input_buffer_t;
220 static void cea708_input_buffer_init(cea708_input_buffer_t *ib)
222 ib->capacity = 0;
223 ib->start = 0;
226 static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t *ib)
228 return ib->capacity;
231 static uint8_t cea708_input_buffer_remain(const cea708_input_buffer_t *ib)
233 return CEA708_SERVICE_INPUT_BUFFER - ib->capacity;
236 static void cea708_input_buffer_add(cea708_input_buffer_t *ib, uint8_t a)
238 if( cea708_input_buffer_remain(ib) > 0 )
239 ib->ringbuffer[(ib->start + ib->capacity++) % CEA708_SERVICE_INPUT_BUFFER] = a;
242 static uint8_t cea708_input_buffer_peek(cea708_input_buffer_t *ib, uint8_t off)
244 if(off + 1 > ib->capacity)
245 return 0;
246 off = (ib->start + off) % CEA708_SERVICE_INPUT_BUFFER;
247 return ib->ringbuffer[off];
250 static uint8_t cea708_input_buffer_get(cea708_input_buffer_t *ib)
252 uint8_t a = cea708_input_buffer_peek( ib, 0 );
253 ib->start = (ib->start + 1) % CEA708_SERVICE_INPUT_BUFFER;
254 ib->capacity--;
255 return a;
258 enum cea708_opacity_e
260 CEA708_OPACITY_SOLID = 0,
261 CEA708_OPACITY_FLASH,
262 CEA708_OPACITY_TRANSLUCENT,
263 CEA708_OPACITY_TRANSPARENT,
266 enum cea708_edge_e
268 CEA708_EDGE_NONE =0,
269 CEA708_EDGE_RAISED,
270 CEA708_EDGE_DEPRESSED,
271 CEA708_EDGE_UNIFORM,
272 CEA708_EDGE_LEFT_DROP_SHADOW,
273 CEA708_EDGE_RIGHT_DROP_SHADOW,
276 typedef struct
278 enum
280 CEA708_PEN_SIZE_SMALL = 0,
281 CEA708_PEN_SIZE_STANDARD,
282 CEA708_PEN_SIZE_LARGE,
283 } size;
284 enum
286 CEA708_FONT_UNDEFINED = 0,
287 CEA708_FONT_MONOSPACED,
288 CEA708_FONT_PROP,
289 CEA708_FONT_MONO_SANS_SERIF,
290 CEA708_FONT_PROP_SANS_SERIF,
291 CEA708_FONT_CASUAL,
292 CEA708_FONT_CURSIVE,
293 CEA708_FONT_SMALL_CAPS,
294 } font;
295 enum
297 CEA708_TAG_DIALOG = 0,
298 CEA708_TAG_SPEAKER,
299 CEA708_TAG_SYNTHETIC_VOICE,
300 CEA708_TAG_DIALOG_SECONDARY_LANG,
301 CEA708_TAG_VOICEOVER,
302 CEA708_TAG_AUDIBLE_TRANSLATION,
303 CEA708_TAG_SUBTITLE_TRANSLATION,
304 CEA708_TAG_VOICE_QUALITY_DESCRIPTION,
305 CEA708_TAG_SONG_LYRICS,
306 CEA708_TAG_FX_DESCRIPTION,
307 CEA708_TAG_SCORE_DESCRIPTION,
308 CEA708_TAG_EXPLETIVE,
309 CEA708_TAG_NOT_TO_BE_DISPLAYED = 15,
310 } text_tag;
311 enum
313 CEA708_PEN_OFFSET_SUBSCRIPT = 0,
314 CEA708_PEN_OFFSET_NORMAL,
315 CEA708_PEN_OFFSET_SUPERSCRIPT,
316 } offset;
317 bool b_italics;
318 bool b_underline;
319 struct
321 uint8_t color;
322 enum cea708_opacity_e opacity;
323 } foreground, background;
324 uint8_t edge_color;
325 enum cea708_edge_e edge_type;
326 } cea708_pen_style_t;
328 typedef struct
330 cea708_pen_style_t style;
331 uint8_t row;
332 uint8_t col;
333 } cea708_pen_t;
335 typedef struct
337 enum
339 CEA708_WA_JUSTIFY_LEFT = 0,
340 CEA708_WA_JUSTIFY_RIGHT,
341 CEA708_WA_JUSTIFY_CENTER,
342 CEA708_WA_JUSTIFY_FULL,
343 } justify;
344 enum
346 CEA708_WA_DIRECTION_LTR = 0,
347 CEA708_WA_DIRECTION_RTL,
348 CEA708_WA_DIRECTION_TB,
349 CEA708_WA_DIRECTION_BT,
350 } print_direction, scroll_direction, effect_direction;
351 bool b_word_wrap;
352 enum
354 CEA708_WA_EFFECT_SNAP = 0,
355 CEA708_WA_EFFECT_FADE,
356 CEA708_WA_EFFECT_WIPE,
357 } display_effect;
358 uint8_t effect_speed;
359 uint8_t fill_color_color;
360 enum cea708_opacity_e fill_opacity;
361 enum cea708_edge_e border_type;
362 uint8_t border_color_color;
363 } cea708_window_style_t;
365 typedef struct cea708_text_row_t cea708_text_row_t;
367 struct cea708_text_row_t
369 uint8_t characters[CEA708_WINDOW_MAX_COLS * 4];
370 cea708_pen_style_t styles[CEA708_WINDOW_MAX_COLS];
371 uint8_t firstcol;
372 uint8_t lastcol;
375 static void cea708_text_row_Delete( cea708_text_row_t *p_row )
377 free( p_row );
380 static cea708_text_row_t * cea708_text_row_New( void )
382 cea708_text_row_t *p_row = malloc( sizeof(*p_row) );
383 if( p_row )
385 p_row->firstcol = CEA708_WINDOW_MAX_COLS;
386 p_row->lastcol = 0;
387 memset(p_row->characters, 0, 4 * CEA708_WINDOW_MAX_COLS);
389 return p_row;
392 typedef struct
394 cea708_text_row_t * rows[CEA708_WINDOW_MAX_ROWS];
395 uint8_t i_firstrow;
396 uint8_t i_lastrow;
398 uint8_t i_priority;
400 enum
402 CEA708_ANCHOR_TOP_LEFT = 0,
403 CEA708_ANCHOR_TOP_CENTER,
404 CEA708_ANCHOR_TOP_RIGHT,
405 CEA708_ANCHOR_CENTER_LEFT,
406 CEA708_ANCHOR_CENTER_CENTER,
407 CEA708_ANCHOR_CENTER_RIGHT,
408 CEA708_ANCHOR_BOTTOM_LEFT,
409 CEA708_ANCHOR_BOTTOM_CENTER,
410 CEA708_ANCHOR_BOTTOM_RIGHT,
411 } anchor_point;
412 uint8_t i_anchor_offset_v;
413 uint8_t i_anchor_offset_h;
415 /* Extras row for window scroll */
416 uint8_t i_row_count;
417 uint8_t i_col_count;
419 /* flags */
420 uint8_t b_relative;
421 uint8_t b_row_lock;
422 uint8_t b_column_lock;
423 uint8_t b_visible;
425 cea708_window_style_t style;
426 cea708_pen_style_t pen;
428 uint8_t row;
429 uint8_t col;
431 bool b_defined;
433 } cea708_window_t;
435 struct cea708_t
437 decoder_t *p_dec;
439 /* Defaults */
440 cea708_window_t window[CEA708_WINDOWS_COUNT];
441 cea708_input_buffer_t input_buffer;
443 /* Decoding context */
444 cea708_window_t *p_cw; /* current window */
445 mtime_t suspended_deadline; /* > 0 when delay is active */
446 mtime_t i_clock;
447 bool b_text_waiting;
450 static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 );
451 static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 );
452 static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 );
453 static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 );
454 static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 );
455 static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 );
457 #define DEFAULT_NTSC_STYLE(font, edge, bgopacity ) \
459 CEA708_PEN_SIZE_STANDARD,\
460 font,\
461 CEA708_TAG_DIALOG,\
462 CEA708_PEN_OFFSET_NORMAL,\
463 false,\
464 false,\
465 { 0x2A, CEA708_OPACITY_SOLID, },\
466 { 0x00, bgopacity, },\
467 0x00,\
468 edge,\
470 static const cea708_pen_style_t cea708_default_pen_styles[CEA708_PREDEFINED_STYLES] =
472 DEFAULT_NTSC_STYLE( CEA708_FONT_UNDEFINED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
473 DEFAULT_NTSC_STYLE( CEA708_FONT_MONOSPACED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
474 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
475 DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
476 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ),
477 DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
478 DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
480 #undef DEFAULT_NTSC_STYLE
482 #define DEFAULT_NTSC_WA_STYLE(just, pd, scroll, wrap, opacity) \
484 just,\
485 pd,\
486 scroll,\
487 CEA708_WA_DIRECTION_LTR,\
488 wrap,\
489 CEA708_WA_EFFECT_SNAP,\
491 0x00,\
492 opacity,\
493 CEA708_EDGE_NONE,\
494 0x00,\
496 static const cea708_window_style_t cea708_default_window_styles[CEA708_PREDEFINED_STYLES] =
498 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
499 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID),
500 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
501 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_TRANSPARENT),
502 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
503 CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID),
504 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
505 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID),
506 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR,
507 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_TRANSPARENT),
508 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
509 CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID),
510 DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_TB,
511 CEA708_WA_DIRECTION_RTL, false, CEA708_OPACITY_SOLID),
513 #undef DEFAULT_NTSC_WA_STYLE
515 static void CEA708_Window_Init( cea708_window_t *p_w )
517 memset( p_w, 0, sizeof(*p_w) );
518 p_w->style = cea708_default_window_styles[0];
519 p_w->pen = cea708_default_pen_styles[0];
520 p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
521 p_w->b_row_lock = true;
522 p_w->b_column_lock = true;
525 static void CEA708_Window_ClearText( cea708_window_t *p_w )
527 for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ )
529 cea708_text_row_Delete( p_w->rows[i] );
530 p_w->rows[i] = NULL;
532 p_w->i_lastrow = 0;
533 p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
536 static void CEA708_Window_Reset( cea708_window_t *p_w )
538 CEA708_Window_ClearText( p_w );
539 CEA708_Window_Init( p_w );
542 static bool CEA708_Window_BreaksSpace( const cea708_window_t *p_w )
544 return true;
545 if( p_w->style.print_direction == CEA708_WA_DIRECTION_LTR &&
546 p_w->style.justify == CEA708_WA_JUSTIFY_LEFT )
547 return true;
549 if( p_w->style.print_direction == CEA708_WA_DIRECTION_RTL &&
550 p_w->style.justify == CEA708_WA_JUSTIFY_RIGHT )
551 return true;
553 return false;
556 static uint8_t CEA708_Window_MinCol( const cea708_window_t *p_w )
558 uint8_t i_min = CEA708_WINDOW_MAX_COLS;
559 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
561 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
562 if( p_row && p_row->firstcol < i_min )
563 i_min = p_row->firstcol;
565 return i_min;
568 static uint8_t CEA708_Window_MaxCol( const cea708_window_t *p_w )
570 uint8_t i_max = 0;
571 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
573 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
574 if( p_row && p_row->lastcol > i_max )
575 i_max = p_row->lastcol;
577 return i_max;
580 static uint8_t CEA708_Window_ColCount( const cea708_window_t *p_w )
582 const cea708_text_row_t *p_row = p_w->rows[p_w->row];
583 if( !p_row || p_row->firstcol > p_row->lastcol )
584 return 0;
585 return 1 + p_row->lastcol - p_row->firstcol;
588 static uint8_t CEA708_Window_RowCount( const cea708_window_t *p_w )
590 if( p_w->i_firstrow > p_w->i_lastrow )
591 return 0;
592 return 1 + p_w->i_lastrow - p_w->i_firstrow;
595 static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
597 switch( i_direction )
599 case CEA708_WA_DIRECTION_LTR: /* Deletes all most right col */
601 uint8_t i_max = CEA708_Window_MaxCol( p_w );
602 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
604 cea708_text_row_t *row = p_w->rows[i];
605 if( row->lastcol == i_max )
607 if( row->firstcol >= row->lastcol )
609 cea708_text_row_Delete( row );
610 p_w->rows[i] = NULL;
611 if( i == p_w->i_firstrow )
612 p_w->i_firstrow++;
613 else if( i == p_w->i_lastrow )
614 p_w->i_lastrow--;
619 break;
620 case CEA708_WA_DIRECTION_RTL: /* Deletes all most left col */
622 uint8_t i_min = CEA708_Window_MinCol( p_w );
623 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
625 cea708_text_row_t *row = p_w->rows[i];
626 if( row->firstcol == i_min )
628 if( row->firstcol >= row->lastcol )
630 cea708_text_row_Delete( row );
631 p_w->rows[i] = NULL;
632 if( i == p_w->i_firstrow )
633 p_w->i_firstrow++;
634 else if( i == p_w->i_lastrow )
635 p_w->i_lastrow--;
640 break;
641 case CEA708_WA_DIRECTION_TB: /* Deletes LAST row */
642 if( CEA708_Window_RowCount( p_w ) > 0 )
644 cea708_text_row_Delete( p_w->rows[p_w->i_lastrow] );
645 p_w->rows[p_w->i_lastrow--] = NULL;
647 break;
648 case CEA708_WA_DIRECTION_BT: /* Deletes First row */
649 if( CEA708_Window_RowCount( p_w ) > 0 )
651 cea708_text_row_Delete( p_w->rows[p_w->i_firstrow] );
652 p_w->rows[p_w->i_firstrow++] = NULL;
654 break;
658 static void CEA708_Window_Scroll( cea708_window_t *p_w )
660 if( CEA708_Window_RowCount( p_w ) == 0 )
661 return;
663 switch( p_w->style.scroll_direction )
665 case CEA708_WA_DIRECTION_LTR:
666 /* Move RIGHT */
667 if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_ROWS - 1 )
668 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR );
669 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
671 cea708_text_row_t *row = p_w->rows[i];
672 if( row->lastcol < row->firstcol ) /* should not happen */
673 continue;
674 memmove( &row->characters[row->firstcol + 1], &row->characters[row->firstcol],
675 (row->lastcol - row->firstcol + 1) * 4U );
676 memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol],
677 (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
678 row->firstcol++;
679 row->lastcol++;
681 break;
682 case CEA708_WA_DIRECTION_RTL:
683 /* Move LEFT */
684 if( CEA708_Window_MinCol( p_w ) == 0 )
685 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_RTL );
686 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
688 cea708_text_row_t *row = p_w->rows[i];
689 if( row->lastcol < row->firstcol ) /* should not happen */
690 continue;
691 memmove( &row->characters[row->firstcol - 1], &row->characters[row->firstcol],
692 (row->lastcol - row->firstcol + 1) * 4U );
693 memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol],
694 (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
695 row->firstcol--;
696 row->lastcol--;
698 break;
699 case CEA708_WA_DIRECTION_TB:
700 /* Move DOWN */
701 if( p_w->i_firstrow == CEA708_WINDOW_MAX_ROWS - 1 )
702 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_TB );
703 for( int i=p_w->i_lastrow; i > p_w->i_firstrow; i-- )
704 p_w->rows[i+1] = p_w->rows[i];
705 p_w->rows[p_w->i_firstrow] = NULL;
706 p_w->i_firstrow++;
707 p_w->i_lastrow++;
708 break;
709 case CEA708_WA_DIRECTION_BT:
710 /* Move UP */
711 if( p_w->i_firstrow == 0 )
712 CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_BT );
713 for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
714 p_w->rows[i-1] = p_w->rows[i];
715 p_w->rows[p_w->i_lastrow] = NULL;
716 p_w->i_firstrow--;
717 p_w->i_lastrow--;
718 break;
722 static void CEA708_Window_CarriageReturn( cea708_window_t *p_w )
724 switch( p_w->style.scroll_direction )
726 case CEA708_WA_DIRECTION_LTR:
727 if( p_w->col > 0 &&
728 CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
729 p_w->col--;
730 else
731 CEA708_Window_Scroll( p_w );
732 p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
733 0 : CEA708_WINDOW_MAX_ROWS - 1;
734 break;
735 case CEA708_WA_DIRECTION_RTL:
736 if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS &&
737 CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
738 p_w->col++;
739 else
740 CEA708_Window_Scroll( p_w );
741 p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
742 0 : CEA708_WINDOW_MAX_ROWS - 1;
743 break;
744 case CEA708_WA_DIRECTION_TB:
745 if( p_w->row > 0 &&
746 CEA708_Window_RowCount( p_w ) < p_w->i_row_count )
747 p_w->row--;
748 else
749 CEA708_Window_Scroll( p_w );
750 p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
751 0 : CEA708_WINDOW_MAX_COLS - 1;
752 break;
753 case CEA708_WA_DIRECTION_BT:
754 if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS &&
755 CEA708_Window_RowCount( p_w ) < p_w->i_row_count )
756 p_w->row++;
757 else
758 CEA708_Window_Scroll( p_w );
759 p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
760 0 : CEA708_WINDOW_MAX_COLS - 1;
761 break;
765 static void CEA708_Window_Forward( cea708_window_t *p_w )
767 switch( p_w->style.print_direction )
769 case CEA708_WA_DIRECTION_LTR:
770 if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS )
771 p_w->col++;
772 else
773 CEA708_Window_CarriageReturn( p_w );
774 break;
775 case CEA708_WA_DIRECTION_RTL:
776 if( p_w->col > 0 )
777 p_w->col--;
778 else
779 CEA708_Window_CarriageReturn( p_w );
780 break;
781 case CEA708_WA_DIRECTION_TB:
782 if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS )
783 p_w->row++;
784 else
785 CEA708_Window_CarriageReturn( p_w );
786 break;
787 case CEA708_WA_DIRECTION_BT:
788 if( p_w->row > 0 )
789 p_w->row--;
790 else
791 CEA708_Window_CarriageReturn( p_w );
792 break;
796 static void CEA708_Window_Backward( cea708_window_t *p_w )
798 static const int reverse[] =
800 [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL,
801 [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR,
802 [CEA708_WA_DIRECTION_TB] = CEA708_WA_DIRECTION_BT,
803 [CEA708_WA_DIRECTION_BT] = CEA708_WA_DIRECTION_TB,
805 int save = p_w->style.print_direction;
806 p_w->style.print_direction = reverse[p_w->style.print_direction];
807 CEA708_Window_Forward( p_w );
808 p_w->style.print_direction = save;
811 static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w )
813 if( !p_w->b_defined )
814 return;
817 if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS ||
818 p_w->col >= CEA708_WINDOW_MAX_COLS ) )
820 assert( p_w->row < CEA708_WINDOW_MAX_ROWS );
821 assert( p_w->col < CEA708_WINDOW_MAX_COLS );
822 return;
825 cea708_text_row_t *p_row = p_w->rows[p_w->row];
826 if( !p_row )
828 p_w->rows[p_w->row] = p_row = cea708_text_row_New();
829 if( !p_row )
830 return;
831 if( p_w->row < p_w->i_firstrow )
832 p_w->i_firstrow = p_w->row;
833 if( p_w->row > p_w->i_lastrow )
834 p_w->i_lastrow = p_w->row;
837 memcpy( &p_row->characters[p_w->col * 4U], c, 4 );
838 p_row->styles[p_w->col] = p_w->pen;
839 if( p_w->col < p_row->firstcol )
840 p_row->firstcol = p_w->col;
841 if( p_w->col > p_row->lastcol )
842 p_row->lastcol = p_w->col;
844 CEA708_Window_Forward( p_w );
846 Debug(printf("\033[0;33m%s\033[0m", c));
849 static uint32_t CEA708ColorConvert( uint8_t c )
851 const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF};
852 c = c & 0x3F;
853 return (value[(c >> 4) & 0x03] << 16) |
854 (value[(c >> 2) & 0x03] << 8) |
855 value[c & 0x03];
858 static uint8_t CEA708AlphaConvert( uint8_t c )
860 if( c == CEA708_OPACITY_TRANSLUCENT )
861 return STYLE_ALPHA_OPAQUE / 2;
862 else if( c == CEA708_OPACITY_TRANSPARENT )
863 return STYLE_ALPHA_TRANSPARENT;
864 else
865 return STYLE_ALPHA_OPAQUE;
868 static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s )
870 if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT )
872 s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity );
873 s->i_style_flags |= STYLE_BACKGROUND;
874 s->i_background_color = CEA708ColorConvert( ps->background.color );
875 s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA;
876 if( ps->background.opacity == CEA708_OPACITY_FLASH )
877 s->i_style_flags |= STYLE_BLINK_BACKGROUND;
879 s->i_font_color = CEA708ColorConvert( ps->foreground.color );
880 s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity );
881 s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR;
882 if( ps->foreground.opacity == CEA708_OPACITY_FLASH )
883 s->i_style_flags |= STYLE_BLINK_FOREGROUND;
885 if( ps->b_italics )
886 s->i_style_flags |= STYLE_ITALIC;
887 if( ps->b_underline )
888 s->i_style_flags |= STYLE_UNDERLINE;
890 switch( ps->font )
892 default:
893 case CEA708_FONT_UNDEFINED:
894 case CEA708_FONT_MONOSPACED:
895 case CEA708_FONT_MONO_SANS_SERIF:
896 s->i_style_flags |= STYLE_MONOSPACED;
897 break;
898 case CEA708_FONT_PROP:
899 case CEA708_FONT_PROP_SANS_SERIF:
900 case CEA708_FONT_CASUAL:
901 case CEA708_FONT_CURSIVE:
902 case CEA708_FONT_SMALL_CAPS:
903 break;
906 switch( ps->size )
908 case CEA708_PEN_SIZE_SMALL:
909 s->f_font_relsize = CEA708_FONTRELSIZE_SMALL;
910 break;
911 case CEA708_PEN_SIZE_LARGE:
912 s->f_font_relsize = CEA708_FONTRELSIZE_LARGE;
913 break;
914 default:
915 s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD;
916 break;
920 static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row,
921 uint8_t i_start, uint8_t i_end,
922 bool b_newline )
924 text_segment_t *p_segment = text_segment_New( NULL );
925 if( !p_segment )
926 return NULL;
928 p_segment->style = text_style_Create( STYLE_NO_DEFAULTS );
929 if( p_segment->style )
930 CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style );
932 p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U );
933 if( !p_segment->psz_text )
935 text_segment_Delete( p_segment );
936 return NULL;
939 size_t offsetw = 0;
940 for( uint8_t i=i_start; i<=i_end; i++ )
942 for( size_t j=0; j<4; j++ )
944 if( p_row->characters[i * 4 + j] != 0 )
945 p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j];
946 else if( j == 0 )
947 p_segment->psz_text[offsetw++] = ' ';
948 else
949 break;
953 if( b_newline )
954 p_segment->psz_text[offsetw++] = '\n';
955 p_segment->psz_text[offsetw] = '\0';
957 return p_segment;
960 static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row,
961 bool b_addnewline )
963 text_segment_t *p_segments = NULL;
964 text_segment_t **pp_last = &p_segments;
966 uint8_t i_start = p_row->firstcol;
967 for( uint8_t i=i_start; i<=p_row->lastcol; i++ )
969 if( i == p_row->lastcol ||
970 memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) )
972 *pp_last = CEA708CharsToSegment( p_row, i_start, i,
973 b_addnewline && (i == p_row->lastcol) );
974 if( *pp_last )
975 pp_last = &((*pp_last)->p_next);
976 i_start = i+1;
980 return p_segments;
983 static void CEA708SpuConvert( const cea708_window_t *p_w,
984 subpicture_updater_sys_region_t *p_region )
986 if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 )
987 return;
989 if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) )
990 return;
992 text_segment_t **pp_last = &p_region->p_segments;
993 for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ )
995 if( !p_w->rows[i] )
996 continue;
998 *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow );
999 if( *pp_last )
1000 pp_last = &((*pp_last)->p_next);
1003 if( p_w->b_relative )
1005 p_region->origin.x = p_w->i_anchor_offset_h / 100.0;
1006 p_region->origin.y = p_w->i_anchor_offset_v / 100.0;
1008 else
1010 p_region->origin.x = (float)p_w->i_anchor_offset_h / CEA708_SCREEN_COLS_169;
1011 p_region->origin.y = (float)p_w->i_anchor_offset_v /
1012 (CEA708_SCREEN_ROWS * CEA708_FONT_TO_LINE_HEIGHT_RATIO);
1014 p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO;
1016 if( p_w->i_firstrow <= p_w->i_lastrow )
1018 p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD;
1019 /*const uint8_t i_min = CEA708_Window_MinCol( p_w );
1020 if( i_min < CEA708_WINDOW_MAX_COLS )
1021 p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/
1024 if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT )
1026 static const int vlc_subpicture_aligns[] =
1028 [CEA708_ANCHOR_TOP_LEFT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
1029 [CEA708_ANCHOR_TOP_CENTER] = SUBPICTURE_ALIGN_TOP,
1030 [CEA708_ANCHOR_TOP_RIGHT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT,
1031 [CEA708_ANCHOR_CENTER_LEFT] = SUBPICTURE_ALIGN_LEFT,
1032 [CEA708_ANCHOR_CENTER_CENTER] = 0,
1033 [CEA708_ANCHOR_CENTER_RIGHT] = SUBPICTURE_ALIGN_RIGHT,
1034 [CEA708_ANCHOR_BOTTOM_LEFT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT,
1035 [CEA708_ANCHOR_BOTTOM_CENTER] = SUBPICTURE_ALIGN_BOTTOM,
1036 [CEA708_ANCHOR_BOTTOM_RIGHT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
1038 p_region->align = vlc_subpicture_aligns[p_w->anchor_point];
1040 p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT;
1043 static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 )
1045 subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec );
1046 if( !p_spu )
1047 return NULL;
1049 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
1050 subpicture_updater_sys_region_t *p_region = &p_spu_sys->region;
1052 p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO;
1054 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1056 cea708_window_t *p_w = &p_cea708->window[i];
1057 if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) )
1059 if( p_region != &p_spu_sys->region )
1061 subpicture_updater_sys_region_t *p_newregion =
1062 SubpictureUpdaterSysRegionNew();
1063 if( p_newregion == NULL )
1064 break;
1065 SubpictureUpdaterSysRegionAdd( p_region, p_newregion );
1066 p_region = p_newregion;
1068 /* Fill region */
1069 CEA708SpuConvert( p_w, p_region );
1073 p_spu->i_start = p_cea708->i_clock;
1074 p_spu->i_stop = p_cea708->i_clock + 10000000; /* 10s max */
1076 p_spu->b_ephemer = true;
1077 p_spu->b_absolute = false;
1078 p_spu->b_subtitle = true;
1080 return p_spu;
1083 static void CEA708_Decoder_Init( cea708_t *p_cea708 )
1085 cea708_input_buffer_init( &p_cea708->input_buffer );
1086 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1087 CEA708_Window_Init( &p_cea708->window[i] );
1088 p_cea708->p_cw = &p_cea708->window[0];
1089 p_cea708->suspended_deadline = 0;
1090 p_cea708->b_text_waiting = false;
1091 p_cea708->i_clock = 0;
1094 static void CEA708_Decoder_Reset( cea708_t *p_cea708 )
1096 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1097 CEA708_Window_Reset( &p_cea708->window[i] );
1098 CEA708_Decoder_Init( p_cea708 );
1101 void CEA708_Decoder_Flush( cea708_t *p_cea708 )
1103 CEA708_Decoder_Reset( p_cea708 );
1106 void CEA708_Decoder_Release( cea708_t *p_cea708 )
1108 CEA708_Decoder_Reset( p_cea708 );
1109 free( p_cea708 );
1112 cea708_t * CEA708_Decoder_New( decoder_t *p_dec )
1114 cea708_t *p_cea708 = malloc( sizeof(cea708_t) );
1115 if( p_cea708 )
1117 CEA708_Decoder_Init( p_cea708 );
1118 p_cea708->p_dec = p_dec;
1120 return p_cea708;
1123 #define POP_COMMAND() (void) cea708_input_buffer_get( ib )
1124 #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND()
1125 #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\
1126 return CEA708_STATUS_STARVING
1127 #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND()
1129 static void CEA708_Output( cea708_t *p_cea708 )
1131 Debug(printf("@%ld ms\n", p_cea708->i_clock / 1000));
1132 subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 );
1133 if( p_spu )
1134 decoder_QueueSub( p_cea708->p_dec, p_spu );
1137 static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 )
1139 uint8_t v, i;
1140 uint16_t u16;
1141 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1142 int i_ret = CEA708_STATUS_OK;
1144 switch( code )
1146 case CEA708_C0_NUL:
1147 POP_COMMAND();
1148 break;
1149 case CEA708_C0_ETX:
1150 POP_COMMAND();
1151 if( p_cea708->b_text_waiting )
1153 i_ret |= CEA708_STATUS_OUTPUT;
1154 p_cea708->b_text_waiting = false;
1156 break;
1157 case CEA708_C0_BS:
1158 POP_COMMAND();
1159 if( !p_cea708->p_cw->b_defined )
1160 break;
1161 CEA708_Window_Backward( p_cea708->p_cw );
1162 p_cea708->b_text_waiting = true;
1163 break;
1164 case CEA708_C0_FF:
1165 POP_COMMAND();
1166 if( !p_cea708->p_cw->b_defined )
1167 break;
1168 CEA708_Window_ClearText( p_cea708->p_cw );
1169 p_cea708->p_cw->col = 0;
1170 p_cea708->p_cw->row = 0;
1171 p_cea708->b_text_waiting = true;
1172 break;
1173 case CEA708_C0_CR:
1174 POP_COMMAND();
1175 if( !p_cea708->p_cw->b_defined )
1176 break;
1177 if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL )
1179 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1180 if( p_cea708->p_cw->b_visible )
1181 i_ret |= CEA708_STATUS_OUTPUT;
1183 break;
1184 case CEA708_C0_HCR:
1185 POP_COMMAND();
1186 if( !p_cea708->p_cw->b_defined )
1187 break;
1188 if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL )
1190 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1191 if( p_cea708->p_cw->b_visible )
1192 i_ret |= CEA708_STATUS_OUTPUT;
1194 break;
1195 case CEA708_C0_EXT1: /* Special extended table case */
1196 if( cea708_input_buffer_size( ib ) >= 2 )
1198 v = cea708_input_buffer_peek( ib, 1 );
1199 /* C2 extended code set */
1200 if( v < 0x20 )
1202 if( v > 0x17 )
1203 i = 3;
1204 else if( v > 0x0f )
1205 i = 2;
1206 else if( v > 0x07 )
1207 i = 1;
1208 else
1209 i = 0;
1210 if( cea708_input_buffer_size( ib ) < 2 + i )
1211 return CEA708_STATUS_STARVING;
1212 POP_COMMAND();
1213 POP_ARGS(1 + i);
1215 /* C3 extended code set */
1216 else if( v > 0x7f && v < 0xa0 )
1218 if( v > 0x87 )
1219 i = 5;
1220 else
1221 i = 4;
1222 if( cea708_input_buffer_size( ib ) < 2 + i )
1223 return CEA708_STATUS_STARVING;
1224 POP_COMMAND();
1225 POP_ARGS(1 + i);
1227 else
1229 POP_COMMAND();
1230 v = cea708_input_buffer_get( ib );
1231 if( p_cea708->p_cw->b_defined )
1232 i_ret |= CEA708_Decode_G2G3( v, p_cea708 );
1235 else return CEA708_STATUS_STARVING;
1236 break;
1237 case CEA708_C0_P16:
1238 REQUIRE_ARGS_AND_POP_COMMAND(2);
1239 u16 = cea708_input_buffer_get( ib ) << 8;
1240 u16 |= cea708_input_buffer_get( ib );
1241 i_ret |= CEA708_Decode_P16( u16, p_cea708 );
1242 Debug(printf("[P16 %x]", u16));
1243 break;
1244 default:
1245 POP_COMMAND();
1246 Debug(printf("[UNK %2.2x]", code));
1247 break;
1249 Debug(printf("[C0 %x]", code));
1250 return i_ret;
1253 static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 )
1255 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1256 POP_COMMAND();
1257 int i_ret = CEA708_STATUS_OK;
1259 if( !p_cea708->p_cw->b_defined )
1260 return i_ret;
1262 uint8_t utf8[4] = {code,0x00,0x00,0x00};
1264 if(code == 0x7F) // Music note
1266 utf8[0] = 0xe2;
1267 utf8[1] = 0x99;
1268 utf8[2] = 0xaa;
1271 CEA708_Window_Write( utf8, p_cea708->p_cw );
1273 if( code == 0x20 &&
1274 p_cea708->b_text_waiting &&
1275 CEA708_Window_BreaksSpace( p_cea708->p_cw ) )
1277 i_ret |= CEA708_STATUS_OUTPUT;
1281 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1283 return i_ret;
1286 static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 )
1288 uint8_t v, i;
1289 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1290 int i_ret = CEA708_STATUS_OK;
1292 if( p_cea708->b_text_waiting )
1294 i_ret |= CEA708_STATUS_OUTPUT;
1295 p_cea708->b_text_waiting = false;
1298 switch( code )
1300 case CEA708_C1_CLW:
1301 REQUIRE_ARGS_AND_POP_COMMAND(1);
1302 Debug(printf("[CLW]"));
1303 if( p_cea708->p_cw->b_defined )
1305 CEA708_Window_ClearText( p_cea708->p_cw );
1306 if( p_cea708->p_cw->b_visible )
1307 i_ret |= CEA708_STATUS_OUTPUT;
1309 break;
1310 case CEA708_C1_DSW:
1311 REQUIRE_ARGS_AND_POP_COMMAND(1);
1312 Debug(printf("[DSW"));
1313 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1314 if( v & 1 )
1316 if( p_cea708->window[i].b_defined )
1318 if( !p_cea708->window[i].b_visible )
1319 i_ret |= CEA708_STATUS_OUTPUT;
1320 p_cea708->window[i].b_visible = true;
1322 Debug(printf("%d", i));
1324 Debug(printf("]"));
1325 break;
1326 case CEA708_C1_HDW:
1327 REQUIRE_ARGS_AND_POP_COMMAND(1);
1328 Debug(printf("[HDW"));
1329 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1330 if( v & 1 )
1332 if( p_cea708->window[i].b_defined )
1334 if( p_cea708->window[i].b_visible )
1335 i_ret |= CEA708_STATUS_OUTPUT;
1336 p_cea708->window[i].b_visible = false;
1338 Debug(printf("%d", i));
1340 Debug(printf("]"));
1341 break;
1342 case CEA708_C1_TGW:
1343 REQUIRE_ARGS_AND_POP_COMMAND(1);
1344 Debug(printf("[TGW"));
1345 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1346 if( v & 1 )
1348 if( p_cea708->window[i].b_defined )
1350 i_ret |= CEA708_STATUS_OUTPUT;
1351 p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible;
1353 Debug(printf("%d", i));
1355 Debug(printf("]"));
1356 break;
1357 case CEA708_C1_DLW:
1358 REQUIRE_ARGS_AND_POP_COMMAND(1);
1359 Debug(printf("[DLW"));
1360 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1361 if( v & 1 )
1363 if( p_cea708->window[i].b_defined )
1365 if( p_cea708->window[i].b_visible )
1366 i_ret |= CEA708_STATUS_OUTPUT;
1367 CEA708_Window_Reset( &p_cea708->window[i] );
1369 Debug(printf("%d", i));
1371 Debug(printf("]"));
1372 break;
1373 case CEA708_C1_DLY:
1374 REQUIRE_ARGS_AND_POP_COMMAND(1);
1375 p_cea708->suspended_deadline = p_cea708->i_clock +
1376 cea708_input_buffer_get( ib ) * 100 * 1000;
1377 Debug(printf("[DLY]"));
1378 break;
1379 case CEA708_C1_DLC:
1380 POP_COMMAND();
1381 p_cea708->suspended_deadline = 0;
1382 Debug(printf("[DLC]"));
1383 break;
1384 case CEA708_C1_RST:
1385 POP_COMMAND();
1386 i_ret |= CEA708_STATUS_OUTPUT;
1387 /* FIXME */
1388 break;
1389 case CEA708_C1_SPA:
1390 REQUIRE_ARGS_AND_POP_COMMAND(2);
1391 if( !p_cea708->p_cw->b_defined )
1393 POP_ARGS(2);
1394 break;
1396 v = cea708_input_buffer_get( ib );
1397 p_cea708->p_cw->pen.text_tag = v >> 4;
1398 p_cea708->p_cw->pen.offset = (v >> 2) & 0x03;
1399 p_cea708->p_cw->pen.size = v & 0x03;
1400 v = cea708_input_buffer_get( ib );
1401 p_cea708->p_cw->pen.b_italics = v & 0x80;
1402 p_cea708->p_cw->pen.b_underline = v & 0x40;
1403 p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07;
1404 p_cea708->p_cw->pen.font = v & 0x07;
1405 Debug(printf("[SPA]"));
1406 break;
1407 case CEA708_C1_SPC:
1408 REQUIRE_ARGS_AND_POP_COMMAND(3);
1409 if( !p_cea708->p_cw->b_defined )
1411 POP_ARGS(3);
1412 break;
1414 v = cea708_input_buffer_get( ib );
1415 p_cea708->p_cw->pen.foreground.opacity = v >> 6;
1416 p_cea708->p_cw->pen.foreground.color = v & 0x3F;
1417 v = cea708_input_buffer_get( ib );
1418 p_cea708->p_cw->pen.background.opacity = v >> 6;
1419 p_cea708->p_cw->pen.background.color = v & 0x3F;
1420 v = cea708_input_buffer_get( ib );
1421 p_cea708->p_cw->pen.edge_color = v & 0x3F;
1422 Debug(printf("[SPC]"));
1423 break;
1424 case CEA708_C1_SPL:
1425 REQUIRE_ARGS_AND_POP_COMMAND(2);
1426 if( !p_cea708->p_cw->b_defined )
1428 POP_ARGS(2);
1429 break;
1431 v = cea708_input_buffer_get( ib );
1432 p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS;
1433 v = cea708_input_buffer_get( ib );
1434 p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS;
1435 Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col));
1436 break;
1437 case CEA708_C1_SWA:
1438 REQUIRE_ARGS_AND_POP_COMMAND(4);
1439 if( !p_cea708->p_cw->b_defined )
1441 POP_ARGS(4);
1442 break;
1444 v = cea708_input_buffer_get( ib );
1445 p_cea708->p_cw->style.fill_opacity = v >> 6;
1446 p_cea708->p_cw->style.fill_color_color = v & 0x3F;
1447 v = cea708_input_buffer_get( ib );
1448 p_cea708->p_cw->style.border_color_color = v & 0x3F;
1449 p_cea708->p_cw->style.border_type = v >> 6;
1450 v = cea708_input_buffer_get( ib );
1451 p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5);
1452 p_cea708->p_cw->style.b_word_wrap = v & 0x40;
1453 p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03;
1454 p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03;
1455 p_cea708->p_cw->style.justify = v & 0x03;
1456 v = cea708_input_buffer_get( ib );
1457 p_cea708->p_cw->style.effect_speed = v >> 4;
1458 p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03;
1459 p_cea708->p_cw->style.display_effect = v & 0x03;
1460 Debug(printf("[SWA]"));
1461 break;
1463 default:
1464 if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 )
1466 POP_COMMAND();
1467 Debug(printf("[CW%d]", code - CEA708_C1_CW0));
1468 if( p_cea708->window[code - CEA708_C1_CW0].b_defined )
1469 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0];
1471 else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 )
1473 REQUIRE_ARGS_AND_POP_COMMAND(6);
1474 Debug(printf("[DF%d]", code - CEA708_C1_DF0));
1475 /* also sets current window */
1476 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0];
1477 v = cea708_input_buffer_get( ib );
1478 if( p_cea708->p_cw->b_defined &&
1479 !p_cea708->p_cw->b_visible != !(v & 0x20) )
1480 i_ret |= CEA708_STATUS_OUTPUT;
1481 p_cea708->p_cw->b_visible = v & 0x20;
1482 p_cea708->p_cw->b_row_lock = v & 0x10;
1483 p_cea708->p_cw->b_column_lock = v & 0x08;
1484 p_cea708->p_cw->i_priority = v & 0x07;
1485 v = cea708_input_buffer_get( ib );
1486 p_cea708->p_cw->b_relative = v & 0x80;
1487 p_cea708->p_cw->i_anchor_offset_v = v & 0x7F;
1488 v = cea708_input_buffer_get( ib );
1489 p_cea708->p_cw->i_anchor_offset_h = v;
1490 v = cea708_input_buffer_get( ib );
1491 p_cea708->p_cw->anchor_point = v >> 4;
1492 p_cea708->p_cw->i_row_count = v & 0x0F;
1493 v = cea708_input_buffer_get( ib );
1494 p_cea708->p_cw->i_col_count = v & 0x3F;
1495 v = cea708_input_buffer_get( ib );
1496 /* zero values style set on init, avoid dealing with updt case */
1497 i = (v >> 3) & 0x07; /* Window style id */
1498 if( i > 0 && !p_cea708->p_cw->b_defined )
1499 p_cea708->p_cw->style = cea708_default_window_styles[i];
1500 i = v & 0x07; /* Pen style id */
1501 if( i > 0 && !p_cea708->p_cw->b_defined )
1502 p_cea708->p_cw->pen = cea708_default_pen_styles[i];
1503 p_cea708->p_cw->b_defined = true;
1505 else
1507 Debug(printf("{%2.2x}", code));
1508 POP_COMMAND();
1512 return i_ret;
1515 static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 )
1517 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1518 POP_COMMAND();
1520 if( !p_cea708->p_cw->b_defined )
1521 return CEA708_STATUS_OK;
1523 uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6,
1524 0x80 | (code & 0x3f),
1525 0, 0};
1527 CEA708_Window_Write( utf8, p_cea708->p_cw );
1528 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1530 return CEA708_STATUS_OK;
1533 static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 )
1535 if( !p_cea708->p_cw->b_defined )
1536 return CEA708_STATUS_OK;
1538 uint8_t out[4] = { '?', 0, 0, 0 };
1539 static const struct {
1540 uint8_t c;
1541 uint8_t utf8[4];
1542 } code2utf8[] = {
1543 /* G2 */
1544 { 0x20, { 0x20 } },// transparent space [*** will need special handling]
1545 { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling]
1546 { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS
1547 { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON
1548 { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE
1549 { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK
1550 { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK
1551 { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK
1552 { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK
1553 { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK
1554 { 0x35, { 0xe2,0x80,0xa2 } },// BULLET
1555 { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM)
1556 { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON
1557 { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE
1558 { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK
1559 { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS
1560 { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH
1561 { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS
1562 { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS
1563 { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS
1564 { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL
1565 { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT
1566 { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT
1567 { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL
1568 { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT
1569 { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT
1570 /* G3 */
1571 { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C)
1574 for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ )
1576 if( code2utf8[i].c == code )
1578 memcpy( out, code2utf8[i].utf8, 4 );
1579 if(out[0] < 0xf0)
1581 if(out[0] < 0x80)
1582 out[1] = 0;
1583 else if(out[0] < 0xe0)
1584 out[2] = 0;
1585 else
1586 out[3] = 0;
1588 break;
1592 CEA708_Window_Write( out, p_cea708->p_cw );
1594 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1596 return CEA708_STATUS_OK;
1599 static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 )
1601 if( !p_cea708->p_cw->b_defined )
1602 return CEA708_STATUS_OK;
1604 uint8_t out[4] = { '?', 0, 0, 0 };
1606 /* adapted from codepoint conversion from strings.h */
1607 if( ucs2 <= 0x7F )
1609 out[0] = ucs2;
1611 else if( ucs2 <= 0x7FF )
1613 out[0] = 0xC0 | (ucs2 >> 6);
1614 out[1] = 0x80 | (ucs2 & 0x3F);
1616 else
1618 out[0] = 0xE0 | (ucs2 >> 12);
1619 out[1] = 0x80 | ((ucs2 >> 6) & 0x3F);
1620 out[2] = 0x80 | (ucs2 & 0x3F);
1623 CEA708_Window_Write( out, p_cea708->p_cw );
1625 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1627 return CEA708_STATUS_OK;
1630 static void CEA708_Decode_ServiceBuffer( cea708_t *h )
1632 for( ;; )
1634 const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer );
1635 if( i_in == 0 )
1636 break;
1638 int i_ret;
1639 uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 );
1641 if( c < 0x20 )
1642 i_ret = CEA708_Decode_C0( c, h );
1643 else if( c >= 0x20 && c <=0x7F )
1644 i_ret = CEA708_Decode_G0( c, h );
1645 else if( c >= 0x80 && c <= 0x9F )
1646 i_ret = CEA708_Decode_C1( c, h );
1647 else if( c > 0x9F )
1648 i_ret = CEA708_Decode_G1( c, h );
1650 if( i_ret & CEA708_STATUS_OUTPUT )
1651 CEA708_Output( h );
1653 if( i_ret & CEA708_STATUS_STARVING )
1654 break;
1656 /* Update internal clock */
1657 const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer );
1658 if( i_consumed )
1659 h->i_clock += CLOCK_FREQ / 1200 * i_consumed;
1663 void CEA708_Decoder_Push( cea708_t *h, mtime_t i_time,
1664 const uint8_t *p_data, size_t i_data )
1666 /* Set new buffer start time */
1667 h->i_clock = i_time;
1669 for( size_t i=0; i<i_data; )
1671 /* Never push more than buffer */
1672 size_t i_push = cea708_input_buffer_remain(&h->input_buffer);
1673 if( (i_data - i) < i_push )
1674 i_push = (i_data - i);
1675 else if( h->suspended_deadline > 0 )
1676 h->suspended_deadline = 0; /* Full buffer cancels pause */
1678 for( size_t j=0; j<i_push; j++ )
1680 uint8_t byte = p_data[i+j];
1681 cea708_input_buffer_add( &h->input_buffer, byte );
1684 if( h->suspended_deadline > 0 )
1686 /* Decoding is paused */
1687 if ( h->suspended_deadline > h->i_clock )
1689 /* Increase internal clock */
1690 if( i_push )
1691 h->i_clock += CLOCK_FREQ / 1200 * i_push;
1692 continue;
1694 h->suspended_deadline = 0;
1697 /* Decode Buffer */
1698 CEA708_Decode_ServiceBuffer( h );
1700 i += i_push;