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_lastrow
== CEA708_WINDOW_MAX_ROWS
- 1 )
700 CEA708_Window_Truncate( p_w
, CEA708_WA_DIRECTION_TB
);
701 for( int i
=p_w
->i_lastrow
; i
>= p_w
->i_firstrow
; i
-- )
702 p_w
->rows
[i
+1] = p_w
->rows
[i
];
703 p_w
->rows
[p_w
->i_firstrow
] = NULL
;
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 < p_w
->i_row_count
)
755 CEA708_Window_Scroll( p_w
);
756 p_w
->col
= (p_w
->style
.print_direction
== CEA708_WA_DIRECTION_LTR
) ?
757 0 : CEA708_WINDOW_MAX_COLS
- 1;
762 static void CEA708_Window_Forward( cea708_window_t
*p_w
)
764 switch( p_w
->style
.print_direction
)
766 case CEA708_WA_DIRECTION_LTR
:
767 if( p_w
->col
+ 1 < CEA708_WINDOW_MAX_COLS
)
770 CEA708_Window_CarriageReturn( p_w
);
772 case CEA708_WA_DIRECTION_RTL
:
776 CEA708_Window_CarriageReturn( p_w
);
778 case CEA708_WA_DIRECTION_TB
:
779 if( p_w
->row
+ 1 < CEA708_WINDOW_MAX_ROWS
)
782 CEA708_Window_CarriageReturn( p_w
);
784 case CEA708_WA_DIRECTION_BT
:
788 CEA708_Window_CarriageReturn( p_w
);
793 static void CEA708_Window_Backward( cea708_window_t
*p_w
)
795 static const int reverse
[] =
797 [CEA708_WA_DIRECTION_LTR
] = CEA708_WA_DIRECTION_RTL
,
798 [CEA708_WA_DIRECTION_RTL
] = CEA708_WA_DIRECTION_LTR
,
799 [CEA708_WA_DIRECTION_TB
] = CEA708_WA_DIRECTION_BT
,
800 [CEA708_WA_DIRECTION_BT
] = CEA708_WA_DIRECTION_TB
,
802 int save
= p_w
->style
.print_direction
;
803 p_w
->style
.print_direction
= reverse
[p_w
->style
.print_direction
];
804 CEA708_Window_Forward( p_w
);
805 p_w
->style
.print_direction
= save
;
808 static void CEA708_Window_Write( const uint8_t c
[4], cea708_window_t
*p_w
)
810 if( !p_w
->b_defined
)
814 if( unlikely( p_w
->row
>= CEA708_WINDOW_MAX_ROWS
||
815 p_w
->col
>= CEA708_WINDOW_MAX_COLS
) )
817 assert( p_w
->row
< CEA708_WINDOW_MAX_ROWS
);
818 assert( p_w
->col
< CEA708_WINDOW_MAX_COLS
);
822 cea708_text_row_t
*p_row
= p_w
->rows
[p_w
->row
];
825 p_w
->rows
[p_w
->row
] = p_row
= cea708_text_row_New();
828 if( p_w
->row
< p_w
->i_firstrow
)
829 p_w
->i_firstrow
= p_w
->row
;
830 if( p_w
->row
> p_w
->i_lastrow
)
831 p_w
->i_lastrow
= p_w
->row
;
834 memcpy( &p_row
->characters
[p_w
->col
* 4U], c
, 4 );
835 p_row
->styles
[p_w
->col
] = p_w
->pen
;
836 if( p_w
->col
< p_row
->firstcol
)
837 p_row
->firstcol
= p_w
->col
;
838 if( p_w
->col
> p_row
->lastcol
)
839 p_row
->lastcol
= p_w
->col
;
841 CEA708_Window_Forward( p_w
);
843 Debug(printf("\033[0;33m%s\033[0m", c
));
846 static uint32_t CEA708ColorConvert( uint8_t c
)
848 const uint32_t value
[4] = {0x00,0x3F,0xF0,0xFF};
850 return (value
[(c
>> 4) & 0x03] << 16) |
851 (value
[(c
>> 2) & 0x03] << 8) |
855 static uint8_t CEA708AlphaConvert( uint8_t c
)
857 if( c
== CEA708_OPACITY_TRANSLUCENT
)
858 return STYLE_ALPHA_OPAQUE
/ 2;
859 else if( c
== CEA708_OPACITY_TRANSPARENT
)
860 return STYLE_ALPHA_TRANSPARENT
;
862 return STYLE_ALPHA_OPAQUE
;
865 static void CEA708PenStyleToSegment( const cea708_pen_style_t
*ps
, text_style_t
*s
)
867 if( ps
->background
.opacity
!= CEA708_OPACITY_TRANSPARENT
)
869 s
->i_background_alpha
= CEA708AlphaConvert( ps
->background
.opacity
);
870 s
->i_style_flags
|= STYLE_BACKGROUND
;
871 s
->i_background_color
= CEA708ColorConvert( ps
->background
.color
);
872 s
->i_features
|= STYLE_HAS_BACKGROUND_COLOR
|STYLE_HAS_BACKGROUND_ALPHA
;
873 if( ps
->background
.opacity
== CEA708_OPACITY_FLASH
)
874 s
->i_style_flags
|= STYLE_BLINK_BACKGROUND
;
876 s
->i_font_color
= CEA708ColorConvert( ps
->foreground
.color
);
877 s
->i_font_alpha
= CEA708AlphaConvert( ps
->foreground
.opacity
);
878 s
->i_features
|= STYLE_HAS_FONT_ALPHA
|STYLE_HAS_FONT_COLOR
;
879 if( ps
->foreground
.opacity
== CEA708_OPACITY_FLASH
)
880 s
->i_style_flags
|= STYLE_BLINK_FOREGROUND
;
883 s
->i_style_flags
|= STYLE_ITALIC
;
884 if( ps
->b_underline
)
885 s
->i_style_flags
|= STYLE_UNDERLINE
;
890 case CEA708_FONT_UNDEFINED
:
891 case CEA708_FONT_MONOSPACED
:
892 case CEA708_FONT_MONO_SANS_SERIF
:
893 s
->i_style_flags
|= STYLE_MONOSPACED
;
895 case CEA708_FONT_PROP
:
896 case CEA708_FONT_PROP_SANS_SERIF
:
897 case CEA708_FONT_CASUAL
:
898 case CEA708_FONT_CURSIVE
:
899 case CEA708_FONT_SMALL_CAPS
:
905 case CEA708_PEN_SIZE_SMALL
:
906 s
->f_font_relsize
= CEA708_FONTRELSIZE_SMALL
;
908 case CEA708_PEN_SIZE_LARGE
:
909 s
->f_font_relsize
= CEA708_FONTRELSIZE_LARGE
;
912 s
->f_font_relsize
= CEA708_FONTRELSIZE_STANDARD
;
917 static text_segment_t
* CEA708CharsToSegment( const cea708_text_row_t
*p_row
,
918 uint8_t i_start
, uint8_t i_end
,
921 text_segment_t
*p_segment
= text_segment_New( NULL
);
925 p_segment
->style
= text_style_Create( STYLE_NO_DEFAULTS
);
926 if( p_segment
->style
)
927 CEA708PenStyleToSegment( &p_row
->styles
[i_start
], p_segment
->style
);
929 p_segment
->psz_text
= malloc( 1U + !!b_newline
+ (i_end
- i_start
+ 1) * 4U );
930 if( !p_segment
->psz_text
)
932 text_segment_Delete( p_segment
);
937 for( uint8_t i
=i_start
; i
<=i_end
; i
++ )
939 for( size_t j
=0; j
<4; j
++ )
941 if( p_row
->characters
[i
* 4 + j
] != 0 )
942 p_segment
->psz_text
[offsetw
++] = p_row
->characters
[i
* 4 + j
];
944 p_segment
->psz_text
[offsetw
++] = ' ';
951 p_segment
->psz_text
[offsetw
++] = '\n';
952 p_segment
->psz_text
[offsetw
] = '\0';
957 static text_segment_t
* CEA708RowToSegments( const cea708_text_row_t
*p_row
,
960 text_segment_t
*p_segments
= NULL
;
961 text_segment_t
**pp_last
= &p_segments
;
963 uint8_t i_start
= p_row
->firstcol
;
964 for( uint8_t i
=i_start
; i
<=p_row
->lastcol
; i
++ )
966 if( i
== p_row
->lastcol
||
967 memcmp( &p_row
->styles
[i
], &p_row
->styles
[i
+1], sizeof(cea708_pen_style_t
) ) )
969 *pp_last
= CEA708CharsToSegment( p_row
, i_start
, i
,
970 b_addnewline
&& (i
== p_row
->lastcol
) );
972 pp_last
= &((*pp_last
)->p_next
);
980 static void CEA708SpuConvert( const cea708_window_t
*p_w
,
981 substext_updater_region_t
*p_region
)
983 if( !p_w
->b_visible
|| CEA708_Window_RowCount( p_w
) == 0 )
986 if( p_region
== NULL
&& !(p_region
= SubpictureUpdaterSysRegionNew()) )
991 if (p_w
->style
.scroll_direction
== CEA708_WA_DIRECTION_BT
) {
992 /* BT is a bit of a special case since we need to grab the last N
993 rows between first and last, rather than the first... */
994 last
= p_w
->i_lastrow
;
995 if (p_w
->i_lastrow
- p_w
->i_row_count
< p_w
->i_firstrow
)
996 first
= p_w
->i_firstrow
;
998 first
= p_w
->i_lastrow
- p_w
->i_row_count
+ 1;
1001 first
= p_w
->i_firstrow
;
1002 if (p_w
->i_firstrow
+ p_w
->i_row_count
> p_w
->i_lastrow
)
1003 last
= p_w
->i_lastrow
;
1005 last
= p_w
->i_firstrow
+ p_w
->i_row_count
- 1;
1008 text_segment_t
**pp_last
= &p_region
->p_segments
;
1009 for( uint8_t i
=first
; i
<=last
; i
++ )
1014 *pp_last
= CEA708RowToSegments( p_w
->rows
[i
], i
< p_w
->i_lastrow
);
1016 pp_last
= &((*pp_last
)->p_next
);
1019 if( p_w
->b_relative
)
1021 /* FIXME: take into account left/right anchors */
1022 p_region
->origin
.x
= p_w
->i_anchor_offset_h
/ 100.0;
1024 switch (p_w
->anchor_point
) {
1025 case CEA708_ANCHOR_TOP_LEFT
:
1026 case CEA708_ANCHOR_TOP_CENTER
:
1027 case CEA708_ANCHOR_TOP_RIGHT
:
1028 p_region
->origin
.y
= p_w
->i_anchor_offset_v
/ 100.0;
1030 case CEA708_ANCHOR_BOTTOM_LEFT
:
1031 case CEA708_ANCHOR_BOTTOM_CENTER
:
1032 case CEA708_ANCHOR_BOTTOM_RIGHT
:
1033 p_region
->origin
.y
= 1.0 - (p_w
->i_anchor_offset_v
/ 100.0);
1036 /* FIXME: for CENTER vertical justified, just position as top */
1037 p_region
->origin
.y
= p_w
->i_anchor_offset_v
/ 100.0;
1043 p_region
->origin
.x
= (float)p_w
->i_anchor_offset_h
/ CEA708_SCREEN_COLS_169
;
1044 p_region
->origin
.y
= (float)p_w
->i_anchor_offset_v
/
1045 (CEA708_SCREEN_ROWS
* CEA708_FONT_TO_LINE_HEIGHT_RATIO
);
1047 p_region
->flags
|= UPDT_REGION_ORIGIN_X_IS_RATIO
|UPDT_REGION_ORIGIN_Y_IS_RATIO
;
1049 if( p_w
->i_firstrow
<= p_w
->i_lastrow
)
1051 p_region
->origin
.y
+= p_w
->i_firstrow
* CEA708_ROW_HEIGHT_STANDARD
;
1052 /*const uint8_t i_min = CEA708_Window_MinCol( p_w );
1053 if( i_min < CEA708_WINDOW_MAX_COLS )
1054 p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/
1057 if( p_w
->anchor_point
<= CEA708_ANCHOR_BOTTOM_RIGHT
)
1059 static const int vlc_subpicture_aligns
[] =
1061 [CEA708_ANCHOR_TOP_LEFT
] = SUBPICTURE_ALIGN_TOP
|SUBPICTURE_ALIGN_LEFT
,
1062 [CEA708_ANCHOR_TOP_CENTER
] = SUBPICTURE_ALIGN_TOP
,
1063 [CEA708_ANCHOR_TOP_RIGHT
] = SUBPICTURE_ALIGN_TOP
|SUBPICTURE_ALIGN_RIGHT
,
1064 [CEA708_ANCHOR_CENTER_LEFT
] = SUBPICTURE_ALIGN_LEFT
,
1065 [CEA708_ANCHOR_CENTER_CENTER
] = 0,
1066 [CEA708_ANCHOR_CENTER_RIGHT
] = SUBPICTURE_ALIGN_RIGHT
,
1067 [CEA708_ANCHOR_BOTTOM_LEFT
] = SUBPICTURE_ALIGN_BOTTOM
|SUBPICTURE_ALIGN_LEFT
,
1068 [CEA708_ANCHOR_BOTTOM_CENTER
] = SUBPICTURE_ALIGN_BOTTOM
,
1069 [CEA708_ANCHOR_BOTTOM_RIGHT
] = SUBPICTURE_ALIGN_BOTTOM
|SUBPICTURE_ALIGN_RIGHT
,
1071 p_region
->align
= vlc_subpicture_aligns
[p_w
->anchor_point
];
1073 p_region
->inner_align
= SUBPICTURE_ALIGN_BOTTOM
|SUBPICTURE_ALIGN_LEFT
;
1076 static subpicture_t
*CEA708_BuildSubtitle( cea708_t
*p_cea708
)
1078 subpicture_t
*p_spu
= decoder_NewSubpictureText( p_cea708
->p_dec
);
1082 subtext_updater_sys_t
*p_spu_sys
= p_spu
->updater
.p_sys
;
1083 substext_updater_region_t
*p_region
= &p_spu_sys
->region
;
1085 p_spu_sys
->margin_ratio
= CEA708_SCREEN_SAFE_MARGIN_RATIO
;
1089 for(size_t i
=0; i
<CEA708_WINDOWS_COUNT
; i
++)
1091 cea708_window_t
*p_w
= &p_cea708
->window
[i
];
1092 if( p_w
->b_defined
&& p_w
->b_visible
&& CEA708_Window_RowCount( p_w
) )
1096 substext_updater_region_t
*p_newregion
=
1097 SubpictureUpdaterSysRegionNew();
1098 if( p_newregion
== NULL
)
1100 SubpictureUpdaterSysRegionAdd( p_region
, p_newregion
);
1101 p_region
= p_newregion
;
1106 CEA708SpuConvert( p_w
, p_region
);
1110 p_spu
->i_start
= p_cea708
->i_clock
;
1111 p_spu
->i_stop
= p_cea708
->i_clock
+ VLC_TICK_FROM_SEC(10); /* 10s max */
1113 p_spu
->b_ephemer
= true;
1114 p_spu
->b_absolute
= false;
1115 p_spu
->b_subtitle
= true;
1120 static void CEA708_Decoder_Init( cea708_t
*p_cea708
)
1122 cea708_input_buffer_init( &p_cea708
->input_buffer
);
1123 for(size_t i
=0; i
<CEA708_WINDOWS_COUNT
; i
++)
1124 CEA708_Window_Init( &p_cea708
->window
[i
] );
1125 p_cea708
->p_cw
= &p_cea708
->window
[0];
1126 p_cea708
->suspended_deadline
= VLC_TICK_INVALID
;
1127 p_cea708
->b_text_waiting
= false;
1128 p_cea708
->i_clock
= 0;
1131 static void CEA708_Decoder_Reset( cea708_t
*p_cea708
)
1133 for(size_t i
=0; i
<CEA708_WINDOWS_COUNT
; i
++)
1134 CEA708_Window_Reset( &p_cea708
->window
[i
] );
1135 CEA708_Decoder_Init( p_cea708
);
1138 void CEA708_Decoder_Flush( cea708_t
*p_cea708
)
1140 CEA708_Decoder_Reset( p_cea708
);
1143 void CEA708_Decoder_Release( cea708_t
*p_cea708
)
1145 CEA708_Decoder_Reset( p_cea708
);
1149 cea708_t
* CEA708_Decoder_New( decoder_t
*p_dec
)
1151 cea708_t
*p_cea708
= malloc( sizeof(cea708_t
) );
1154 CEA708_Decoder_Init( p_cea708
);
1155 p_cea708
->p_dec
= p_dec
;
1160 #define POP_COMMAND() (void) cea708_input_buffer_get( ib )
1161 #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND()
1162 #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\
1163 return CEA708_STATUS_STARVING
1164 #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND()
1166 static void CEA708_Output( cea708_t
*p_cea708
)
1168 Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708
->i_clock
)));
1169 subpicture_t
*p_spu
= CEA708_BuildSubtitle( p_cea708
);
1171 decoder_QueueSub( p_cea708
->p_dec
, p_spu
);
1174 static int CEA708_Decode_C0( uint8_t code
, cea708_t
*p_cea708
)
1178 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1179 int i_ret
= CEA708_STATUS_OK
;
1188 if( p_cea708
->b_text_waiting
)
1190 i_ret
|= CEA708_STATUS_OUTPUT
;
1191 p_cea708
->b_text_waiting
= false;
1196 if( !p_cea708
->p_cw
->b_defined
)
1198 CEA708_Window_Backward( p_cea708
->p_cw
);
1199 p_cea708
->b_text_waiting
= true;
1203 if( !p_cea708
->p_cw
->b_defined
)
1205 CEA708_Window_ClearText( p_cea708
->p_cw
);
1206 p_cea708
->p_cw
->col
= 0;
1207 p_cea708
->p_cw
->row
= 0;
1208 p_cea708
->b_text_waiting
= true;
1212 if( !p_cea708
->p_cw
->b_defined
)
1214 if( p_cea708
->p_cw
->style
.print_direction
<= CEA708_WA_DIRECTION_RTL
)
1216 CEA708_Window_CarriageReturn( p_cea708
->p_cw
);
1217 if( p_cea708
->p_cw
->b_visible
)
1218 i_ret
|= CEA708_STATUS_OUTPUT
;
1223 if( !p_cea708
->p_cw
->b_defined
)
1225 if( p_cea708
->p_cw
->style
.print_direction
> CEA708_WA_DIRECTION_RTL
)
1227 CEA708_Window_CarriageReturn( p_cea708
->p_cw
);
1228 if( p_cea708
->p_cw
->b_visible
)
1229 i_ret
|= CEA708_STATUS_OUTPUT
;
1232 case CEA708_C0_EXT1
: /* Special extended table case */
1233 if( cea708_input_buffer_size( ib
) >= 2 )
1235 v
= cea708_input_buffer_peek( ib
, 1 );
1236 /* C2 extended code set */
1247 if( cea708_input_buffer_size( ib
) < 2 + i
)
1248 return CEA708_STATUS_STARVING
;
1252 /* C3 extended code set */
1253 else if( v
> 0x7f && v
< 0xa0 )
1259 if( cea708_input_buffer_size( ib
) < 2 + i
)
1260 return CEA708_STATUS_STARVING
;
1267 v
= cea708_input_buffer_get( ib
);
1268 if( p_cea708
->p_cw
->b_defined
)
1269 i_ret
|= CEA708_Decode_G2G3( v
, p_cea708
);
1272 else return CEA708_STATUS_STARVING
;
1275 REQUIRE_ARGS_AND_POP_COMMAND(2);
1276 u16
= cea708_input_buffer_get( ib
) << 8;
1277 u16
|= cea708_input_buffer_get( ib
);
1278 i_ret
|= CEA708_Decode_P16( u16
, p_cea708
);
1279 Debug(printf("[P16 %x]", u16
));
1283 Debug(printf("[UNK %2.2x]", code
));
1286 Debug(printf("[C0 %x]", code
));
1290 static int CEA708_Decode_G0( uint8_t code
, cea708_t
*p_cea708
)
1292 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1294 int i_ret
= CEA708_STATUS_OK
;
1296 if( !p_cea708
->p_cw
->b_defined
)
1299 uint8_t utf8
[4] = {code
,0x00,0x00,0x00};
1301 if(code
== 0x7F) // Music note
1308 CEA708_Window_Write( utf8
, p_cea708
->p_cw
);
1311 p_cea708
->b_text_waiting
&&
1312 CEA708_Window_BreaksSpace( p_cea708
->p_cw
) )
1314 i_ret
|= CEA708_STATUS_OUTPUT
;
1318 p_cea708
->b_text_waiting
|= p_cea708
->p_cw
->b_visible
;
1323 static int CEA708_Decode_C1( uint8_t code
, cea708_t
*p_cea708
)
1326 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1327 int i_ret
= CEA708_STATUS_OK
;
1329 if( p_cea708
->b_text_waiting
)
1331 i_ret
|= CEA708_STATUS_OUTPUT
;
1332 p_cea708
->b_text_waiting
= false;
1338 REQUIRE_ARGS_AND_POP_COMMAND(1);
1339 Debug(printf("[CLW"));
1340 for( i
= 0, v
= cea708_input_buffer_get( ib
); v
; v
= v
>> 1, i
++ )
1343 if( p_cea708
->window
[i
].b_defined
&&
1344 p_cea708
->window
[i
].b_visible
)
1345 i_ret
|= CEA708_STATUS_OUTPUT
;
1346 CEA708_Window_ClearText( &p_cea708
->window
[i
] );
1347 Debug(printf("%d", i
));
1352 REQUIRE_ARGS_AND_POP_COMMAND(1);
1353 Debug(printf("[DSW"));
1354 for( i
= 0, v
= cea708_input_buffer_get( ib
); v
; v
= v
>> 1, i
++ )
1357 if( p_cea708
->window
[i
].b_defined
)
1359 if( !p_cea708
->window
[i
].b_visible
)
1360 i_ret
|= CEA708_STATUS_OUTPUT
;
1361 p_cea708
->window
[i
].b_visible
= true;
1363 Debug(printf("%d", i
));
1368 REQUIRE_ARGS_AND_POP_COMMAND(1);
1369 Debug(printf("[HDW"));
1370 for( i
= 0, v
= cea708_input_buffer_get( ib
); v
; v
= v
>> 1, i
++ )
1373 if( p_cea708
->window
[i
].b_defined
)
1375 if( p_cea708
->window
[i
].b_visible
)
1376 i_ret
|= CEA708_STATUS_OUTPUT
;
1377 p_cea708
->window
[i
].b_visible
= false;
1379 Debug(printf("%d", i
));
1384 REQUIRE_ARGS_AND_POP_COMMAND(1);
1385 Debug(printf("[TGW"));
1386 for( i
= 0, v
= cea708_input_buffer_get( ib
); v
; v
= v
>> 1, i
++ )
1389 if( p_cea708
->window
[i
].b_defined
)
1391 i_ret
|= CEA708_STATUS_OUTPUT
;
1392 p_cea708
->window
[i
].b_visible
= !p_cea708
->window
[i
].b_visible
;
1394 Debug(printf("%d", i
));
1399 REQUIRE_ARGS_AND_POP_COMMAND(1);
1400 Debug(printf("[DLW"));
1401 for( i
= 0, v
= cea708_input_buffer_get( ib
); v
; v
= v
>> 1, i
++ )
1404 if( p_cea708
->window
[i
].b_defined
)
1406 if( p_cea708
->window
[i
].b_visible
)
1407 i_ret
|= CEA708_STATUS_OUTPUT
;
1408 CEA708_Window_Reset( &p_cea708
->window
[i
] );
1410 Debug(printf("%d", i
));
1415 REQUIRE_ARGS_AND_POP_COMMAND(1);
1416 p_cea708
->suspended_deadline
= p_cea708
->i_clock
+
1417 VLC_TICK_FROM_MS( cea708_input_buffer_get( ib
) * 100 );
1418 Debug(printf("[DLY]"));
1422 p_cea708
->suspended_deadline
= VLC_TICK_INVALID
;
1423 Debug(printf("[DLC]"));
1427 i_ret
|= CEA708_STATUS_OUTPUT
;
1431 REQUIRE_ARGS_AND_POP_COMMAND(2);
1432 if( !p_cea708
->p_cw
->b_defined
)
1437 v
= cea708_input_buffer_get( ib
);
1438 p_cea708
->p_cw
->pen
.text_tag
= v
>> 4;
1439 p_cea708
->p_cw
->pen
.offset
= (v
>> 2) & 0x03;
1440 p_cea708
->p_cw
->pen
.size
= v
& 0x03;
1441 v
= cea708_input_buffer_get( ib
);
1442 p_cea708
->p_cw
->pen
.b_italics
= v
& 0x80;
1443 p_cea708
->p_cw
->pen
.b_underline
= v
& 0x40;
1444 p_cea708
->p_cw
->pen
.edge_type
= (v
>> 3) & 0x07;
1445 p_cea708
->p_cw
->pen
.font
= v
& 0x07;
1446 Debug(printf("[SPA]"));
1449 REQUIRE_ARGS_AND_POP_COMMAND(3);
1450 if( !p_cea708
->p_cw
->b_defined
)
1455 v
= cea708_input_buffer_get( ib
);
1456 p_cea708
->p_cw
->pen
.foreground
.opacity
= v
>> 6;
1457 p_cea708
->p_cw
->pen
.foreground
.color
= v
& 0x3F;
1458 v
= cea708_input_buffer_get( ib
);
1459 p_cea708
->p_cw
->pen
.background
.opacity
= v
>> 6;
1460 p_cea708
->p_cw
->pen
.background
.color
= v
& 0x3F;
1461 v
= cea708_input_buffer_get( ib
);
1462 p_cea708
->p_cw
->pen
.edge_color
= v
& 0x3F;
1463 Debug(printf("[SPC]"));
1466 REQUIRE_ARGS_AND_POP_COMMAND(2);
1467 if( !p_cea708
->p_cw
->b_defined
)
1472 v
= cea708_input_buffer_get( ib
);
1473 p_cea708
->p_cw
->row
= (v
& 0x0F) % CEA708_WINDOW_MAX_ROWS
;
1474 v
= cea708_input_buffer_get( ib
);
1475 p_cea708
->p_cw
->col
= (v
& 0x3F) % CEA708_WINDOW_MAX_COLS
;
1476 Debug(printf("[SPL r%d c%d]", p_cea708
->p_cw
->row
, p_cea708
->p_cw
->col
));
1479 REQUIRE_ARGS_AND_POP_COMMAND(4);
1480 if( !p_cea708
->p_cw
->b_defined
)
1485 v
= cea708_input_buffer_get( ib
);
1486 p_cea708
->p_cw
->style
.fill_opacity
= v
>> 6;
1487 p_cea708
->p_cw
->style
.fill_color_color
= v
& 0x3F;
1488 v
= cea708_input_buffer_get( ib
);
1489 p_cea708
->p_cw
->style
.border_color_color
= v
& 0x3F;
1490 p_cea708
->p_cw
->style
.border_type
= v
>> 6;
1491 v
= cea708_input_buffer_get( ib
);
1492 p_cea708
->p_cw
->style
.border_type
|= ((v
& 0x80) >> 5);
1493 p_cea708
->p_cw
->style
.b_word_wrap
= v
& 0x40;
1494 p_cea708
->p_cw
->style
.print_direction
= (v
>> 4) & 0x03;
1495 p_cea708
->p_cw
->style
.scroll_direction
= (v
>> 2) & 0x03;
1496 p_cea708
->p_cw
->style
.justify
= v
& 0x03;
1497 v
= cea708_input_buffer_get( ib
);
1498 p_cea708
->p_cw
->style
.effect_speed
= v
>> 4;
1499 p_cea708
->p_cw
->style
.effect_direction
= (v
>> 2) & 0x03;
1500 p_cea708
->p_cw
->style
.display_effect
= v
& 0x03;
1501 Debug(printf("[SWA]"));
1505 if( code
>= CEA708_C1_CW0
&& code
<= CEA708_C1_CW7
)
1508 Debug(printf("[CW%d]", code
- CEA708_C1_CW0
));
1509 if( p_cea708
->window
[code
- CEA708_C1_CW0
].b_defined
)
1510 p_cea708
->p_cw
= &p_cea708
->window
[code
- CEA708_C1_CW0
];
1512 else if( code
>= CEA708_C1_DF0
&& code
<= CEA708_C1_DF7
)
1514 REQUIRE_ARGS_AND_POP_COMMAND(6);
1515 Debug(printf("[DF%d]", code
- CEA708_C1_DF0
));
1516 /* also sets current window */
1517 p_cea708
->p_cw
= &p_cea708
->window
[code
- CEA708_C1_DF0
];
1518 v
= cea708_input_buffer_get( ib
);
1519 if( p_cea708
->p_cw
->b_defined
&&
1520 !p_cea708
->p_cw
->b_visible
!= !(v
& 0x20) )
1521 i_ret
|= CEA708_STATUS_OUTPUT
;
1522 p_cea708
->p_cw
->b_visible
= v
& 0x20;
1523 p_cea708
->p_cw
->b_row_lock
= v
& 0x10;
1524 p_cea708
->p_cw
->b_column_lock
= v
& 0x08;
1525 p_cea708
->p_cw
->i_priority
= v
& 0x07;
1526 v
= cea708_input_buffer_get( ib
);
1527 p_cea708
->p_cw
->b_relative
= v
& 0x80;
1528 p_cea708
->p_cw
->i_anchor_offset_v
= v
& 0x7F;
1529 v
= cea708_input_buffer_get( ib
);
1530 p_cea708
->p_cw
->i_anchor_offset_h
= v
;
1531 v
= cea708_input_buffer_get( ib
);
1532 p_cea708
->p_cw
->anchor_point
= v
>> 4;
1533 p_cea708
->p_cw
->i_row_count
= (v
& 0x0F) + 1;
1534 v
= cea708_input_buffer_get( ib
);
1535 p_cea708
->p_cw
->i_col_count
= v
& 0x3F;
1536 v
= cea708_input_buffer_get( ib
);
1537 /* zero values style set on init, avoid dealing with updt case */
1538 i
= (v
>> 3) & 0x07; /* Window style id */
1539 if( i
> 0 && !p_cea708
->p_cw
->b_defined
)
1540 p_cea708
->p_cw
->style
= cea708_default_window_styles
[i
];
1541 i
= v
& 0x07; /* Pen style id */
1542 if( i
> 0 && !p_cea708
->p_cw
->b_defined
)
1543 p_cea708
->p_cw
->pen
= cea708_default_pen_styles
[i
];
1544 p_cea708
->p_cw
->b_defined
= true;
1548 Debug(printf("{%2.2x}", code
));
1556 static int CEA708_Decode_G1( uint8_t code
, cea708_t
*p_cea708
)
1558 cea708_input_buffer_t
*ib
= &p_cea708
->input_buffer
;
1561 if( !p_cea708
->p_cw
->b_defined
)
1562 return CEA708_STATUS_OK
;
1564 uint8_t utf8
[4] = {0xc0 | (code
& 0xc0) >> 6,
1565 0x80 | (code
& 0x3f),
1568 CEA708_Window_Write( utf8
, p_cea708
->p_cw
);
1569 p_cea708
->b_text_waiting
|= p_cea708
->p_cw
->b_visible
;
1571 return CEA708_STATUS_OK
;
1574 static int CEA708_Decode_G2G3( uint8_t code
, cea708_t
*p_cea708
)
1576 if( !p_cea708
->p_cw
->b_defined
)
1577 return CEA708_STATUS_OK
;
1579 uint8_t out
[4] = { '?', 0, 0, 0 };
1580 static const struct {
1585 { 0x20, { 0x20 } },// transparent space [*** will need special handling]
1586 { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling]
1587 { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS
1588 { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON
1589 { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE
1590 { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK
1591 { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK
1592 { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK
1593 { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK
1594 { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK
1595 { 0x35, { 0xe2,0x80,0xa2 } },// BULLET
1596 { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM)
1597 { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON
1598 { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE
1599 { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK
1600 { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS
1601 { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH
1602 { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS
1603 { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS
1604 { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS
1605 { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL
1606 { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT
1607 { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT
1608 { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL
1609 { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT
1610 { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT
1612 { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C)
1615 for( size_t i
= 0; i
< ARRAY_SIZE(code2utf8
) ; i
++ )
1617 if( code2utf8
[i
].c
== code
)
1619 memcpy( out
, code2utf8
[i
].utf8
, 4 );
1624 else if(out
[0] < 0xe0)
1633 CEA708_Window_Write( out
, p_cea708
->p_cw
);
1635 p_cea708
->b_text_waiting
|= p_cea708
->p_cw
->b_visible
;
1637 return CEA708_STATUS_OK
;
1640 static int CEA708_Decode_P16( uint16_t ucs2
, cea708_t
*p_cea708
)
1642 if( !p_cea708
->p_cw
->b_defined
)
1643 return CEA708_STATUS_OK
;
1645 uint8_t out
[4] = { '?', 0, 0, 0 };
1647 /* adapted from codepoint conversion from strings.h */
1652 else if( ucs2
<= 0x7FF )
1654 out
[0] = 0xC0 | (ucs2
>> 6);
1655 out
[1] = 0x80 | (ucs2
& 0x3F);
1659 out
[0] = 0xE0 | (ucs2
>> 12);
1660 out
[1] = 0x80 | ((ucs2
>> 6) & 0x3F);
1661 out
[2] = 0x80 | (ucs2
& 0x3F);
1664 CEA708_Window_Write( out
, p_cea708
->p_cw
);
1666 p_cea708
->b_text_waiting
|= p_cea708
->p_cw
->b_visible
;
1668 return CEA708_STATUS_OK
;
1671 static void CEA708_Decode_ServiceBuffer( cea708_t
*h
)
1675 const uint8_t i_in
= cea708_input_buffer_size( &h
->input_buffer
);
1680 uint8_t c
= cea708_input_buffer_peek( &h
->input_buffer
, 0 );
1683 i_ret
= CEA708_Decode_C0( c
, h
);
1684 else if( c
<= 0x7F )
1685 i_ret
= CEA708_Decode_G0( c
, h
);
1686 else if( c
<= 0x9F )
1687 i_ret
= CEA708_Decode_C1( c
, h
);
1689 i_ret
= CEA708_Decode_G1( c
, h
);
1691 if( i_ret
& CEA708_STATUS_OUTPUT
)
1694 if( i_ret
& CEA708_STATUS_STARVING
)
1697 /* Update internal clock */
1698 const uint8_t i_consumed
= i_in
- cea708_input_buffer_size( &h
->input_buffer
);
1700 h
->i_clock
+= vlc_tick_from_samples(1, 9600) * i_consumed
;
1704 void CEA708_Decoder_Push( cea708_t
*h
, vlc_tick_t i_time
,
1705 const uint8_t *p_data
, size_t i_data
)
1707 /* Set new buffer start time */
1708 h
->i_clock
= i_time
;
1710 for( size_t i
=0; i
<i_data
; )
1712 /* Never push more than buffer */
1713 size_t i_push
= cea708_input_buffer_remain(&h
->input_buffer
);
1714 if( (i_data
- i
) < i_push
)
1715 i_push
= (i_data
- i
);
1717 h
->suspended_deadline
= VLC_TICK_INVALID
; /* Full buffer cancels pause */
1719 for( size_t j
=0; j
<i_push
; j
++ )
1721 uint8_t byte
= p_data
[i
+j
];
1722 cea708_input_buffer_add( &h
->input_buffer
, byte
);
1725 if( h
->suspended_deadline
!= VLC_TICK_INVALID
)
1727 /* Decoding is paused */
1728 if ( h
->suspended_deadline
> h
->i_clock
)
1730 /* Increase internal clock */
1732 h
->i_clock
+= vlc_tick_from_samples(1, 1200) * i_push
;
1735 h
->suspended_deadline
= VLC_TICK_INVALID
;
1739 CEA708_Decode_ServiceBuffer( h
);