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 *****************************************************************************/
24 #include <vlc_common.h>
25 #include <vlc_codec.h>
26 #include <vlc_subpicture.h>
34 #define Debug(code) code
39 /*****************************************************************************
40 * Demuxing / Agreggation
41 *****************************************************************************/
44 int8_t i_pkt_sequence
;
47 uint8_t data
[CEA708_DTVCC_MAX_PKT_SIZE
];
49 service_data_hdlr_t p_callback
;
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
)
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
) );
71 CEA708_DTVCC_Demuxer_Flush( 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
)
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 )
88 else if( i_sid
== 0x07 )
92 i_sid
= p_data
[1] & 0x3F;
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
;
120 uint8_t pktsize
= data
[1] & 63;
124 pktsize
= pktsize
* 2 - 1;
126 h
->i_pkt_sequence
= i_pkt_sequence
;
127 h
->i_total_data
= pktsize
;
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 / \
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)
176 CEA708_STATUS_OK
= 1 << 0,
177 CEA708_STATUS_STARVING
= 1 << 1,
178 CEA708_STATUS_OUTPUT
= 1 << 2,
183 CEA708_C0_NUL
= 0x00,
184 CEA708_C0_ETX
= 0x03,
188 CEA708_C0_HCR
= 0x0E,
189 CEA708_C0_EXT1
= 0x10,
190 CEA708_C0_P16
= 0x18,
195 CEA708_C1_CW0
= 0x80,
196 CEA708_C1_CW7
= 0x87,
205 CEA708_C1_SPA
= 0x90,
208 CEA708_C1_SWA
= 0x97,
210 CEA708_C1_DF7
= 0x9F,
215 uint8_t ringbuffer
[CEA708_SERVICE_INPUT_BUFFER
];
218 } cea708_input_buffer_t
;
220 static void cea708_input_buffer_init(cea708_input_buffer_t
*ib
)
226 static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t
*ib
)
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
)
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
;
258 enum cea708_opacity_e
260 CEA708_OPACITY_SOLID
= 0,
261 CEA708_OPACITY_FLASH
,
262 CEA708_OPACITY_TRANSLUCENT
,
263 CEA708_OPACITY_TRANSPARENT
,
270 CEA708_EDGE_DEPRESSED
,
272 CEA708_EDGE_LEFT_DROP_SHADOW
,
273 CEA708_EDGE_RIGHT_DROP_SHADOW
,
280 CEA708_PEN_SIZE_SMALL
= 0,
281 CEA708_PEN_SIZE_STANDARD
,
282 CEA708_PEN_SIZE_LARGE
,
286 CEA708_FONT_UNDEFINED
= 0,
287 CEA708_FONT_MONOSPACED
,
289 CEA708_FONT_MONO_SANS_SERIF
,
290 CEA708_FONT_PROP_SANS_SERIF
,
293 CEA708_FONT_SMALL_CAPS
,
297 CEA708_TAG_DIALOG
= 0,
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,
313 CEA708_PEN_OFFSET_SUBSCRIPT
= 0,
314 CEA708_PEN_OFFSET_NORMAL
,
315 CEA708_PEN_OFFSET_SUPERSCRIPT
,
322 enum cea708_opacity_e opacity
;
323 } foreground
, background
;
325 enum cea708_edge_e edge_type
;
326 } cea708_pen_style_t
;
330 cea708_pen_style_t style
;
339 CEA708_WA_JUSTIFY_LEFT
= 0,
340 CEA708_WA_JUSTIFY_RIGHT
,
341 CEA708_WA_JUSTIFY_CENTER
,
342 CEA708_WA_JUSTIFY_FULL
,
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
;
354 CEA708_WA_EFFECT_SNAP
= 0,
355 CEA708_WA_EFFECT_FADE
,
356 CEA708_WA_EFFECT_WIPE
,
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
];
375 static void cea708_text_row_Delete( cea708_text_row_t
*p_row
)
380 static cea708_text_row_t
* cea708_text_row_New( void )
382 cea708_text_row_t
*p_row
= malloc( sizeof(*p_row
) );
385 p_row
->firstcol
= CEA708_WINDOW_MAX_COLS
;
387 memset(p_row
->characters
, 0, 4 * CEA708_WINDOW_MAX_COLS
);
394 cea708_text_row_t
* rows
[CEA708_WINDOW_MAX_ROWS
];
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
,
412 uint8_t i_anchor_offset_v
;
413 uint8_t i_anchor_offset_h
;
415 /* Extras row for window scroll */
422 uint8_t b_column_lock
;
425 cea708_window_style_t style
;
426 cea708_pen_style_t pen
;
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 */
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,\
462 CEA708_PEN_OFFSET_NORMAL,\
465 { 0x2A, CEA708_OPACITY_SOLID, },\
466 { 0x00, bgopacity, },\
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) \
487 CEA708_WA_DIRECTION_LTR,\
489 CEA708_WA_EFFECT_SNAP,\
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
] );
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
)
545 if( p_w
->style
.print_direction
== CEA708_WA_DIRECTION_LTR
&&
546 p_w
->style
.justify
== CEA708_WA_JUSTIFY_LEFT
)
549 if( p_w
->style
.print_direction
== CEA708_WA_DIRECTION_RTL
&&
550 p_w
->style
.justify
== CEA708_WA_JUSTIFY_RIGHT
)
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
;
568 static uint8_t CEA708_Window_MaxCol( const cea708_window_t
*p_w
)
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
;
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
)
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
)
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
);
611 if( i
== p_w
->i_firstrow
)
613 else if( i
== p_w
->i_lastrow
)
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
);
632 if( i
== p_w
->i_firstrow
)
634 else if( i
== p_w
->i_lastrow
)
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
;
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
;
658 static void CEA708_Window_Scroll( cea708_window_t
*p_w
)
660 if( CEA708_Window_RowCount( p_w
) == 0 )
663 switch( p_w
->style
.scroll_direction
)
665 case CEA708_WA_DIRECTION_LTR
:
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 */
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
) );
682 case CEA708_WA_DIRECTION_RTL
:
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 */
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
) );
699 case CEA708_WA_DIRECTION_TB
:
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
;
709 case CEA708_WA_DIRECTION_BT
:
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
;
722 static void CEA708_Window_CarriageReturn( cea708_window_t
*p_w
)
724 switch( p_w
->style
.scroll_direction
)
726 case CEA708_WA_DIRECTION_LTR
:
728 CEA708_Window_ColCount( p_w
) < p_w
->i_col_count
)
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;
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
)
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;
744 case CEA708_WA_DIRECTION_TB
:
746 CEA708_Window_RowCount( p_w
) < p_w
->i_row_count
)
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;
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
)
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;
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
)
773 CEA708_Window_CarriageReturn( p_w
);
775 case CEA708_WA_DIRECTION_RTL
:
779 CEA708_Window_CarriageReturn( p_w
);
781 case CEA708_WA_DIRECTION_TB
:
782 if( p_w
->row
+ 1 < CEA708_WINDOW_MAX_ROWS
)
785 CEA708_Window_CarriageReturn( p_w
);
787 case CEA708_WA_DIRECTION_BT
:
791 CEA708_Window_CarriageReturn( p_w
);
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
)
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
);
825 cea708_text_row_t
*p_row
= p_w
->rows
[p_w
->row
];
828 p_w
->rows
[p_w
->row
] = p_row
= cea708_text_row_New();
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};
853 return (value
[(c
>> 4) & 0x03] << 16) |
854 (value
[(c
>> 2) & 0x03] << 8) |
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
;
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
;
886 s
->i_style_flags
|= STYLE_ITALIC
;
887 if( ps
->b_underline
)
888 s
->i_style_flags
|= STYLE_UNDERLINE
;
893 case CEA708_FONT_UNDEFINED
:
894 case CEA708_FONT_MONOSPACED
:
895 case CEA708_FONT_MONO_SANS_SERIF
:
896 s
->i_style_flags
|= STYLE_MONOSPACED
;
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
:
908 case CEA708_PEN_SIZE_SMALL
:
909 s
->f_font_relsize
= CEA708_FONTRELSIZE_SMALL
;
911 case CEA708_PEN_SIZE_LARGE
:
912 s
->f_font_relsize
= CEA708_FONTRELSIZE_LARGE
;
915 s
->f_font_relsize
= CEA708_FONTRELSIZE_STANDARD
;
920 static text_segment_t
* CEA708CharsToSegment( const cea708_text_row_t
*p_row
,
921 uint8_t i_start
, uint8_t i_end
,
924 text_segment_t
*p_segment
= text_segment_New( 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
);
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
];
947 p_segment
->psz_text
[offsetw
++] = ' ';
954 p_segment
->psz_text
[offsetw
++] = '\n';
955 p_segment
->psz_text
[offsetw
] = '\0';
960 static text_segment_t
* CEA708RowToSegments( const cea708_text_row_t
*p_row
,
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
) );
975 pp_last
= &((*pp_last
)->p_next
);
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 )
989 if( p_region
== NULL
&& !(p_region
= SubpictureUpdaterSysRegionNew()) )
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
++ )
998 *pp_last
= CEA708RowToSegments( p_w
->rows
[i
], i
< p_w
->i_lastrow
);
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;
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
);
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
)
1065 SubpictureUpdaterSysRegionAdd( p_region
, p_newregion
);
1066 p_region
= p_newregion
;
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;
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
);
1112 cea708_t
* CEA708_Decoder_New( decoder_t
*p_dec
)
1114 cea708_t
*p_cea708
= malloc( sizeof(cea708_t
) );
1117 CEA708_Decoder_Init( p_cea708
);
1118 p_cea708
->p_dec
= p_dec
;
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
);
1134 decoder_QueueSub( p_cea708
->p_dec
, p_spu
);
1137 static int CEA708_Decode_C0( uint8_t code
, cea708_t
*p_cea708
)
1141 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1142 int i_ret
= CEA708_STATUS_OK
;
1151 if( p_cea708
->b_text_waiting
)
1153 i_ret
|= CEA708_STATUS_OUTPUT
;
1154 p_cea708
->b_text_waiting
= false;
1159 if( !p_cea708
->p_cw
->b_defined
)
1161 CEA708_Window_Backward( p_cea708
->p_cw
);
1162 p_cea708
->b_text_waiting
= true;
1166 if( !p_cea708
->p_cw
->b_defined
)
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;
1175 if( !p_cea708
->p_cw
->b_defined
)
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
;
1186 if( !p_cea708
->p_cw
->b_defined
)
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
;
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 */
1210 if( cea708_input_buffer_size( ib
) < 2 + i
)
1211 return CEA708_STATUS_STARVING
;
1215 /* C3 extended code set */
1216 else if( v
> 0x7f && v
< 0xa0 )
1222 if( cea708_input_buffer_size( ib
) < 2 + i
)
1223 return CEA708_STATUS_STARVING
;
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
;
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
));
1246 Debug(printf("[UNK %2.2x]", code
));
1249 Debug(printf("[C0 %x]", code
));
1253 static int CEA708_Decode_G0( uint8_t code
, cea708_t
*p_cea708
)
1255 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1257 int i_ret
= CEA708_STATUS_OK
;
1259 if( !p_cea708
->p_cw
->b_defined
)
1262 uint8_t utf8
[4] = {code
,0x00,0x00,0x00};
1264 if(code
== 0x7F) // Music note
1271 CEA708_Window_Write( utf8
, p_cea708
->p_cw
);
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
;
1286 static int CEA708_Decode_C1( uint8_t code
, cea708_t
*p_cea708
)
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;
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
;
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
++ )
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
));
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
++ )
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
));
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
++ )
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
));
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
++ )
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
));
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]"));
1381 p_cea708
->suspended_deadline
= 0;
1382 Debug(printf("[DLC]"));
1386 i_ret
|= CEA708_STATUS_OUTPUT
;
1390 REQUIRE_ARGS_AND_POP_COMMAND(2);
1391 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1408 REQUIRE_ARGS_AND_POP_COMMAND(3);
1409 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1425 REQUIRE_ARGS_AND_POP_COMMAND(2);
1426 if( !p_cea708
->p_cw
->b_defined
)
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
));
1438 REQUIRE_ARGS_AND_POP_COMMAND(4);
1439 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1464 if( code
>= CEA708_C1_CW0
&& code
<= CEA708_C1_CW7
)
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;
1507 Debug(printf("{%2.2x}", code
));
1515 static int CEA708_Decode_G1( uint8_t code
, cea708_t
*p_cea708
)
1517 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
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),
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 {
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
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 );
1583 else if(out
[0] < 0xe0)
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 */
1611 else if( ucs2
<= 0x7FF )
1613 out
[0] = 0xC0 | (ucs2
>> 6);
1614 out
[1] = 0x80 | (ucs2
& 0x3F);
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
)
1634 const uint8_t i_in
= cea708_input_buffer_size( &h
->input_buffer
);
1639 uint8_t c
= cea708_input_buffer_peek( &h
->input_buffer
, 0 );
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
);
1648 i_ret
= CEA708_Decode_G1( c
, h
);
1650 if( i_ret
& CEA708_STATUS_OUTPUT
)
1653 if( i_ret
& CEA708_STATUS_STARVING
)
1656 /* Update internal clock */
1657 const uint8_t i_consumed
= i_in
- cea708_input_buffer_size( &h
->input_buffer
);
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 */
1691 h
->i_clock
+= CLOCK_FREQ
/ 1200 * i_push
;
1694 h
->suspended_deadline
= 0;
1698 CEA708_Decode_ServiceBuffer( h
);