demux: mp4: add missing coreaudio enumerated mappings
[vlc.git] / modules / codec / cea708.c
blobc0cea476f7faf3de47f14b6c229b72da9a478954
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_firstrow == 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 < CEA708_WINDOW_MAX_ROWS &&
753 CEA708_Window_RowCount( p_w ) < p_w->i_row_count )
754 p_w->row++;
755 else
756 CEA708_Window_Scroll( p_w );
757 p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
758 0 : CEA708_WINDOW_MAX_COLS - 1;
759 break;
763 static void CEA708_Window_Forward( cea708_window_t *p_w )
765 switch( p_w->style.print_direction )
767 case CEA708_WA_DIRECTION_LTR:
768 if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS )
769 p_w->col++;
770 else
771 CEA708_Window_CarriageReturn( p_w );
772 break;
773 case CEA708_WA_DIRECTION_RTL:
774 if( p_w->col > 0 )
775 p_w->col--;
776 else
777 CEA708_Window_CarriageReturn( p_w );
778 break;
779 case CEA708_WA_DIRECTION_TB:
780 if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS )
781 p_w->row++;
782 else
783 CEA708_Window_CarriageReturn( p_w );
784 break;
785 case CEA708_WA_DIRECTION_BT:
786 if( p_w->row > 0 )
787 p_w->row--;
788 else
789 CEA708_Window_CarriageReturn( p_w );
790 break;
794 static void CEA708_Window_Backward( cea708_window_t *p_w )
796 static const int reverse[] =
798 [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL,
799 [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR,
800 [CEA708_WA_DIRECTION_TB] = CEA708_WA_DIRECTION_BT,
801 [CEA708_WA_DIRECTION_BT] = CEA708_WA_DIRECTION_TB,
803 int save = p_w->style.print_direction;
804 p_w->style.print_direction = reverse[p_w->style.print_direction];
805 CEA708_Window_Forward( p_w );
806 p_w->style.print_direction = save;
809 static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w )
811 if( !p_w->b_defined )
812 return;
815 if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS ||
816 p_w->col >= CEA708_WINDOW_MAX_COLS ) )
818 assert( p_w->row < CEA708_WINDOW_MAX_ROWS );
819 assert( p_w->col < CEA708_WINDOW_MAX_COLS );
820 return;
823 cea708_text_row_t *p_row = p_w->rows[p_w->row];
824 if( !p_row )
826 p_w->rows[p_w->row] = p_row = cea708_text_row_New();
827 if( !p_row )
828 return;
829 if( p_w->row < p_w->i_firstrow )
830 p_w->i_firstrow = p_w->row;
831 if( p_w->row > p_w->i_lastrow )
832 p_w->i_lastrow = p_w->row;
835 memcpy( &p_row->characters[p_w->col * 4U], c, 4 );
836 p_row->styles[p_w->col] = p_w->pen;
837 if( p_w->col < p_row->firstcol )
838 p_row->firstcol = p_w->col;
839 if( p_w->col > p_row->lastcol )
840 p_row->lastcol = p_w->col;
842 CEA708_Window_Forward( p_w );
844 Debug(printf("\033[0;33m%s\033[0m", c));
847 static uint32_t CEA708ColorConvert( uint8_t c )
849 const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF};
850 c = c & 0x3F;
851 return (value[(c >> 4) & 0x03] << 16) |
852 (value[(c >> 2) & 0x03] << 8) |
853 value[c & 0x03];
856 static uint8_t CEA708AlphaConvert( uint8_t c )
858 if( c == CEA708_OPACITY_TRANSLUCENT )
859 return STYLE_ALPHA_OPAQUE / 2;
860 else if( c == CEA708_OPACITY_TRANSPARENT )
861 return STYLE_ALPHA_TRANSPARENT;
862 else
863 return STYLE_ALPHA_OPAQUE;
866 static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s )
868 if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT )
870 s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity );
871 s->i_style_flags |= STYLE_BACKGROUND;
872 s->i_background_color = CEA708ColorConvert( ps->background.color );
873 s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA;
874 if( ps->background.opacity == CEA708_OPACITY_FLASH )
875 s->i_style_flags |= STYLE_BLINK_BACKGROUND;
877 s->i_font_color = CEA708ColorConvert( ps->foreground.color );
878 s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity );
879 s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR;
880 if( ps->foreground.opacity == CEA708_OPACITY_FLASH )
881 s->i_style_flags |= STYLE_BLINK_FOREGROUND;
883 if( ps->b_italics )
884 s->i_style_flags |= STYLE_ITALIC;
885 if( ps->b_underline )
886 s->i_style_flags |= STYLE_UNDERLINE;
888 switch( ps->font )
890 default:
891 case CEA708_FONT_UNDEFINED:
892 case CEA708_FONT_MONOSPACED:
893 case CEA708_FONT_MONO_SANS_SERIF:
894 s->i_style_flags |= STYLE_MONOSPACED;
895 break;
896 case CEA708_FONT_PROP:
897 case CEA708_FONT_PROP_SANS_SERIF:
898 case CEA708_FONT_CASUAL:
899 case CEA708_FONT_CURSIVE:
900 case CEA708_FONT_SMALL_CAPS:
901 break;
904 switch( ps->size )
906 case CEA708_PEN_SIZE_SMALL:
907 s->f_font_relsize = CEA708_FONTRELSIZE_SMALL;
908 break;
909 case CEA708_PEN_SIZE_LARGE:
910 s->f_font_relsize = CEA708_FONTRELSIZE_LARGE;
911 break;
912 default:
913 s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD;
914 break;
918 static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row,
919 uint8_t i_start, uint8_t i_end,
920 bool b_newline )
922 text_segment_t *p_segment = text_segment_New( NULL );
923 if( !p_segment )
924 return NULL;
926 p_segment->style = text_style_Create( STYLE_NO_DEFAULTS );
927 if( p_segment->style )
928 CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style );
930 p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U );
931 if( !p_segment->psz_text )
933 text_segment_Delete( p_segment );
934 return NULL;
937 size_t offsetw = 0;
938 for( uint8_t i=i_start; i<=i_end; i++ )
940 for( size_t j=0; j<4; j++ )
942 if( p_row->characters[i * 4 + j] != 0 )
943 p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j];
944 else if( j == 0 )
945 p_segment->psz_text[offsetw++] = ' ';
946 else
947 break;
951 if( b_newline )
952 p_segment->psz_text[offsetw++] = '\n';
953 p_segment->psz_text[offsetw] = '\0';
955 return p_segment;
958 static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row,
959 bool b_addnewline )
961 text_segment_t *p_segments = NULL;
962 text_segment_t **pp_last = &p_segments;
964 uint8_t i_start = p_row->firstcol;
965 for( uint8_t i=i_start; i<=p_row->lastcol; i++ )
967 if( i == p_row->lastcol ||
968 memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) )
970 *pp_last = CEA708CharsToSegment( p_row, i_start, i,
971 b_addnewline && (i == p_row->lastcol) );
972 if( *pp_last )
973 pp_last = &((*pp_last)->p_next);
974 i_start = i+1;
978 return p_segments;
981 static void CEA708SpuConvert( const cea708_window_t *p_w,
982 substext_updater_region_t *p_region )
984 if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 )
985 return;
987 if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) )
988 return;
990 text_segment_t **pp_last = &p_region->p_segments;
991 for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ )
993 if( !p_w->rows[i] )
994 continue;
996 *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow );
997 if( *pp_last )
998 pp_last = &((*pp_last)->p_next);
1001 if( p_w->b_relative )
1003 p_region->origin.x = p_w->i_anchor_offset_h / 100.0;
1004 p_region->origin.y = p_w->i_anchor_offset_v / 100.0;
1006 else
1008 p_region->origin.x = (float)p_w->i_anchor_offset_h / CEA708_SCREEN_COLS_169;
1009 p_region->origin.y = (float)p_w->i_anchor_offset_v /
1010 (CEA708_SCREEN_ROWS * CEA708_FONT_TO_LINE_HEIGHT_RATIO);
1012 p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO;
1014 if( p_w->i_firstrow <= p_w->i_lastrow )
1016 p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD;
1017 /*const uint8_t i_min = CEA708_Window_MinCol( p_w );
1018 if( i_min < CEA708_WINDOW_MAX_COLS )
1019 p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/
1022 if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT )
1024 static const int vlc_subpicture_aligns[] =
1026 [CEA708_ANCHOR_TOP_LEFT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
1027 [CEA708_ANCHOR_TOP_CENTER] = SUBPICTURE_ALIGN_TOP,
1028 [CEA708_ANCHOR_TOP_RIGHT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT,
1029 [CEA708_ANCHOR_CENTER_LEFT] = SUBPICTURE_ALIGN_LEFT,
1030 [CEA708_ANCHOR_CENTER_CENTER] = 0,
1031 [CEA708_ANCHOR_CENTER_RIGHT] = SUBPICTURE_ALIGN_RIGHT,
1032 [CEA708_ANCHOR_BOTTOM_LEFT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT,
1033 [CEA708_ANCHOR_BOTTOM_CENTER] = SUBPICTURE_ALIGN_BOTTOM,
1034 [CEA708_ANCHOR_BOTTOM_RIGHT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
1036 p_region->align = vlc_subpicture_aligns[p_w->anchor_point];
1038 p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT;
1041 static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 )
1043 subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec );
1044 if( !p_spu )
1045 return NULL;
1047 subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
1048 substext_updater_region_t *p_region = &p_spu_sys->region;
1050 p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO;
1052 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1054 cea708_window_t *p_w = &p_cea708->window[i];
1055 if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) )
1057 if( p_region != &p_spu_sys->region )
1059 substext_updater_region_t *p_newregion =
1060 SubpictureUpdaterSysRegionNew();
1061 if( p_newregion == NULL )
1062 break;
1063 SubpictureUpdaterSysRegionAdd( p_region, p_newregion );
1064 p_region = p_newregion;
1066 /* Fill region */
1067 CEA708SpuConvert( p_w, p_region );
1071 p_spu->i_start = p_cea708->i_clock;
1072 p_spu->i_stop = p_cea708->i_clock + VLC_TICK_FROM_SEC(10); /* 10s max */
1074 p_spu->b_ephemer = true;
1075 p_spu->b_absolute = false;
1076 p_spu->b_subtitle = true;
1078 return p_spu;
1081 static void CEA708_Decoder_Init( cea708_t *p_cea708 )
1083 cea708_input_buffer_init( &p_cea708->input_buffer );
1084 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1085 CEA708_Window_Init( &p_cea708->window[i] );
1086 p_cea708->p_cw = &p_cea708->window[0];
1087 p_cea708->suspended_deadline = VLC_TICK_INVALID;
1088 p_cea708->b_text_waiting = false;
1089 p_cea708->i_clock = 0;
1092 static void CEA708_Decoder_Reset( cea708_t *p_cea708 )
1094 for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1095 CEA708_Window_Reset( &p_cea708->window[i] );
1096 CEA708_Decoder_Init( p_cea708 );
1099 void CEA708_Decoder_Flush( cea708_t *p_cea708 )
1101 CEA708_Decoder_Reset( p_cea708 );
1104 void CEA708_Decoder_Release( cea708_t *p_cea708 )
1106 CEA708_Decoder_Reset( p_cea708 );
1107 free( p_cea708 );
1110 cea708_t * CEA708_Decoder_New( decoder_t *p_dec )
1112 cea708_t *p_cea708 = malloc( sizeof(cea708_t) );
1113 if( p_cea708 )
1115 CEA708_Decoder_Init( p_cea708 );
1116 p_cea708->p_dec = p_dec;
1118 return p_cea708;
1121 #define POP_COMMAND() (void) cea708_input_buffer_get( ib )
1122 #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND()
1123 #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\
1124 return CEA708_STATUS_STARVING
1125 #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND()
1127 static void CEA708_Output( cea708_t *p_cea708 )
1129 Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708->i_clock)));
1130 subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 );
1131 if( p_spu )
1132 decoder_QueueSub( p_cea708->p_dec, p_spu );
1135 static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 )
1137 uint8_t v, i;
1138 uint16_t u16;
1139 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1140 int i_ret = CEA708_STATUS_OK;
1142 switch( code )
1144 case CEA708_C0_NUL:
1145 POP_COMMAND();
1146 break;
1147 case CEA708_C0_ETX:
1148 POP_COMMAND();
1149 if( p_cea708->b_text_waiting )
1151 i_ret |= CEA708_STATUS_OUTPUT;
1152 p_cea708->b_text_waiting = false;
1154 break;
1155 case CEA708_C0_BS:
1156 POP_COMMAND();
1157 if( !p_cea708->p_cw->b_defined )
1158 break;
1159 CEA708_Window_Backward( p_cea708->p_cw );
1160 p_cea708->b_text_waiting = true;
1161 break;
1162 case CEA708_C0_FF:
1163 POP_COMMAND();
1164 if( !p_cea708->p_cw->b_defined )
1165 break;
1166 CEA708_Window_ClearText( p_cea708->p_cw );
1167 p_cea708->p_cw->col = 0;
1168 p_cea708->p_cw->row = 0;
1169 p_cea708->b_text_waiting = true;
1170 break;
1171 case CEA708_C0_CR:
1172 POP_COMMAND();
1173 if( !p_cea708->p_cw->b_defined )
1174 break;
1175 if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL )
1177 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1178 if( p_cea708->p_cw->b_visible )
1179 i_ret |= CEA708_STATUS_OUTPUT;
1181 break;
1182 case CEA708_C0_HCR:
1183 POP_COMMAND();
1184 if( !p_cea708->p_cw->b_defined )
1185 break;
1186 if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL )
1188 CEA708_Window_CarriageReturn( p_cea708->p_cw );
1189 if( p_cea708->p_cw->b_visible )
1190 i_ret |= CEA708_STATUS_OUTPUT;
1192 break;
1193 case CEA708_C0_EXT1: /* Special extended table case */
1194 if( cea708_input_buffer_size( ib ) >= 2 )
1196 v = cea708_input_buffer_peek( ib, 1 );
1197 /* C2 extended code set */
1198 if( v < 0x20 )
1200 if( v > 0x17 )
1201 i = 3;
1202 else if( v > 0x0f )
1203 i = 2;
1204 else if( v > 0x07 )
1205 i = 1;
1206 else
1207 i = 0;
1208 if( cea708_input_buffer_size( ib ) < 2 + i )
1209 return CEA708_STATUS_STARVING;
1210 POP_COMMAND();
1211 POP_ARGS(1 + i);
1213 /* C3 extended code set */
1214 else if( v > 0x7f && v < 0xa0 )
1216 if( v > 0x87 )
1217 i = 5;
1218 else
1219 i = 4;
1220 if( cea708_input_buffer_size( ib ) < 2 + i )
1221 return CEA708_STATUS_STARVING;
1222 POP_COMMAND();
1223 POP_ARGS(1 + i);
1225 else
1227 POP_COMMAND();
1228 v = cea708_input_buffer_get( ib );
1229 if( p_cea708->p_cw->b_defined )
1230 i_ret |= CEA708_Decode_G2G3( v, p_cea708 );
1233 else return CEA708_STATUS_STARVING;
1234 break;
1235 case CEA708_C0_P16:
1236 REQUIRE_ARGS_AND_POP_COMMAND(2);
1237 u16 = cea708_input_buffer_get( ib ) << 8;
1238 u16 |= cea708_input_buffer_get( ib );
1239 i_ret |= CEA708_Decode_P16( u16, p_cea708 );
1240 Debug(printf("[P16 %x]", u16));
1241 break;
1242 default:
1243 POP_COMMAND();
1244 Debug(printf("[UNK %2.2x]", code));
1245 break;
1247 Debug(printf("[C0 %x]", code));
1248 return i_ret;
1251 static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 )
1253 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1254 POP_COMMAND();
1255 int i_ret = CEA708_STATUS_OK;
1257 if( !p_cea708->p_cw->b_defined )
1258 return i_ret;
1260 uint8_t utf8[4] = {code,0x00,0x00,0x00};
1262 if(code == 0x7F) // Music note
1264 utf8[0] = 0xe2;
1265 utf8[1] = 0x99;
1266 utf8[2] = 0xaa;
1269 CEA708_Window_Write( utf8, p_cea708->p_cw );
1271 if( code == 0x20 &&
1272 p_cea708->b_text_waiting &&
1273 CEA708_Window_BreaksSpace( p_cea708->p_cw ) )
1275 i_ret |= CEA708_STATUS_OUTPUT;
1279 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1281 return i_ret;
1284 static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 )
1286 uint8_t v, i;
1287 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1288 int i_ret = CEA708_STATUS_OK;
1290 if( p_cea708->b_text_waiting )
1292 i_ret |= CEA708_STATUS_OUTPUT;
1293 p_cea708->b_text_waiting = false;
1296 switch( code )
1298 case CEA708_C1_CLW:
1299 REQUIRE_ARGS_AND_POP_COMMAND(1);
1300 Debug(printf("[CLW"));
1301 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1302 if( v & 1 )
1304 if( p_cea708->window[i].b_defined &&
1305 p_cea708->window[i].b_visible )
1306 i_ret |= CEA708_STATUS_OUTPUT;
1307 CEA708_Window_ClearText( &p_cea708->window[i] );
1308 Debug(printf("%d", i));
1310 Debug(printf("]"));
1311 break;
1312 case CEA708_C1_DSW:
1313 REQUIRE_ARGS_AND_POP_COMMAND(1);
1314 Debug(printf("[DSW"));
1315 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1316 if( v & 1 )
1318 if( p_cea708->window[i].b_defined )
1320 if( !p_cea708->window[i].b_visible )
1321 i_ret |= CEA708_STATUS_OUTPUT;
1322 p_cea708->window[i].b_visible = true;
1324 Debug(printf("%d", i));
1326 Debug(printf("]"));
1327 break;
1328 case CEA708_C1_HDW:
1329 REQUIRE_ARGS_AND_POP_COMMAND(1);
1330 Debug(printf("[HDW"));
1331 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1332 if( v & 1 )
1334 if( p_cea708->window[i].b_defined )
1336 if( p_cea708->window[i].b_visible )
1337 i_ret |= CEA708_STATUS_OUTPUT;
1338 p_cea708->window[i].b_visible = false;
1340 Debug(printf("%d", i));
1342 Debug(printf("]"));
1343 break;
1344 case CEA708_C1_TGW:
1345 REQUIRE_ARGS_AND_POP_COMMAND(1);
1346 Debug(printf("[TGW"));
1347 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1348 if( v & 1 )
1350 if( p_cea708->window[i].b_defined )
1352 i_ret |= CEA708_STATUS_OUTPUT;
1353 p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible;
1355 Debug(printf("%d", i));
1357 Debug(printf("]"));
1358 break;
1359 case CEA708_C1_DLW:
1360 REQUIRE_ARGS_AND_POP_COMMAND(1);
1361 Debug(printf("[DLW"));
1362 for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1363 if( v & 1 )
1365 if( p_cea708->window[i].b_defined )
1367 if( p_cea708->window[i].b_visible )
1368 i_ret |= CEA708_STATUS_OUTPUT;
1369 CEA708_Window_Reset( &p_cea708->window[i] );
1371 Debug(printf("%d", i));
1373 Debug(printf("]"));
1374 break;
1375 case CEA708_C1_DLY:
1376 REQUIRE_ARGS_AND_POP_COMMAND(1);
1377 p_cea708->suspended_deadline = p_cea708->i_clock +
1378 VLC_TICK_FROM_MS( cea708_input_buffer_get( ib ) * 100 );
1379 Debug(printf("[DLY]"));
1380 break;
1381 case CEA708_C1_DLC:
1382 POP_COMMAND();
1383 p_cea708->suspended_deadline = VLC_TICK_INVALID;
1384 Debug(printf("[DLC]"));
1385 break;
1386 case CEA708_C1_RST:
1387 POP_COMMAND();
1388 i_ret |= CEA708_STATUS_OUTPUT;
1389 /* FIXME */
1390 break;
1391 case CEA708_C1_SPA:
1392 REQUIRE_ARGS_AND_POP_COMMAND(2);
1393 if( !p_cea708->p_cw->b_defined )
1395 POP_ARGS(2);
1396 break;
1398 v = cea708_input_buffer_get( ib );
1399 p_cea708->p_cw->pen.text_tag = v >> 4;
1400 p_cea708->p_cw->pen.offset = (v >> 2) & 0x03;
1401 p_cea708->p_cw->pen.size = v & 0x03;
1402 v = cea708_input_buffer_get( ib );
1403 p_cea708->p_cw->pen.b_italics = v & 0x80;
1404 p_cea708->p_cw->pen.b_underline = v & 0x40;
1405 p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07;
1406 p_cea708->p_cw->pen.font = v & 0x07;
1407 Debug(printf("[SPA]"));
1408 break;
1409 case CEA708_C1_SPC:
1410 REQUIRE_ARGS_AND_POP_COMMAND(3);
1411 if( !p_cea708->p_cw->b_defined )
1413 POP_ARGS(3);
1414 break;
1416 v = cea708_input_buffer_get( ib );
1417 p_cea708->p_cw->pen.foreground.opacity = v >> 6;
1418 p_cea708->p_cw->pen.foreground.color = v & 0x3F;
1419 v = cea708_input_buffer_get( ib );
1420 p_cea708->p_cw->pen.background.opacity = v >> 6;
1421 p_cea708->p_cw->pen.background.color = v & 0x3F;
1422 v = cea708_input_buffer_get( ib );
1423 p_cea708->p_cw->pen.edge_color = v & 0x3F;
1424 Debug(printf("[SPC]"));
1425 break;
1426 case CEA708_C1_SPL:
1427 REQUIRE_ARGS_AND_POP_COMMAND(2);
1428 if( !p_cea708->p_cw->b_defined )
1430 POP_ARGS(2);
1431 break;
1433 v = cea708_input_buffer_get( ib );
1434 p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS;
1435 v = cea708_input_buffer_get( ib );
1436 p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS;
1437 Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col));
1438 break;
1439 case CEA708_C1_SWA:
1440 REQUIRE_ARGS_AND_POP_COMMAND(4);
1441 if( !p_cea708->p_cw->b_defined )
1443 POP_ARGS(4);
1444 break;
1446 v = cea708_input_buffer_get( ib );
1447 p_cea708->p_cw->style.fill_opacity = v >> 6;
1448 p_cea708->p_cw->style.fill_color_color = v & 0x3F;
1449 v = cea708_input_buffer_get( ib );
1450 p_cea708->p_cw->style.border_color_color = v & 0x3F;
1451 p_cea708->p_cw->style.border_type = v >> 6;
1452 v = cea708_input_buffer_get( ib );
1453 p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5);
1454 p_cea708->p_cw->style.b_word_wrap = v & 0x40;
1455 p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03;
1456 p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03;
1457 p_cea708->p_cw->style.justify = v & 0x03;
1458 v = cea708_input_buffer_get( ib );
1459 p_cea708->p_cw->style.effect_speed = v >> 4;
1460 p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03;
1461 p_cea708->p_cw->style.display_effect = v & 0x03;
1462 Debug(printf("[SWA]"));
1463 break;
1465 default:
1466 if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 )
1468 POP_COMMAND();
1469 Debug(printf("[CW%d]", code - CEA708_C1_CW0));
1470 if( p_cea708->window[code - CEA708_C1_CW0].b_defined )
1471 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0];
1473 else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 )
1475 REQUIRE_ARGS_AND_POP_COMMAND(6);
1476 Debug(printf("[DF%d]", code - CEA708_C1_DF0));
1477 /* also sets current window */
1478 p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0];
1479 v = cea708_input_buffer_get( ib );
1480 if( p_cea708->p_cw->b_defined &&
1481 !p_cea708->p_cw->b_visible != !(v & 0x20) )
1482 i_ret |= CEA708_STATUS_OUTPUT;
1483 p_cea708->p_cw->b_visible = v & 0x20;
1484 p_cea708->p_cw->b_row_lock = v & 0x10;
1485 p_cea708->p_cw->b_column_lock = v & 0x08;
1486 p_cea708->p_cw->i_priority = v & 0x07;
1487 v = cea708_input_buffer_get( ib );
1488 p_cea708->p_cw->b_relative = v & 0x80;
1489 p_cea708->p_cw->i_anchor_offset_v = v & 0x7F;
1490 v = cea708_input_buffer_get( ib );
1491 p_cea708->p_cw->i_anchor_offset_h = v;
1492 v = cea708_input_buffer_get( ib );
1493 p_cea708->p_cw->anchor_point = v >> 4;
1494 p_cea708->p_cw->i_row_count = v & 0x0F;
1495 v = cea708_input_buffer_get( ib );
1496 p_cea708->p_cw->i_col_count = v & 0x3F;
1497 v = cea708_input_buffer_get( ib );
1498 /* zero values style set on init, avoid dealing with updt case */
1499 i = (v >> 3) & 0x07; /* Window style id */
1500 if( i > 0 && !p_cea708->p_cw->b_defined )
1501 p_cea708->p_cw->style = cea708_default_window_styles[i];
1502 i = v & 0x07; /* Pen style id */
1503 if( i > 0 && !p_cea708->p_cw->b_defined )
1504 p_cea708->p_cw->pen = cea708_default_pen_styles[i];
1505 p_cea708->p_cw->b_defined = true;
1507 else
1509 Debug(printf("{%2.2x}", code));
1510 POP_COMMAND();
1514 return i_ret;
1517 static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 )
1519 cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1520 POP_COMMAND();
1522 if( !p_cea708->p_cw->b_defined )
1523 return CEA708_STATUS_OK;
1525 uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6,
1526 0x80 | (code & 0x3f),
1527 0, 0};
1529 CEA708_Window_Write( utf8, p_cea708->p_cw );
1530 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1532 return CEA708_STATUS_OK;
1535 static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 )
1537 if( !p_cea708->p_cw->b_defined )
1538 return CEA708_STATUS_OK;
1540 uint8_t out[4] = { '?', 0, 0, 0 };
1541 static const struct {
1542 uint8_t c;
1543 uint8_t utf8[4];
1544 } code2utf8[] = {
1545 /* G2 */
1546 { 0x20, { 0x20 } },// transparent space [*** will need special handling]
1547 { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling]
1548 { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS
1549 { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON
1550 { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE
1551 { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK
1552 { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK
1553 { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK
1554 { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK
1555 { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK
1556 { 0x35, { 0xe2,0x80,0xa2 } },// BULLET
1557 { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM)
1558 { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON
1559 { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE
1560 { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK
1561 { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS
1562 { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH
1563 { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS
1564 { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS
1565 { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS
1566 { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL
1567 { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT
1568 { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT
1569 { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL
1570 { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT
1571 { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT
1572 /* G3 */
1573 { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C)
1576 for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ )
1578 if( code2utf8[i].c == code )
1580 memcpy( out, code2utf8[i].utf8, 4 );
1581 if(out[0] < 0xf0)
1583 if(out[0] < 0x80)
1584 out[1] = 0;
1585 else if(out[0] < 0xe0)
1586 out[2] = 0;
1587 else
1588 out[3] = 0;
1590 break;
1594 CEA708_Window_Write( out, p_cea708->p_cw );
1596 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1598 return CEA708_STATUS_OK;
1601 static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 )
1603 if( !p_cea708->p_cw->b_defined )
1604 return CEA708_STATUS_OK;
1606 uint8_t out[4] = { '?', 0, 0, 0 };
1608 /* adapted from codepoint conversion from strings.h */
1609 if( ucs2 <= 0x7F )
1611 out[0] = ucs2;
1613 else if( ucs2 <= 0x7FF )
1615 out[0] = 0xC0 | (ucs2 >> 6);
1616 out[1] = 0x80 | (ucs2 & 0x3F);
1618 else
1620 out[0] = 0xE0 | (ucs2 >> 12);
1621 out[1] = 0x80 | ((ucs2 >> 6) & 0x3F);
1622 out[2] = 0x80 | (ucs2 & 0x3F);
1625 CEA708_Window_Write( out, p_cea708->p_cw );
1627 p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1629 return CEA708_STATUS_OK;
1632 static void CEA708_Decode_ServiceBuffer( cea708_t *h )
1634 for( ;; )
1636 const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer );
1637 if( i_in == 0 )
1638 break;
1640 int i_ret;
1641 uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 );
1643 if( c < 0x20 )
1644 i_ret = CEA708_Decode_C0( c, h );
1645 else if( c >= 0x20 && c <=0x7F )
1646 i_ret = CEA708_Decode_G0( c, h );
1647 else if( c >= 0x80 && c <= 0x9F )
1648 i_ret = CEA708_Decode_C1( c, h );
1649 else if( c > 0x9F )
1650 i_ret = CEA708_Decode_G1( c, h );
1652 if( i_ret & CEA708_STATUS_OUTPUT )
1653 CEA708_Output( h );
1655 if( i_ret & CEA708_STATUS_STARVING )
1656 break;
1658 /* Update internal clock */
1659 const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer );
1660 if( i_consumed )
1661 h->i_clock += vlc_tick_from_samples(1, 9600) * i_consumed;
1665 void CEA708_Decoder_Push( cea708_t *h, vlc_tick_t i_time,
1666 const uint8_t *p_data, size_t i_data )
1668 /* Set new buffer start time */
1669 h->i_clock = i_time;
1671 for( size_t i=0; i<i_data; )
1673 /* Never push more than buffer */
1674 size_t i_push = cea708_input_buffer_remain(&h->input_buffer);
1675 if( (i_data - i) < i_push )
1676 i_push = (i_data - i);
1677 else
1678 h->suspended_deadline = VLC_TICK_INVALID; /* Full buffer cancels pause */
1680 for( size_t j=0; j<i_push; j++ )
1682 uint8_t byte = p_data[i+j];
1683 cea708_input_buffer_add( &h->input_buffer, byte );
1686 if( h->suspended_deadline != VLC_TICK_INVALID )
1688 /* Decoding is paused */
1689 if ( h->suspended_deadline > h->i_clock )
1691 /* Increase internal clock */
1692 if( i_push )
1693 h->i_clock += vlc_tick_from_samples(1, 1200) * i_push;
1694 continue;
1696 h->suspended_deadline = VLC_TICK_INVALID;
1699 /* Decode Buffer */
1700 CEA708_Decode_ServiceBuffer( h );
1702 i += i_push;