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
, vlc_tick_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 )
90 i_sid
= p_data
[1] & 0x3F;
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
;
118 uint8_t pktsize
= data
[1] & 63;
122 pktsize
= pktsize
* 2 - 1;
124 h
->i_pkt_sequence
= i_pkt_sequence
;
125 h
->i_total_data
= pktsize
;
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)
174 CEA708_STATUS_OK
= 1 << 0,
175 CEA708_STATUS_STARVING
= 1 << 1,
176 CEA708_STATUS_OUTPUT
= 1 << 2,
181 CEA708_C0_NUL
= 0x00,
182 CEA708_C0_ETX
= 0x03,
186 CEA708_C0_HCR
= 0x0E,
187 CEA708_C0_EXT1
= 0x10,
188 CEA708_C0_P16
= 0x18,
193 CEA708_C1_CW0
= 0x80,
194 CEA708_C1_CW7
= 0x87,
203 CEA708_C1_SPA
= 0x90,
206 CEA708_C1_SWA
= 0x97,
208 CEA708_C1_DF7
= 0x9F,
213 uint8_t ringbuffer
[CEA708_SERVICE_INPUT_BUFFER
];
216 } cea708_input_buffer_t
;
218 static void cea708_input_buffer_init(cea708_input_buffer_t
*ib
)
224 static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t
*ib
)
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
)
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
;
256 enum cea708_opacity_e
258 CEA708_OPACITY_SOLID
= 0,
259 CEA708_OPACITY_FLASH
,
260 CEA708_OPACITY_TRANSLUCENT
,
261 CEA708_OPACITY_TRANSPARENT
,
268 CEA708_EDGE_DEPRESSED
,
270 CEA708_EDGE_LEFT_DROP_SHADOW
,
271 CEA708_EDGE_RIGHT_DROP_SHADOW
,
278 CEA708_PEN_SIZE_SMALL
= 0,
279 CEA708_PEN_SIZE_STANDARD
,
280 CEA708_PEN_SIZE_LARGE
,
284 CEA708_FONT_UNDEFINED
= 0,
285 CEA708_FONT_MONOSPACED
,
287 CEA708_FONT_MONO_SANS_SERIF
,
288 CEA708_FONT_PROP_SANS_SERIF
,
291 CEA708_FONT_SMALL_CAPS
,
295 CEA708_TAG_DIALOG
= 0,
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,
311 CEA708_PEN_OFFSET_SUBSCRIPT
= 0,
312 CEA708_PEN_OFFSET_NORMAL
,
313 CEA708_PEN_OFFSET_SUPERSCRIPT
,
320 enum cea708_opacity_e opacity
;
321 } foreground
, background
;
323 enum cea708_edge_e edge_type
;
324 } cea708_pen_style_t
;
328 cea708_pen_style_t style
;
337 CEA708_WA_JUSTIFY_LEFT
= 0,
338 CEA708_WA_JUSTIFY_RIGHT
,
339 CEA708_WA_JUSTIFY_CENTER
,
340 CEA708_WA_JUSTIFY_FULL
,
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
;
352 CEA708_WA_EFFECT_SNAP
= 0,
353 CEA708_WA_EFFECT_FADE
,
354 CEA708_WA_EFFECT_WIPE
,
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
];
373 static void cea708_text_row_Delete( cea708_text_row_t
*p_row
)
378 static cea708_text_row_t
* cea708_text_row_New( void )
380 cea708_text_row_t
*p_row
= malloc( sizeof(*p_row
) );
383 p_row
->firstcol
= CEA708_WINDOW_MAX_COLS
;
385 memset(p_row
->characters
, 0, 4 * CEA708_WINDOW_MAX_COLS
);
392 cea708_text_row_t
* rows
[CEA708_WINDOW_MAX_ROWS
];
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
,
410 uint8_t i_anchor_offset_v
;
411 uint8_t i_anchor_offset_h
;
413 /* Extras row for window scroll */
420 uint8_t b_column_lock
;
423 cea708_window_style_t style
;
424 cea708_pen_style_t pen
;
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 */
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,\
460 CEA708_PEN_OFFSET_NORMAL,\
463 { 0x2A, CEA708_OPACITY_SOLID, },\
464 { 0x00, bgopacity, },\
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) \
485 CEA708_WA_DIRECTION_LTR,\
487 CEA708_WA_EFFECT_SNAP,\
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
] );
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
)
543 if( p_w
->style
.print_direction
== CEA708_WA_DIRECTION_LTR
&&
544 p_w
->style
.justify
== CEA708_WA_JUSTIFY_LEFT
)
547 if( p_w
->style
.print_direction
== CEA708_WA_DIRECTION_RTL
&&
548 p_w
->style
.justify
== CEA708_WA_JUSTIFY_RIGHT
)
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
;
566 static uint8_t CEA708_Window_MaxCol( const cea708_window_t
*p_w
)
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
;
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
)
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
)
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
);
609 if( i
== p_w
->i_firstrow
)
611 else if( i
== p_w
->i_lastrow
)
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
);
630 if( i
== p_w
->i_firstrow
)
632 else if( i
== p_w
->i_lastrow
)
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
;
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
;
656 static void CEA708_Window_Scroll( cea708_window_t
*p_w
)
658 if( CEA708_Window_RowCount( p_w
) == 0 )
661 switch( p_w
->style
.scroll_direction
)
663 case CEA708_WA_DIRECTION_LTR
:
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 */
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
) );
680 case CEA708_WA_DIRECTION_RTL
:
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 */
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
) );
697 case CEA708_WA_DIRECTION_TB
:
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
;
707 case CEA708_WA_DIRECTION_BT
:
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
;
720 static void CEA708_Window_CarriageReturn( cea708_window_t
*p_w
)
722 switch( p_w
->style
.scroll_direction
)
724 case CEA708_WA_DIRECTION_LTR
:
726 CEA708_Window_ColCount( p_w
) < p_w
->i_col_count
)
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;
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
)
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;
742 case CEA708_WA_DIRECTION_TB
:
744 CEA708_Window_RowCount( p_w
) < p_w
->i_row_count
)
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;
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
)
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;
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
)
771 CEA708_Window_CarriageReturn( p_w
);
773 case CEA708_WA_DIRECTION_RTL
:
777 CEA708_Window_CarriageReturn( p_w
);
779 case CEA708_WA_DIRECTION_TB
:
780 if( p_w
->row
+ 1 < CEA708_WINDOW_MAX_ROWS
)
783 CEA708_Window_CarriageReturn( p_w
);
785 case CEA708_WA_DIRECTION_BT
:
789 CEA708_Window_CarriageReturn( p_w
);
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
)
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
);
823 cea708_text_row_t
*p_row
= p_w
->rows
[p_w
->row
];
826 p_w
->rows
[p_w
->row
] = p_row
= cea708_text_row_New();
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};
851 return (value
[(c
>> 4) & 0x03] << 16) |
852 (value
[(c
>> 2) & 0x03] << 8) |
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
;
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
;
884 s
->i_style_flags
|= STYLE_ITALIC
;
885 if( ps
->b_underline
)
886 s
->i_style_flags
|= STYLE_UNDERLINE
;
891 case CEA708_FONT_UNDEFINED
:
892 case CEA708_FONT_MONOSPACED
:
893 case CEA708_FONT_MONO_SANS_SERIF
:
894 s
->i_style_flags
|= STYLE_MONOSPACED
;
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
:
906 case CEA708_PEN_SIZE_SMALL
:
907 s
->f_font_relsize
= CEA708_FONTRELSIZE_SMALL
;
909 case CEA708_PEN_SIZE_LARGE
:
910 s
->f_font_relsize
= CEA708_FONTRELSIZE_LARGE
;
913 s
->f_font_relsize
= CEA708_FONTRELSIZE_STANDARD
;
918 static text_segment_t
* CEA708CharsToSegment( const cea708_text_row_t
*p_row
,
919 uint8_t i_start
, uint8_t i_end
,
922 text_segment_t
*p_segment
= text_segment_New( 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
);
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
];
945 p_segment
->psz_text
[offsetw
++] = ' ';
952 p_segment
->psz_text
[offsetw
++] = '\n';
953 p_segment
->psz_text
[offsetw
] = '\0';
958 static text_segment_t
* CEA708RowToSegments( const cea708_text_row_t
*p_row
,
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
) );
973 pp_last
= &((*pp_last
)->p_next
);
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 )
987 if( p_region
== NULL
&& !(p_region
= SubpictureUpdaterSysRegionNew()) )
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
++ )
996 *pp_last
= CEA708RowToSegments( p_w
->rows
[i
], i
< p_w
->i_lastrow
);
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;
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
);
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
)
1063 SubpictureUpdaterSysRegionAdd( p_region
, p_newregion
);
1064 p_region
= p_newregion
;
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;
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
);
1110 cea708_t
* CEA708_Decoder_New( decoder_t
*p_dec
)
1112 cea708_t
*p_cea708
= malloc( sizeof(cea708_t
) );
1115 CEA708_Decoder_Init( p_cea708
);
1116 p_cea708
->p_dec
= p_dec
;
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
);
1132 decoder_QueueSub( p_cea708
->p_dec
, p_spu
);
1135 static int CEA708_Decode_C0( uint8_t code
, cea708_t
*p_cea708
)
1139 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1140 int i_ret
= CEA708_STATUS_OK
;
1149 if( p_cea708
->b_text_waiting
)
1151 i_ret
|= CEA708_STATUS_OUTPUT
;
1152 p_cea708
->b_text_waiting
= false;
1157 if( !p_cea708
->p_cw
->b_defined
)
1159 CEA708_Window_Backward( p_cea708
->p_cw
);
1160 p_cea708
->b_text_waiting
= true;
1164 if( !p_cea708
->p_cw
->b_defined
)
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;
1173 if( !p_cea708
->p_cw
->b_defined
)
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
;
1184 if( !p_cea708
->p_cw
->b_defined
)
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
;
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 */
1208 if( cea708_input_buffer_size( ib
) < 2 + i
)
1209 return CEA708_STATUS_STARVING
;
1213 /* C3 extended code set */
1214 else if( v
> 0x7f && v
< 0xa0 )
1220 if( cea708_input_buffer_size( ib
) < 2 + i
)
1221 return CEA708_STATUS_STARVING
;
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
;
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
));
1244 Debug(printf("[UNK %2.2x]", code
));
1247 Debug(printf("[C0 %x]", code
));
1251 static int CEA708_Decode_G0( uint8_t code
, cea708_t
*p_cea708
)
1253 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1255 int i_ret
= CEA708_STATUS_OK
;
1257 if( !p_cea708
->p_cw
->b_defined
)
1260 uint8_t utf8
[4] = {code
,0x00,0x00,0x00};
1262 if(code
== 0x7F) // Music note
1269 CEA708_Window_Write( utf8
, p_cea708
->p_cw
);
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
;
1284 static int CEA708_Decode_C1( uint8_t code
, cea708_t
*p_cea708
)
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;
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
++ )
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
));
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
++ )
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
));
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
++ )
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
));
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
++ )
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
));
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
++ )
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
));
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]"));
1383 p_cea708
->suspended_deadline
= VLC_TICK_INVALID
;
1384 Debug(printf("[DLC]"));
1388 i_ret
|= CEA708_STATUS_OUTPUT
;
1392 REQUIRE_ARGS_AND_POP_COMMAND(2);
1393 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1410 REQUIRE_ARGS_AND_POP_COMMAND(3);
1411 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1427 REQUIRE_ARGS_AND_POP_COMMAND(2);
1428 if( !p_cea708
->p_cw
->b_defined
)
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
));
1440 REQUIRE_ARGS_AND_POP_COMMAND(4);
1441 if( !p_cea708
->p_cw
->b_defined
)
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]"));
1466 if( code
>= CEA708_C1_CW0
&& code
<= CEA708_C1_CW7
)
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;
1509 Debug(printf("{%2.2x}", code
));
1517 static int CEA708_Decode_G1( uint8_t code
, cea708_t
*p_cea708
)
1519 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
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),
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 {
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
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 );
1585 else if(out
[0] < 0xe0)
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 */
1613 else if( ucs2
<= 0x7FF )
1615 out
[0] = 0xC0 | (ucs2
>> 6);
1616 out
[1] = 0x80 | (ucs2
& 0x3F);
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
)
1636 const uint8_t i_in
= cea708_input_buffer_size( &h
->input_buffer
);
1641 uint8_t c
= cea708_input_buffer_peek( &h
->input_buffer
, 0 );
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
);
1650 i_ret
= CEA708_Decode_G1( c
, h
);
1652 if( i_ret
& CEA708_STATUS_OUTPUT
)
1655 if( i_ret
& CEA708_STATUS_STARVING
)
1658 /* Update internal clock */
1659 const uint8_t i_consumed
= i_in
- cea708_input_buffer_size( &h
->input_buffer
);
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
);
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 */
1693 h
->i_clock
+= vlc_tick_from_samples(1, 1200) * i_push
;
1696 h
->suspended_deadline
= VLC_TICK_INVALID
;
1700 CEA708_Decode_ServiceBuffer( h
);