Coverage Report

Created: 2026-11-12 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/cea708.c
Line
Count
Source
1
/*****************************************************************************
2
 * cea708.c : CEA708 subtitles decoder
3
 *****************************************************************************
4
 * Copyright © 2017 VideoLabs, VideoLAN and VLC authors
5
 *
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.
10
 *
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.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19
 *****************************************************************************/
20
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include <vlc_common.h>
25
#include <vlc_codec.h>
26
#include <vlc_subpicture.h>
27
28
#include "cea708.h"
29
#include "substext.h"
30
31
#include <assert.h>
32
33
#if 0
34
#define Debug(code) code
35
#else
36
#define Debug(code)
37
#endif
38
39
/*****************************************************************************
40
 * Demuxing / Agreggation
41
 *****************************************************************************/
42
struct cea708_demux_t
43
{
44
   int8_t  i_pkt_sequence;
45
   uint8_t i_total_data;
46
   uint8_t i_data;
47
   uint8_t data[CEA708_DTVCC_MAX_PKT_SIZE];
48
   vlc_tick_t i_time;
49
   service_data_hdlr_t p_callback;
50
   void *priv;
51
};
52
53
void CEA708_DTVCC_Demuxer_Flush( cea708_demux_t *h )
54
16
{
55
16
    h->i_pkt_sequence = -1;
56
16
    h->i_total_data = h->i_data = 0;
57
16
}
58
59
void CEA708_DTVCC_Demuxer_Release( cea708_demux_t *h )
60
16
{
61
16
    free( h );
62
16
}
63
64
cea708_demux_t * CEA708_DTVCC_Demuxer_New( void *priv, service_data_hdlr_t hdlr )
65
16
{
66
16
    cea708_demux_t *h = malloc( sizeof(cea708_demux_t) );
67
16
    if( h )
68
16
    {
69
16
        h->priv = priv;
70
16
        h->p_callback = hdlr;
71
16
        CEA708_DTVCC_Demuxer_Flush( h );
72
16
    }
73
16
    return h;
74
16
}
75
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 )
78
3
{
79
5
    while( i_data >= 2 )
80
5
    {
81
5
        uint8_t i_sid = p_data[0] >> 5;
82
5
        const uint8_t i_block_size = p_data[0] & 0x1F;
83
84
5
        if( i_sid == 0x07 )
85
0
        {
86
0
            i_sid = p_data[1] & 0x3F;
87
0
            if( i_sid < 0x07 )
88
0
                return;
89
0
            p_data += 1; i_data -= 1;
90
0
        }
91
5
        if( i_block_size == 0 || i_block_size > i_data - 1 )
92
3
        {
93
3
            return;
94
3
        }
95
2
        p_data += 1; i_data -= 1;
96
97
2
        h->p_callback( h->priv, i_sid, i_start, p_data, i_block_size );
98
99
2
        p_data += i_block_size;
100
2
        i_data -= i_block_size;
101
2
    }
102
3
}
103
104
void CEA708_DTVCC_Demuxer_Push( cea708_demux_t *h, vlc_tick_t i_start, const uint8_t data[3] )
105
486
{
106
486
    if( (data[0] & 0x03) == 3 ) /* Header packet */
107
176
    {
108
176
        const int8_t i_pkt_sequence = data[1] >> 6;
109
110
        /* pkt loss/discontinuity, trash buffer */
111
176
        if( i_pkt_sequence > 0 && ((h->i_pkt_sequence + 1) % 4) != i_pkt_sequence )
112
35
        {
113
35
            h->i_data = h->i_total_data = 0;
114
35
            h->i_pkt_sequence = i_pkt_sequence;
115
35
            return;
116
35
        }
117
118
141
        uint8_t pktsize = data[1] & 63;
119
141
        if( pktsize == 0 )
120
18
            pktsize = 127;
121
123
        else
122
123
            pktsize = pktsize * 2 - 1;
123
124
141
        h->i_pkt_sequence = i_pkt_sequence;
125
141
        h->i_total_data = pktsize;
126
141
        h->i_data = 0;
127
141
        h->i_time = i_start;
128
141
        h->data[h->i_data++] = data[2];
129
141
    }
130
310
    else if( h->i_total_data > 0 ) /* Not synced to pkt header yet */
131
132
    {
132
132
        h->data[h->i_data++] = data[1];
133
132
        h->data[h->i_data++] = data[2];
134
132
    }
135
136
    /* pkts assembly finished, we have a service block */
137
451
    if( h->i_data > 0 && h->i_data >= h->i_total_data )
138
3
    {
139
3
        if( h->i_data == h->i_total_data ) /* Only if correct */
140
3
            CEA708_DTVCC_Demux_ServiceBlocks( h, h->i_time, h->data, h->i_data );
141
3
        h->i_total_data = h->i_data = 0;
142
3
    }
143
451
}
144
145
/*****************************************************************************
146
 * Service Data Decoding
147
 *****************************************************************************/
148
149
0
#define CEA708_SERVICE_INPUT_BUFFER    128
150
151
432
#define CEA708_WINDOWS_COUNT            8
152
#define CEA708_PREDEFINED_STYLES        7
153
154
0
#define CEA708_SCREEN_ROWS              75
155
0
#define CEA708_SCREEN_COLS_43           160
156
0
#define CEA708_SCREEN_COLS_169          210
157
0
#define CEA708_SCREEN_SAFE_MARGIN_RATIO 0.10
158
0
#define CEA708_SAFE_AREA_REL            (1.0 - CEA708_SCREEN_SAFE_MARGIN_RATIO)
159
160
0
#define CEA708_WINDOW_MAX_COLS          42
161
512
#define CEA708_WINDOW_MAX_ROWS          15
162
163
0
#define CEA708_ROW_HEIGHT_STANDARD     (CEA708_SAFE_AREA_REL / \
164
0
                                        CEA708_WINDOW_MAX_ROWS)
165
0
#define CEA708_FONT_TO_LINE_HEIGHT_RATIO 1.06
166
167
0
#define CEA708_CENTER_ANCHOR_START      0.25f
168
0
#define CEA708_CENTER_ANCHOR_RANGE      0.5f
169
170
0
#define CEA708_FONTRELSIZE_STANDARD    (100.0 * CEA708_ROW_HEIGHT_STANDARD / \
171
0
                                        CEA708_FONT_TO_LINE_HEIGHT_RATIO)
172
0
#define CEA708_FONTRELSIZE_SMALL       (CEA708_FONTRELSIZE_STANDARD * 0.7)
173
0
#define CEA708_FONTRELSIZE_LARGE       (CEA708_FONTRELSIZE_STANDARD * 1.3)
174
175
enum cea708_status_e
176
{
177
    CEA708_STATUS_OK       = 1 << 0,
178
    CEA708_STATUS_STARVING = 1 << 1,
179
    CEA708_STATUS_OUTPUT   = 1 << 2,
180
};
181
182
enum cea708_c0_codes
183
{
184
    CEA708_C0_NUL   = 0x00,
185
    CEA708_C0_ETX   = 0x03,
186
    CEA708_C0_BS    = 0x08,
187
    CEA708_C0_FF    = 0x0C,
188
    CEA708_C0_CR    = 0x0D,
189
    CEA708_C0_HCR   = 0x0E,
190
    CEA708_C0_EXT1  = 0x10,
191
    CEA708_C0_P16   = 0x18,
192
};
193
194
enum cea708_c1_codes
195
{
196
    CEA708_C1_CW0   = 0x80,
197
    CEA708_C1_CW7   = 0x87,
198
    CEA708_C1_CLW,
199
    CEA708_C1_DSW,
200
    CEA708_C1_HDW,
201
    CEA708_C1_TGW,
202
    CEA708_C1_DLW,
203
    CEA708_C1_DLY,
204
    CEA708_C1_DLC,
205
    CEA708_C1_RST,
206
    CEA708_C1_SPA   = 0x90,
207
    CEA708_C1_SPC,
208
    CEA708_C1_SPL,
209
    CEA708_C1_SWA   = 0x97,
210
    CEA708_C1_DF0,
211
    CEA708_C1_DF7   = 0x9F,
212
};
213
214
typedef struct
215
{
216
    uint8_t ringbuffer[CEA708_SERVICE_INPUT_BUFFER];
217
    uint8_t start;
218
    uint8_t capacity;
219
} cea708_input_buffer_t;
220
221
static void cea708_input_buffer_init(cea708_input_buffer_t *ib)
222
32
{
223
32
    ib->capacity = 0;
224
32
    ib->start = 0;
225
32
}
226
227
static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t *ib)
228
0
{
229
0
    return ib->capacity;
230
0
}
231
232
static uint8_t cea708_input_buffer_remain(const cea708_input_buffer_t *ib)
233
0
{
234
0
    return CEA708_SERVICE_INPUT_BUFFER - ib->capacity;
235
0
}
236
237
static void cea708_input_buffer_add(cea708_input_buffer_t *ib, uint8_t a)
238
0
{
239
0
    if( cea708_input_buffer_remain(ib) > 0 )
240
0
        ib->ringbuffer[(ib->start + ib->capacity++) % CEA708_SERVICE_INPUT_BUFFER] = a;
241
0
}
242
243
static uint8_t cea708_input_buffer_peek(cea708_input_buffer_t *ib, uint8_t off)
244
0
{
245
0
    if(off + 1 > ib->capacity)
246
0
        return 0;
247
0
    off = (ib->start + off) % CEA708_SERVICE_INPUT_BUFFER;
248
0
    return ib->ringbuffer[off];
249
0
}
250
251
static uint8_t cea708_input_buffer_get(cea708_input_buffer_t *ib)
252
0
{
253
0
    uint8_t a = cea708_input_buffer_peek( ib, 0 );
254
0
    ib->start = (ib->start + 1) % CEA708_SERVICE_INPUT_BUFFER;
255
0
    ib->capacity--;
256
0
    return a;
257
0
}
258
259
enum cea708_opacity_e
260
{
261
    CEA708_OPACITY_SOLID = 0,
262
    CEA708_OPACITY_FLASH,
263
    CEA708_OPACITY_TRANSLUCENT,
264
    CEA708_OPACITY_TRANSPARENT,
265
};
266
267
enum cea708_edge_e
268
{
269
    CEA708_EDGE_NONE =0,
270
    CEA708_EDGE_RAISED,
271
    CEA708_EDGE_DEPRESSED,
272
    CEA708_EDGE_UNIFORM,
273
    CEA708_EDGE_LEFT_DROP_SHADOW,
274
    CEA708_EDGE_RIGHT_DROP_SHADOW,
275
};
276
277
typedef struct
278
{
279
    enum
280
    {
281
        CEA708_PEN_SIZE_SMALL = 0,
282
        CEA708_PEN_SIZE_STANDARD,
283
        CEA708_PEN_SIZE_LARGE,
284
    } size;
285
    enum
286
    {
287
        CEA708_FONT_UNDEFINED = 0,
288
        CEA708_FONT_MONOSPACED,
289
        CEA708_FONT_PROP,
290
        CEA708_FONT_MONO_SANS_SERIF,
291
        CEA708_FONT_PROP_SANS_SERIF,
292
        CEA708_FONT_CASUAL,
293
        CEA708_FONT_CURSIVE,
294
        CEA708_FONT_SMALL_CAPS,
295
    } font;
296
    enum
297
    {
298
        CEA708_TAG_DIALOG = 0,
299
        CEA708_TAG_SPEAKER,
300
        CEA708_TAG_SYNTHETIC_VOICE,
301
        CEA708_TAG_DIALOG_SECONDARY_LANG,
302
        CEA708_TAG_VOICEOVER,
303
        CEA708_TAG_AUDIBLE_TRANSLATION,
304
        CEA708_TAG_SUBTITLE_TRANSLATION,
305
        CEA708_TAG_VOICE_QUALITY_DESCRIPTION,
306
        CEA708_TAG_SONG_LYRICS,
307
        CEA708_TAG_FX_DESCRIPTION,
308
        CEA708_TAG_SCORE_DESCRIPTION,
309
        CEA708_TAG_EXPLETIVE,
310
        CEA708_TAG_NOT_TO_BE_DISPLAYED = 15,
311
    } text_tag;
312
    enum
313
    {
314
        CEA708_PEN_OFFSET_SUBSCRIPT = 0,
315
        CEA708_PEN_OFFSET_NORMAL,
316
        CEA708_PEN_OFFSET_SUPERSCRIPT,
317
    } offset;
318
    bool b_italics;
319
    bool b_underline;
320
    struct
321
    {
322
        uint8_t color;
323
        enum cea708_opacity_e opacity;
324
    } foreground, background;
325
    uint8_t edge_color;
326
    enum cea708_edge_e edge_type;
327
} cea708_pen_style_t;
328
329
typedef struct
330
{
331
    cea708_pen_style_t style;
332
    uint8_t row;
333
    uint8_t col;
334
} cea708_pen_t;
335
336
typedef struct
337
{
338
    enum
339
    {
340
        CEA708_WA_JUSTIFY_LEFT = 0,
341
        CEA708_WA_JUSTIFY_RIGHT,
342
        CEA708_WA_JUSTIFY_CENTER,
343
        CEA708_WA_JUSTIFY_FULL,
344
    } justify;
345
    enum
346
    {
347
        CEA708_WA_DIRECTION_LTR = 0,
348
        CEA708_WA_DIRECTION_RTL,
349
        CEA708_WA_DIRECTION_TB,
350
        CEA708_WA_DIRECTION_BT,
351
    } print_direction, scroll_direction, effect_direction;
352
    bool b_word_wrap;
353
    enum
354
    {
355
        CEA708_WA_EFFECT_SNAP = 0,
356
        CEA708_WA_EFFECT_FADE,
357
        CEA708_WA_EFFECT_WIPE,
358
    } display_effect;
359
    uint8_t effect_speed;
360
    uint8_t fill_color_color;
361
    enum cea708_opacity_e fill_opacity;
362
    enum cea708_edge_e border_type;
363
    uint8_t border_color_color;
364
} cea708_window_style_t;
365
366
typedef struct cea708_text_row_t cea708_text_row_t;
367
368
struct cea708_text_row_t
369
{
370
    uint8_t characters[CEA708_WINDOW_MAX_COLS * 4];
371
    cea708_pen_style_t styles[CEA708_WINDOW_MAX_COLS];
372
    uint8_t firstcol;
373
    uint8_t lastcol;
374
};
375
376
static const cea708_pen_style_t cea708_default_pen_styles[CEA708_PREDEFINED_STYLES];
377
378
static void cea708_text_row_Delete( cea708_text_row_t *p_row )
379
0
{
380
0
    free( p_row );
381
0
}
382
383
static cea708_text_row_t * cea708_text_row_New( void )
384
0
{
385
0
    cea708_text_row_t *p_row = malloc( sizeof(*p_row) );
386
0
    if( p_row )
387
0
    {
388
0
        p_row->firstcol = CEA708_WINDOW_MAX_COLS;
389
0
        p_row->lastcol = 0;
390
0
        memset(p_row->characters, 0, 4 * CEA708_WINDOW_MAX_COLS);
391
0
        for( size_t i = 0; i < ARRAY_SIZE(p_row->styles); i++ )
392
0
            p_row->styles[i] = cea708_default_pen_styles[0];
393
0
    }
394
0
    return p_row;
395
0
}
396
397
typedef struct
398
{
399
    cea708_text_row_t * rows[CEA708_WINDOW_MAX_ROWS];
400
    uint8_t i_firstrow;
401
    uint8_t i_lastrow;
402
403
    uint8_t i_priority;
404
405
    enum
406
    {
407
        CEA708_ANCHOR_TOP_LEFT = 0,
408
        CEA708_ANCHOR_TOP_CENTER,
409
        CEA708_ANCHOR_TOP_RIGHT,
410
        CEA708_ANCHOR_CENTER_LEFT,
411
        CEA708_ANCHOR_CENTER_CENTER,
412
        CEA708_ANCHOR_CENTER_RIGHT,
413
        CEA708_ANCHOR_BOTTOM_LEFT,
414
        CEA708_ANCHOR_BOTTOM_CENTER,
415
        CEA708_ANCHOR_BOTTOM_RIGHT,
416
    } anchor_point;
417
    uint8_t i_anchor_offset_v;
418
    uint8_t i_anchor_offset_h;
419
420
    /* Extras row for window scroll */
421
    uint8_t i_row_count;
422
    uint8_t i_col_count;
423
424
    /* flags */
425
    uint8_t b_relative;
426
    uint8_t b_row_lock;
427
    uint8_t b_column_lock;
428
    uint8_t b_visible;
429
430
    cea708_window_style_t style;
431
    cea708_pen_style_t    pen;
432
433
    uint8_t row;
434
    uint8_t col;
435
436
    bool b_defined;
437
438
} cea708_window_t;
439
440
struct cea708_t
441
{
442
    decoder_t *p_dec;
443
444
    /* Defaults */
445
    cea708_window_t window[CEA708_WINDOWS_COUNT];
446
    cea708_input_buffer_t input_buffer;
447
448
    /* Decoding context */
449
    cea708_window_t *p_cw; /* current window */
450
    vlc_tick_t suspended_deadline; /* not VLC_TICK_INVALID when delay is active */
451
    vlc_tick_t i_clock;
452
    bool b_text_waiting;
453
};
454
455
static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 );
456
static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 );
457
static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 );
458
static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 );
459
static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 );
460
static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 );
461
462
#define DEFAULT_NTSC_STYLE(font, edge, bgopacity ) \
463
    {\
464
        CEA708_PEN_SIZE_STANDARD,\
465
        font,\
466
        CEA708_TAG_DIALOG,\
467
        CEA708_PEN_OFFSET_NORMAL,\
468
        false,\
469
        false,\
470
        {   0x2A,   CEA708_OPACITY_SOLID,   },\
471
        {   0x00,   bgopacity,              },\
472
        0x00,\
473
        edge,\
474
    }
475
static const cea708_pen_style_t cea708_default_pen_styles[CEA708_PREDEFINED_STYLES] =
476
{
477
    DEFAULT_NTSC_STYLE( CEA708_FONT_UNDEFINED,       CEA708_EDGE_NONE,    CEA708_OPACITY_SOLID ),
478
    DEFAULT_NTSC_STYLE( CEA708_FONT_MONOSPACED,      CEA708_EDGE_NONE,    CEA708_OPACITY_SOLID ),
479
    DEFAULT_NTSC_STYLE( CEA708_FONT_PROP,            CEA708_EDGE_NONE,    CEA708_OPACITY_SOLID ),
480
    DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_NONE,    CEA708_OPACITY_SOLID ),
481
    DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_NONE,    CEA708_OPACITY_SOLID ),
482
    DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
483
    DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ),
484
};
485
#undef DEFAULT_NTSC_STYLE
486
487
#define DEFAULT_NTSC_WA_STYLE(just, pd, scroll, wrap, opacity) \
488
    {\
489
        just,\
490
        pd,\
491
        scroll,\
492
        CEA708_WA_DIRECTION_LTR,\
493
        wrap,\
494
        CEA708_WA_EFFECT_SNAP,\
495
        1,\
496
        0x00,\
497
        opacity,\
498
        CEA708_EDGE_NONE,\
499
        0x00,\
500
    }
501
static const cea708_window_style_t cea708_default_window_styles[CEA708_PREDEFINED_STYLES] =
502
{
503
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT,   CEA708_WA_DIRECTION_LTR,
504
                          CEA708_WA_DIRECTION_BT,   false, CEA708_OPACITY_SOLID),
505
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT,   CEA708_WA_DIRECTION_LTR,
506
                          CEA708_WA_DIRECTION_BT,   false, CEA708_OPACITY_TRANSPARENT),
507
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
508
                          CEA708_WA_DIRECTION_BT,   false, CEA708_OPACITY_SOLID),
509
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT,   CEA708_WA_DIRECTION_LTR,
510
                          CEA708_WA_DIRECTION_BT,   true,  CEA708_OPACITY_SOLID),
511
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT,   CEA708_WA_DIRECTION_LTR,
512
                          CEA708_WA_DIRECTION_BT,   true,  CEA708_OPACITY_TRANSPARENT),
513
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR,
514
                          CEA708_WA_DIRECTION_BT,   true, CEA708_OPACITY_SOLID),
515
    DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT,   CEA708_WA_DIRECTION_TB,
516
                          CEA708_WA_DIRECTION_RTL,  false, CEA708_OPACITY_SOLID),
517
};
518
#undef DEFAULT_NTSC_WA_STYLE
519
520
static void CEA708_Window_Init( cea708_window_t *p_w )
521
384
{
522
384
    memset( p_w, 0, sizeof(*p_w) );
523
384
    p_w->style = cea708_default_window_styles[0];
524
384
    p_w->pen = cea708_default_pen_styles[0];
525
384
    p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
526
384
    p_w->b_row_lock = true;
527
384
    p_w->b_column_lock = true;
528
384
}
529
530
static void CEA708_Window_ClearText( cea708_window_t *p_w )
531
128
{
532
128
    for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ )
533
0
    {
534
0
        cea708_text_row_Delete( p_w->rows[i] );
535
0
        p_w->rows[i] = NULL;
536
0
    }
537
128
    p_w->i_lastrow = 0;
538
128
    p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS;
539
128
}
540
541
static void CEA708_Window_Reset( cea708_window_t *p_w )
542
128
{
543
128
    CEA708_Window_ClearText( p_w );
544
128
    CEA708_Window_Init( p_w );
545
128
}
546
547
static bool CEA708_Window_BreaksSpace( const cea708_window_t *p_w )
548
0
{
549
0
#if 1
550
    // FIXME: missing test case
551
0
    (void)p_w;
552
0
    return true;
553
#else
554
    if( p_w->style.print_direction == CEA708_WA_DIRECTION_LTR &&
555
        p_w->style.justify == CEA708_WA_JUSTIFY_LEFT )
556
        return true;
557
558
    if( p_w->style.print_direction == CEA708_WA_DIRECTION_RTL &&
559
        p_w->style.justify == CEA708_WA_JUSTIFY_RIGHT )
560
        return true;
561
562
    return false;
563
#endif
564
0
}
565
566
static uint8_t CEA708_Window_MinCol( const cea708_window_t *p_w )
567
0
{
568
0
    uint8_t i_min = CEA708_WINDOW_MAX_COLS;
569
0
    for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
570
0
    {
571
0
        const cea708_text_row_t *p_row = p_w->rows[i];
572
0
        if( p_row && p_row->firstcol < i_min )
573
0
            i_min = p_row->firstcol;
574
0
    }
575
0
    return i_min;
576
0
}
577
578
static uint8_t CEA708_Window_MaxCol( const cea708_window_t *p_w )
579
0
{
580
0
    uint8_t i_max = 0;
581
0
    for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
582
0
    {
583
0
        const cea708_text_row_t *p_row = p_w->rows[i];
584
0
        if( p_row && p_row->lastcol > i_max )
585
0
            i_max = p_row->lastcol;
586
0
    }
587
0
    return i_max;
588
0
}
589
590
static uint8_t CEA708_Window_ColCount( const cea708_window_t *p_w )
591
0
{
592
0
    const cea708_text_row_t *p_row = p_w->rows[p_w->row];
593
0
    if( !p_row || p_row->firstcol > p_row->lastcol )
594
0
        return 0;
595
0
    return 1 + p_row->lastcol - p_row->firstcol;
596
0
}
597
598
static uint8_t CEA708_Window_RowCount( const cea708_window_t *p_w )
599
0
{
600
0
    if( p_w->i_firstrow > p_w->i_lastrow )
601
0
        return 0;
602
0
    return 1 + p_w->i_lastrow - p_w->i_firstrow;
603
0
}
604
605
static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction )
606
0
{
607
0
    switch( i_direction )
608
0
    {
609
0
        case CEA708_WA_DIRECTION_LTR: /* Deletes all most right col */
610
0
        {
611
0
            uint8_t i_max = CEA708_Window_MaxCol( p_w );
612
0
            for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
613
0
            {
614
0
                cea708_text_row_t *row = p_w->rows[i];
615
0
                if (!row)
616
0
                    continue;
617
0
                if( row->lastcol == i_max )
618
0
                {
619
0
                    if( row->firstcol >= row->lastcol )
620
0
                    {
621
0
                        cea708_text_row_Delete( row );
622
0
                        p_w->rows[i] = NULL;
623
0
                        if( i == p_w->i_firstrow )
624
0
                            p_w->i_firstrow++;
625
0
                        else if( i == p_w->i_lastrow )
626
0
                            p_w->i_lastrow--;
627
0
                    }
628
0
                    else
629
0
                    {
630
                        /* Drop rightmost column */
631
0
                        row->lastcol--;
632
0
                    }
633
634
0
                }
635
0
            }
636
0
        }
637
0
            break;
638
0
        case CEA708_WA_DIRECTION_RTL: /* Deletes all most left col */
639
0
        {
640
0
            uint8_t i_min = CEA708_Window_MinCol( p_w );
641
0
            for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
642
0
            {
643
0
                cea708_text_row_t *row = p_w->rows[i];
644
0
                if (!row)
645
0
                    continue;
646
0
                if( row->firstcol == i_min )
647
0
                {
648
0
                    if( row->firstcol >= row->lastcol )
649
0
                    {
650
0
                        cea708_text_row_Delete( row );
651
0
                        p_w->rows[i] = NULL;
652
0
                        if( i == p_w->i_firstrow )
653
0
                            p_w->i_firstrow++;
654
0
                        else if( i == p_w->i_lastrow )
655
0
                            p_w->i_lastrow--;
656
0
                    }
657
0
                    else
658
0
                    {
659
                        /* Drop leftmost column */
660
0
                        row->firstcol++;
661
0
                    }
662
663
0
                }
664
0
            }
665
0
        }
666
0
            break;
667
0
        case CEA708_WA_DIRECTION_TB: /* Deletes LAST row */
668
0
            if( CEA708_Window_RowCount( p_w ) > 0 )
669
0
            {
670
0
                cea708_text_row_Delete( p_w->rows[p_w->i_lastrow] );
671
0
                p_w->rows[p_w->i_lastrow--] = NULL;
672
0
            }
673
0
            break;
674
0
        case CEA708_WA_DIRECTION_BT: /* Deletes First row */
675
0
            if( CEA708_Window_RowCount( p_w ) > 0 )
676
0
            {
677
0
                cea708_text_row_Delete( p_w->rows[p_w->i_firstrow] );
678
0
                p_w->rows[p_w->i_firstrow++] = NULL;
679
0
            }
680
0
            break;
681
0
    }
682
0
}
683
684
static void CEA708_Window_Scroll( cea708_window_t *p_w )
685
0
{
686
0
    if( CEA708_Window_RowCount( p_w ) == 0 )
687
0
        return;
688
689
0
    switch( p_w->style.scroll_direction )
690
0
    {
691
0
        case CEA708_WA_DIRECTION_LTR:
692
            /* Move RIGHT */
693
0
            if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_COLS - 1 )
694
0
                CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR );
695
0
            for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
696
0
            {
697
0
                cea708_text_row_t *row = p_w->rows[i];
698
0
                if( !row )
699
0
                    continue;
700
0
                if( row->lastcol < row->firstcol ) /* should not happen */
701
0
                    continue;
702
703
0
                size_t start = (size_t) row->firstcol * 4U;
704
0
                size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U;
705
0
                memmove( &row->characters[start + 4U], &row->characters[start],
706
0
                         count );
707
0
                memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol],
708
0
                         (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
709
0
                row->firstcol++;
710
0
                row->lastcol++;
711
0
            }
712
0
            break;
713
0
        case CEA708_WA_DIRECTION_RTL:
714
            /* Move LEFT */
715
0
            if( CEA708_Window_MinCol( p_w ) == 0 )
716
0
                CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_RTL );
717
0
            for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
718
0
            {
719
0
                cea708_text_row_t *row = p_w->rows[i];
720
0
                if( !row )
721
0
                    continue;
722
0
                if( row->lastcol < row->firstcol ) /* should not happen */
723
0
                    continue;
724
0
                if( row->firstcol > 0 )
725
0
                {
726
0
                    size_t start = (size_t) row->firstcol * 4U;
727
0
                    size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U;
728
0
                    memmove( &row->characters[start -4U], &row->characters[start],
729
0
                             count );
730
0
                    memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol],
731
0
                             (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) );
732
0
                    row->firstcol--;
733
0
                    row->lastcol--;
734
0
                }
735
0
            }
736
0
            break;
737
0
        case CEA708_WA_DIRECTION_TB:
738
            /* Move DOWN */
739
0
            if( p_w->i_lastrow == CEA708_WINDOW_MAX_ROWS - 1 )
740
0
                CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_TB );
741
0
            for( int i=p_w->i_lastrow; i >= p_w->i_firstrow; i-- )
742
0
                p_w->rows[i+1] = p_w->rows[i];
743
0
            p_w->rows[p_w->i_firstrow] = NULL;
744
0
            p_w->i_firstrow++;
745
0
            p_w->i_lastrow++;
746
0
            break;
747
0
        case CEA708_WA_DIRECTION_BT:
748
            /* Move UP */
749
0
            if( p_w->i_firstrow == 0 )
750
0
                CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_BT );
751
0
            for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ )
752
0
                p_w->rows[i-1] = p_w->rows[i];
753
0
            p_w->rows[p_w->i_lastrow] = NULL;
754
0
            if( p_w->i_firstrow != 0 )
755
0
                p_w->i_firstrow--;
756
0
            if( p_w->i_lastrow != 0 )
757
0
                p_w->i_lastrow--;
758
0
            break;
759
0
    }
760
0
}
761
762
static void CEA708_Window_CarriageReturn( cea708_window_t *p_w )
763
0
{
764
0
    switch( p_w->style.scroll_direction )
765
0
    {
766
0
        case CEA708_WA_DIRECTION_LTR:
767
0
            if( p_w->col > 0 &&
768
0
                CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
769
0
                p_w->col--;
770
0
            else
771
0
                CEA708_Window_Scroll( p_w );
772
0
            p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
773
0
                       0 : CEA708_WINDOW_MAX_ROWS - 1;
774
0
            break;
775
0
        case CEA708_WA_DIRECTION_RTL:
776
0
            if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS &&
777
0
                CEA708_Window_ColCount( p_w ) < p_w->i_col_count )
778
0
                p_w->col++;
779
0
            else
780
0
                CEA708_Window_Scroll( p_w );
781
0
            p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ?
782
0
                       0 : CEA708_WINDOW_MAX_ROWS - 1;
783
0
            break;
784
0
        case CEA708_WA_DIRECTION_TB:
785
0
            if( p_w->row > 0 &&
786
0
                CEA708_Window_RowCount( p_w ) < p_w->i_row_count )
787
0
                p_w->row--;
788
0
            else
789
0
                CEA708_Window_Scroll( p_w );
790
0
            p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
791
0
                       0 : CEA708_WINDOW_MAX_COLS - 1;
792
0
            break;
793
0
        case CEA708_WA_DIRECTION_BT:
794
0
            if( p_w->row + 1 < p_w->i_row_count )
795
0
                p_w->row++;
796
0
            else
797
0
                CEA708_Window_Scroll( p_w );
798
0
            p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ?
799
0
                       0 : CEA708_WINDOW_MAX_COLS - 1;
800
0
            break;
801
0
    }
802
0
}
803
804
static void CEA708_Window_Forward( cea708_window_t *p_w )
805
0
{
806
0
    switch( p_w->style.print_direction )
807
0
    {
808
0
        case CEA708_WA_DIRECTION_LTR:
809
0
            if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS )
810
0
                p_w->col++;
811
0
            else
812
0
                CEA708_Window_CarriageReturn( p_w );
813
0
            break;
814
0
        case CEA708_WA_DIRECTION_RTL:
815
0
            if( p_w->col > 0 )
816
0
                p_w->col--;
817
0
            else
818
0
                CEA708_Window_CarriageReturn( p_w );
819
0
            break;
820
0
        case CEA708_WA_DIRECTION_TB:
821
0
            if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS )
822
0
                p_w->row++;
823
0
            else
824
0
                CEA708_Window_CarriageReturn( p_w );
825
0
            break;
826
0
        case CEA708_WA_DIRECTION_BT:
827
0
            if( p_w->row > 0 )
828
0
                p_w->row--;
829
0
            else
830
0
                CEA708_Window_CarriageReturn( p_w );
831
0
            break;
832
0
    }
833
0
}
834
835
static void CEA708_Window_Backward( cea708_window_t *p_w )
836
0
{
837
0
    static const int reverse[] =
838
0
    {
839
0
        [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL,
840
0
        [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR,
841
0
        [CEA708_WA_DIRECTION_TB]  = CEA708_WA_DIRECTION_BT,
842
0
        [CEA708_WA_DIRECTION_BT]  = CEA708_WA_DIRECTION_TB,
843
0
    };
844
0
    int save = p_w->style.print_direction;
845
0
    p_w->style.print_direction = reverse[p_w->style.print_direction];
846
0
    CEA708_Window_Forward( p_w );
847
0
    p_w->style.print_direction = save;
848
0
}
849
850
static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w )
851
0
{
852
0
    if( !p_w->b_defined )
853
0
        return;
854
855
856
0
    if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS ||
857
0
                  p_w->col >= CEA708_WINDOW_MAX_COLS ) )
858
0
    {
859
0
        assert( p_w->row < CEA708_WINDOW_MAX_ROWS );
860
0
        assert( p_w->col < CEA708_WINDOW_MAX_COLS );
861
0
        return;
862
0
    }
863
864
0
    cea708_text_row_t *p_row = p_w->rows[p_w->row];
865
0
    if( !p_row )
866
0
    {
867
0
        p_w->rows[p_w->row] = p_row = cea708_text_row_New();
868
0
        if( !p_row )
869
0
            return;
870
0
        if( p_w->row < p_w->i_firstrow )
871
0
            p_w->i_firstrow = p_w->row;
872
0
        if( p_w->row > p_w->i_lastrow )
873
0
            p_w->i_lastrow = p_w->row;
874
0
    }
875
876
0
    memcpy( &p_row->characters[p_w->col * 4U], c, 4 );
877
0
    p_row->styles[p_w->col] = p_w->pen;
878
0
    if( p_w->col < p_row->firstcol )
879
0
        p_row->firstcol = p_w->col;
880
0
    if( p_w->col > p_row->lastcol )
881
0
        p_row->lastcol = p_w->col;
882
883
0
    CEA708_Window_Forward( p_w );
884
885
0
    Debug(printf("\033[0;33m%s\033[0m", c));
886
0
}
887
888
static uint32_t CEA708ColorConvert( uint8_t c )
889
0
{
890
0
    const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF};
891
0
    c = c & 0x3F;
892
0
    return (value[(c >> 4) & 0x03] << 16) |
893
0
           (value[(c >> 2) & 0x03] << 8) |
894
0
           value[c & 0x03];
895
0
}
896
897
static uint8_t CEA708AlphaConvert( uint8_t c )
898
0
{
899
0
    if( c == CEA708_OPACITY_TRANSLUCENT )
900
0
        return STYLE_ALPHA_OPAQUE / 2;
901
0
    else if( c == CEA708_OPACITY_TRANSPARENT )
902
0
        return STYLE_ALPHA_TRANSPARENT;
903
0
    else
904
0
        return STYLE_ALPHA_OPAQUE;
905
0
}
906
907
static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s )
908
0
{
909
0
    if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT )
910
0
    {
911
0
        s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity );
912
0
        s->i_style_flags |= STYLE_BACKGROUND;
913
0
        s->i_background_color = CEA708ColorConvert( ps->background.color );
914
0
        s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA;
915
0
        if( ps->background.opacity == CEA708_OPACITY_FLASH )
916
0
            s->i_style_flags |= STYLE_BLINK_BACKGROUND;
917
0
    }
918
0
    s->i_font_color = CEA708ColorConvert( ps->foreground.color );
919
0
    s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity );
920
0
    s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR;
921
0
    if( ps->foreground.opacity == CEA708_OPACITY_FLASH )
922
0
        s->i_style_flags |= STYLE_BLINK_FOREGROUND;
923
924
0
    if( ps->b_italics )
925
0
        s->i_style_flags |= STYLE_ITALIC;
926
0
    if( ps->b_underline )
927
0
        s->i_style_flags |= STYLE_UNDERLINE;
928
929
0
    switch( ps->font )
930
0
    {
931
0
        default:
932
0
        case CEA708_FONT_UNDEFINED:
933
0
        case CEA708_FONT_MONOSPACED:
934
0
        case CEA708_FONT_MONO_SANS_SERIF:
935
0
            s->i_style_flags |= STYLE_MONOSPACED;
936
0
            break;
937
0
        case CEA708_FONT_PROP:
938
0
        case CEA708_FONT_PROP_SANS_SERIF:
939
0
        case CEA708_FONT_CASUAL:
940
0
        case CEA708_FONT_CURSIVE:
941
0
        case CEA708_FONT_SMALL_CAPS:
942
0
            break;
943
0
    }
944
945
0
    switch( ps->size )
946
0
    {
947
0
        case CEA708_PEN_SIZE_SMALL:
948
0
            s->f_font_relsize = CEA708_FONTRELSIZE_SMALL;
949
0
            break;
950
0
        case CEA708_PEN_SIZE_LARGE:
951
0
            s->f_font_relsize = CEA708_FONTRELSIZE_LARGE;
952
0
            break;
953
0
        default:
954
0
            s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD;
955
0
            break;
956
0
    }
957
0
}
958
959
static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row,
960
                                              uint8_t i_start, uint8_t i_end,
961
                                              bool b_newline )
962
0
{
963
0
    text_segment_t *p_segment = text_segment_New( NULL );
964
0
    if( !p_segment )
965
0
        return NULL;
966
967
0
    p_segment->style = text_style_Create( STYLE_NO_DEFAULTS );
968
0
    if( p_segment->style )
969
0
        CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style );
970
971
0
    p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U );
972
0
    if( !p_segment->psz_text )
973
0
    {
974
0
        text_segment_Delete( p_segment );
975
0
        return NULL;
976
0
    }
977
978
0
    size_t offsetw = 0;
979
0
    for( uint8_t i=i_start; i<=i_end; i++ )
980
0
    {
981
0
        for( size_t j=0; j<4; j++ )
982
0
        {
983
0
            if( p_row->characters[i * 4 + j] != 0 )
984
0
                p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j];
985
0
            else if( j == 0 )
986
0
                p_segment->psz_text[offsetw++] = ' ';
987
0
            else
988
0
                break;
989
0
        }
990
0
    }
991
992
0
    if( b_newline )
993
0
        p_segment->psz_text[offsetw++] = '\n';
994
0
    p_segment->psz_text[offsetw] = '\0';
995
996
0
    return p_segment;
997
0
}
998
999
static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row,
1000
                                             bool b_addnewline )
1001
0
{
1002
0
    text_segment_t *p_segments = NULL;
1003
0
    text_segment_t **pp_last = &p_segments;
1004
1005
0
    uint8_t i_start = p_row->firstcol;
1006
0
    for( uint8_t i=i_start; i<=p_row->lastcol; i++ )
1007
0
    {
1008
0
        if( i == p_row->lastcol ||
1009
0
            memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) )
1010
0
        {
1011
0
            *pp_last = CEA708CharsToSegment( p_row, i_start, i,
1012
0
                                             b_addnewline && (i == p_row->lastcol) );
1013
0
            while( *pp_last )
1014
0
                pp_last  = &((*pp_last)->p_next);
1015
0
            i_start = i+1;
1016
0
        }
1017
0
    }
1018
1019
0
    return p_segments;
1020
0
}
1021
1022
static void CEA708SpuConvert( const cea708_window_t *p_w,
1023
                              substext_updater_region_t *p_region,
1024
                              const video_format_t *p_fmt )
1025
0
{
1026
0
    if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 )
1027
0
        return;
1028
1029
0
    if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) )
1030
0
        return;
1031
1032
0
    int first, last;
1033
1034
0
    if (p_w->style.scroll_direction == CEA708_WA_DIRECTION_BT) {
1035
        /* BT is a bit of a special case since we need to grab the last N
1036
           rows between first and last, rather than the first... */
1037
0
        last = p_w->i_lastrow;
1038
0
        if (p_w->i_lastrow - p_w->i_row_count < p_w->i_firstrow)
1039
0
            first = p_w->i_firstrow;
1040
0
        else
1041
0
            first = p_w->i_lastrow - p_w->i_row_count + 1;
1042
1043
0
    } else {
1044
0
        first = p_w->i_firstrow;
1045
0
        if (p_w->i_firstrow + p_w->i_row_count > p_w->i_lastrow)
1046
0
            last = p_w->i_lastrow;
1047
0
        else
1048
0
            last = p_w->i_firstrow + p_w->i_row_count - 1;
1049
0
    }
1050
1051
0
    text_segment_t **pp_last = &p_region->p_segments;
1052
0
    for( uint8_t i=first; i<=last; i++ )
1053
0
    {
1054
0
        if( !p_w->rows[i] )
1055
0
            continue;
1056
1057
0
        *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow );
1058
0
        while( *pp_last )
1059
0
            pp_last  = &((*pp_last)->p_next);
1060
0
    }
1061
1062
0
    if( p_w->b_relative )
1063
0
    {
1064
        /* CEA-708-E Section 8.4: anchor_horizontal/vertical are percentages (0-99) */
1065
0
        p_region->origin.x = p_w->i_anchor_offset_h / 100.0f;
1066
1067
0
        switch (p_w->anchor_point) {
1068
0
        case CEA708_ANCHOR_TOP_LEFT:
1069
0
        case CEA708_ANCHOR_TOP_CENTER:
1070
0
        case CEA708_ANCHOR_TOP_RIGHT:
1071
0
            p_region->origin.y = p_w->i_anchor_offset_v / 100.0f;
1072
0
            break;
1073
0
        case CEA708_ANCHOR_BOTTOM_LEFT:
1074
0
        case CEA708_ANCHOR_BOTTOM_CENTER:
1075
0
        case CEA708_ANCHOR_BOTTOM_RIGHT:
1076
0
            p_region->origin.y = 1.0f - ( p_w->i_anchor_offset_v / 100.0f );
1077
0
            break;
1078
0
        case CEA708_ANCHOR_CENTER_LEFT:
1079
0
        case CEA708_ANCHOR_CENTER_CENTER:
1080
0
        case CEA708_ANCHOR_CENTER_RIGHT:
1081
0
        default:
1082
0
        {
1083
            /* Center anchors use middle 50% of screen */
1084
0
            float f_center_offset = p_w->i_anchor_offset_v / 100.0f;
1085
0
            p_region->origin.y = CEA708_CENTER_ANCHOR_START + ( f_center_offset * CEA708_CENTER_ANCHOR_RANGE );
1086
0
            break;
1087
0
        }
1088
0
        }
1089
0
    }
1090
0
    else
1091
0
    {
1092
0
        int i_grid_cols;
1093
0
        if( p_fmt->i_visible_width > 0 && p_fmt->i_visible_height > 0 )
1094
0
        {
1095
0
            unsigned dar_num = p_fmt->i_visible_width * p_fmt->i_sar_num;
1096
0
            unsigned dar_den = p_fmt->i_visible_height * p_fmt->i_sar_den;
1097
0
            if( dar_num * 3 < dar_den * 5 )
1098
0
            {
1099
0
                i_grid_cols = CEA708_SCREEN_COLS_43;
1100
0
            }
1101
0
            else
1102
0
            {
1103
0
                i_grid_cols = CEA708_SCREEN_COLS_169;
1104
0
                p_region->flags |= UPDT_REGION_USES_16_9_GRID;
1105
0
            }
1106
0
        }
1107
0
        else
1108
0
        {
1109
0
            i_grid_cols = CEA708_SCREEN_COLS_169;
1110
0
            p_region->flags |= UPDT_REGION_USES_16_9_GRID;
1111
0
        }
1112
0
        p_region->origin.x = (float)p_w->i_anchor_offset_h / i_grid_cols;
1113
0
        p_region->origin.y = (float)p_w->i_anchor_offset_v / CEA708_SCREEN_ROWS;
1114
0
    }
1115
1116
0
    p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO|UPDT_REGION_USES_GRID_COORDINATES;
1117
0
    p_region->b_absolute = false; p_region->b_in_window = false;
1118
1119
0
    if( p_w->i_firstrow <= p_w->i_lastrow )
1120
0
    {
1121
0
        p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD;
1122
        /* TODO: offset origin.x by first non-empty column (CEA708_Window_MinCol) */
1123
0
    }
1124
1125
0
    if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT )
1126
0
    {
1127
0
        static const int vlc_subpicture_aligns[] =
1128
0
        {
1129
0
            [CEA708_ANCHOR_TOP_LEFT]        = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
1130
0
            [CEA708_ANCHOR_TOP_CENTER]      = SUBPICTURE_ALIGN_TOP,
1131
0
            [CEA708_ANCHOR_TOP_RIGHT]       = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT,
1132
0
            [CEA708_ANCHOR_CENTER_LEFT]     = SUBPICTURE_ALIGN_LEFT,
1133
0
            [CEA708_ANCHOR_CENTER_CENTER]   = 0,
1134
0
            [CEA708_ANCHOR_CENTER_RIGHT]    = SUBPICTURE_ALIGN_RIGHT,
1135
0
            [CEA708_ANCHOR_BOTTOM_LEFT]     = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT,
1136
0
            [CEA708_ANCHOR_BOTTOM_CENTER]   = SUBPICTURE_ALIGN_BOTTOM,
1137
0
            [CEA708_ANCHOR_BOTTOM_RIGHT]    = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
1138
0
        };
1139
0
        p_region->align = vlc_subpicture_aligns[p_w->anchor_point];
1140
0
    }
1141
0
    p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT;
1142
0
}
1143
1144
static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 )
1145
0
{
1146
0
    subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec );
1147
0
    if( !p_spu )
1148
0
        return NULL;
1149
1150
0
    subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys;
1151
0
    substext_updater_region_t *p_region = &p_spu_sys->region;
1152
1153
0
    p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO;
1154
1155
0
    bool first = true;
1156
1157
0
    for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1158
0
    {
1159
0
        cea708_window_t *p_w = &p_cea708->window[i];
1160
0
        if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) )
1161
0
        {
1162
0
            if( !first )
1163
0
            {
1164
0
                substext_updater_region_t *p_newregion =
1165
0
                        SubpictureUpdaterSysRegionNew();
1166
0
                if( p_newregion == NULL )
1167
0
                    break;
1168
0
                SubpictureUpdaterSysRegionAdd( p_region, p_newregion );
1169
0
                p_region = p_newregion;
1170
0
            }
1171
0
            first = false;
1172
1173
            /* Fill region */
1174
0
            CEA708SpuConvert( p_w, p_region, &p_cea708->p_dec->fmt_out.video );
1175
0
        }
1176
0
    }
1177
1178
0
    p_spu->i_start    = p_cea708->i_clock;
1179
0
    p_spu->i_stop     = p_cea708->i_clock + VLC_TICK_FROM_SEC(10);   /* 10s max */
1180
1181
0
    p_spu->b_ephemer  = true;
1182
0
    p_spu->b_subtitle = true;
1183
1184
0
    return p_spu;
1185
0
}
1186
1187
static void CEA708_Decoder_Init( cea708_t *p_cea708 )
1188
32
{
1189
32
    cea708_input_buffer_init( &p_cea708->input_buffer );
1190
288
    for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1191
256
        CEA708_Window_Init( &p_cea708->window[i] );
1192
32
    p_cea708->p_cw = &p_cea708->window[0];
1193
32
    p_cea708->suspended_deadline = VLC_TICK_INVALID;
1194
32
    p_cea708->b_text_waiting = false;
1195
32
    p_cea708->i_clock = 0;
1196
32
}
1197
1198
static void CEA708_Decoder_Reset( cea708_t *p_cea708 )
1199
16
{
1200
144
    for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++)
1201
128
        CEA708_Window_Reset( &p_cea708->window[i] );
1202
16
    CEA708_Decoder_Init( p_cea708 );
1203
16
}
1204
1205
void CEA708_Decoder_Flush( cea708_t *p_cea708 )
1206
0
{
1207
0
    CEA708_Decoder_Reset( p_cea708 );
1208
0
}
1209
1210
void CEA708_Decoder_Release( cea708_t *p_cea708 )
1211
16
{
1212
16
    CEA708_Decoder_Reset( p_cea708 );
1213
16
    free( p_cea708 );
1214
16
}
1215
1216
cea708_t * CEA708_Decoder_New( decoder_t *p_dec )
1217
16
{
1218
16
    cea708_t *p_cea708 = malloc( sizeof(cea708_t) );
1219
16
    if( p_cea708 )
1220
16
    {
1221
16
        CEA708_Decoder_Init( p_cea708 );
1222
16
        p_cea708->p_dec = p_dec;
1223
16
    }
1224
16
    return p_cea708;
1225
16
}
1226
1227
0
#define POP_COMMAND() (void) cea708_input_buffer_get( ib )
1228
0
#define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND()
1229
0
#define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\
1230
0
                            return CEA708_STATUS_STARVING
1231
0
#define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND()
1232
1233
static void CEA708_Output( cea708_t *p_cea708 )
1234
0
{
1235
0
    Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708->i_clock)));
1236
0
    subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 );
1237
0
    if( p_spu )
1238
0
        decoder_QueueSub( p_cea708->p_dec, p_spu );
1239
0
}
1240
1241
static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 )
1242
0
{
1243
0
    uint8_t v, i;
1244
0
    uint16_t u16;
1245
0
    cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1246
0
    int i_ret = CEA708_STATUS_OK;
1247
1248
0
    switch( code )
1249
0
    {
1250
0
        case CEA708_C0_NUL:
1251
0
            POP_COMMAND();
1252
0
            break;
1253
0
        case CEA708_C0_ETX:
1254
0
            POP_COMMAND();
1255
0
            if( p_cea708->b_text_waiting )
1256
0
            {
1257
0
                i_ret |= CEA708_STATUS_OUTPUT;
1258
0
                p_cea708->b_text_waiting = false;
1259
0
            }
1260
0
            break;
1261
0
        case CEA708_C0_BS:
1262
0
            POP_COMMAND();
1263
0
            if( !p_cea708->p_cw->b_defined )
1264
0
                break;
1265
0
            CEA708_Window_Backward( p_cea708->p_cw );
1266
0
            p_cea708->b_text_waiting = true;
1267
0
            break;
1268
0
        case CEA708_C0_FF:
1269
0
            POP_COMMAND();
1270
0
            if( !p_cea708->p_cw->b_defined )
1271
0
                break;
1272
0
            CEA708_Window_ClearText( p_cea708->p_cw );
1273
0
            p_cea708->p_cw->col = 0;
1274
0
            p_cea708->p_cw->row = 0;
1275
0
            p_cea708->b_text_waiting = true;
1276
0
            break;
1277
0
        case CEA708_C0_CR:
1278
0
            POP_COMMAND();
1279
0
            if( !p_cea708->p_cw->b_defined )
1280
0
                break;
1281
0
            if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL )
1282
0
            {
1283
0
                CEA708_Window_CarriageReturn( p_cea708->p_cw );
1284
0
                if( p_cea708->p_cw->b_visible )
1285
0
                    i_ret |= CEA708_STATUS_OUTPUT;
1286
0
            }
1287
0
            break;
1288
0
        case CEA708_C0_HCR:
1289
0
            POP_COMMAND();
1290
0
            if( !p_cea708->p_cw->b_defined )
1291
0
                break;
1292
0
            if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL )
1293
0
            {
1294
0
                CEA708_Window_CarriageReturn( p_cea708->p_cw );
1295
0
                if( p_cea708->p_cw->b_visible )
1296
0
                    i_ret |= CEA708_STATUS_OUTPUT;
1297
0
            }
1298
0
            break;
1299
0
        case CEA708_C0_EXT1: /* Special extended table case */
1300
0
            if( cea708_input_buffer_size( ib ) >= 2 )
1301
0
            {
1302
0
                v = cea708_input_buffer_peek( ib, 1 );
1303
                /* C2 extended code set */
1304
0
                if( v < 0x20 )
1305
0
                {
1306
0
                    if( v > 0x17 )
1307
0
                        i = 3;
1308
0
                    else if( v > 0x0f )
1309
0
                        i = 2;
1310
0
                    else if( v > 0x07 )
1311
0
                        i = 1;
1312
0
                    else
1313
0
                        i = 0;
1314
0
                    if( cea708_input_buffer_size( ib ) < 2 + i )
1315
0
                        return CEA708_STATUS_STARVING;
1316
0
                    POP_COMMAND();
1317
0
                    POP_ARGS(1 + i);
1318
0
                }
1319
                /* C3 extended code set */
1320
0
                else if( v > 0x7f && v < 0xa0 )
1321
0
                {
1322
0
                    if( v > 0x87 )
1323
0
                        i = 5;
1324
0
                    else
1325
0
                        i = 4;
1326
0
                    if( cea708_input_buffer_size( ib ) < 2 + i )
1327
0
                        return CEA708_STATUS_STARVING;
1328
0
                    POP_COMMAND();
1329
0
                    POP_ARGS(1 + i);
1330
0
                }
1331
0
                else
1332
0
                {
1333
0
                    POP_COMMAND();
1334
0
                    v = cea708_input_buffer_get( ib );
1335
0
                    if( p_cea708->p_cw->b_defined )
1336
0
                        i_ret |= CEA708_Decode_G2G3( v, p_cea708 );
1337
0
                }
1338
0
            }
1339
0
            else return CEA708_STATUS_STARVING;
1340
0
            break;
1341
0
        case CEA708_C0_P16:
1342
0
            REQUIRE_ARGS_AND_POP_COMMAND(2);
1343
0
            u16 = cea708_input_buffer_get( ib ) << 8;
1344
0
            u16 |= cea708_input_buffer_get( ib );
1345
0
            i_ret |= CEA708_Decode_P16( u16, p_cea708 );
1346
0
            Debug(printf("[P16 %x]", u16));
1347
0
            break;
1348
0
        default:
1349
0
            POP_COMMAND();
1350
0
            Debug(printf("[UNK %2.2x]", code));
1351
0
            break;
1352
0
    }
1353
0
    Debug(printf("[C0 %x]", code));
1354
0
    return i_ret;
1355
0
}
1356
1357
static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 )
1358
0
{
1359
0
    cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1360
0
    POP_COMMAND();
1361
0
    int i_ret = CEA708_STATUS_OK;
1362
1363
0
    if( !p_cea708->p_cw->b_defined )
1364
0
        return i_ret;
1365
1366
0
    uint8_t utf8[4] = {code,0x00,0x00,0x00};
1367
1368
0
    if(code == 0x7F) // Music note
1369
0
    {
1370
0
        utf8[0] = 0xe2;
1371
0
        utf8[1] = 0x99;
1372
0
        utf8[2] = 0xaa;
1373
0
    }
1374
1375
0
    CEA708_Window_Write( utf8, p_cea708->p_cw );
1376
1377
0
    if( code == 0x20 &&
1378
0
        p_cea708->b_text_waiting &&
1379
0
        CEA708_Window_BreaksSpace( p_cea708->p_cw ) )
1380
0
    {
1381
0
        i_ret |= CEA708_STATUS_OUTPUT;
1382
0
    }
1383
1384
1385
0
    p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1386
1387
0
    return i_ret;
1388
0
}
1389
1390
static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 )
1391
0
{
1392
0
    uint8_t v, i;
1393
0
    cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1394
0
    int i_ret = CEA708_STATUS_OK;
1395
1396
0
    if( p_cea708->b_text_waiting )
1397
0
    {
1398
0
        i_ret |= CEA708_STATUS_OUTPUT;
1399
0
        p_cea708->b_text_waiting = false;
1400
0
    }
1401
1402
0
    switch( code )
1403
0
    {
1404
0
        case CEA708_C1_CLW:
1405
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1406
0
            Debug(printf("[CLW"));
1407
0
            for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1408
0
                if( v & 1 )
1409
0
                {
1410
0
                    if( p_cea708->window[i].b_defined &&
1411
0
                        p_cea708->window[i].b_visible )
1412
0
                        i_ret |= CEA708_STATUS_OUTPUT;
1413
0
                    CEA708_Window_ClearText( &p_cea708->window[i] );
1414
0
                    Debug(printf("%d", i));
1415
0
                }
1416
0
            Debug(printf("]"));
1417
0
            break;
1418
0
        case CEA708_C1_DSW:
1419
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1420
0
            Debug(printf("[DSW"));
1421
0
            for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1422
0
                if( v & 1 )
1423
0
                {
1424
0
                    if( p_cea708->window[i].b_defined )
1425
0
                    {
1426
0
                        if( !p_cea708->window[i].b_visible )
1427
0
                            i_ret |= CEA708_STATUS_OUTPUT;
1428
0
                        p_cea708->window[i].b_visible = true;
1429
0
                    }
1430
0
                    Debug(printf("%d", i));
1431
0
                }
1432
0
            Debug(printf("]"));
1433
0
            break;
1434
0
        case CEA708_C1_HDW:
1435
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1436
0
            Debug(printf("[HDW"));
1437
0
            for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1438
0
                if( v & 1 )
1439
0
                {
1440
0
                    if( p_cea708->window[i].b_defined )
1441
0
                    {
1442
0
                        if( p_cea708->window[i].b_visible )
1443
0
                            i_ret |= CEA708_STATUS_OUTPUT;
1444
0
                        p_cea708->window[i].b_visible = false;
1445
0
                    }
1446
0
                    Debug(printf("%d", i));
1447
0
                }
1448
0
            Debug(printf("]"));
1449
0
            break;
1450
0
        case CEA708_C1_TGW:
1451
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1452
0
            Debug(printf("[TGW"));
1453
0
            for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1454
0
                if( v & 1 )
1455
0
                {
1456
0
                    if( p_cea708->window[i].b_defined )
1457
0
                    {
1458
0
                        i_ret |= CEA708_STATUS_OUTPUT;
1459
0
                        p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible;
1460
0
                    }
1461
0
                    Debug(printf("%d", i));
1462
0
                }
1463
0
            Debug(printf("]"));
1464
0
            break;
1465
0
        case CEA708_C1_DLW:
1466
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1467
0
            Debug(printf("[DLW"));
1468
0
            for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ )
1469
0
                if( v & 1 )
1470
0
                {
1471
0
                    if( p_cea708->window[i].b_defined )
1472
0
                    {
1473
0
                        if( p_cea708->window[i].b_visible )
1474
0
                            i_ret |= CEA708_STATUS_OUTPUT;
1475
0
                        CEA708_Window_Reset( &p_cea708->window[i] );
1476
0
                    }
1477
0
                    Debug(printf("%d", i));
1478
0
                }
1479
0
            Debug(printf("]"));
1480
0
            break;
1481
0
        case CEA708_C1_DLY:
1482
0
            REQUIRE_ARGS_AND_POP_COMMAND(1);
1483
0
            p_cea708->suspended_deadline = p_cea708->i_clock +
1484
0
                    VLC_TICK_FROM_MS( cea708_input_buffer_get( ib ) * 100 );
1485
0
            Debug(printf("[DLY]"));
1486
0
            break;
1487
0
        case CEA708_C1_DLC:
1488
0
            POP_COMMAND();
1489
0
            p_cea708->suspended_deadline = VLC_TICK_INVALID;
1490
0
            Debug(printf("[DLC]"));
1491
0
            break;
1492
0
        case CEA708_C1_RST:
1493
0
            POP_COMMAND();
1494
0
            i_ret |= CEA708_STATUS_OUTPUT;
1495
            /* FIXME */
1496
0
            break;
1497
0
        case CEA708_C1_SPA:
1498
0
            REQUIRE_ARGS_AND_POP_COMMAND(2);
1499
0
            if( !p_cea708->p_cw->b_defined )
1500
0
            {
1501
0
                POP_ARGS(2);
1502
0
                break;
1503
0
            }
1504
0
            v = cea708_input_buffer_get( ib );
1505
0
            p_cea708->p_cw->pen.text_tag = v >> 4;
1506
0
            p_cea708->p_cw->pen.offset = (v >> 2) & 0x03;
1507
0
            p_cea708->p_cw->pen.size = v & 0x03;
1508
0
            v = cea708_input_buffer_get( ib );
1509
0
            p_cea708->p_cw->pen.b_italics = v & 0x80;
1510
0
            p_cea708->p_cw->pen.b_underline = v & 0x40;
1511
0
            p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07;
1512
0
            p_cea708->p_cw->pen.font = v & 0x07;
1513
0
            Debug(printf("[SPA]"));
1514
0
            break;
1515
0
        case CEA708_C1_SPC:
1516
0
            REQUIRE_ARGS_AND_POP_COMMAND(3);
1517
0
            if( !p_cea708->p_cw->b_defined )
1518
0
            {
1519
0
                POP_ARGS(3);
1520
0
                break;
1521
0
            }
1522
0
            v = cea708_input_buffer_get( ib );
1523
0
            p_cea708->p_cw->pen.foreground.opacity = v >> 6;
1524
0
            p_cea708->p_cw->pen.foreground.color = v & 0x3F;
1525
0
            v = cea708_input_buffer_get( ib );
1526
0
            p_cea708->p_cw->pen.background.opacity = v >> 6;
1527
0
            p_cea708->p_cw->pen.background.color = v & 0x3F;
1528
0
            v = cea708_input_buffer_get( ib );
1529
0
            p_cea708->p_cw->pen.edge_color = v & 0x3F;
1530
0
            Debug(printf("[SPC]"));
1531
0
            break;
1532
0
        case CEA708_C1_SPL:
1533
0
            REQUIRE_ARGS_AND_POP_COMMAND(2);
1534
0
            if( !p_cea708->p_cw->b_defined )
1535
0
            {
1536
0
                POP_ARGS(2);
1537
0
                break;
1538
0
            }
1539
0
            v = cea708_input_buffer_get( ib );
1540
0
            p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS;
1541
0
            v = cea708_input_buffer_get( ib );
1542
0
            p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS;
1543
0
            Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col));
1544
0
            break;
1545
0
        case CEA708_C1_SWA:
1546
0
            REQUIRE_ARGS_AND_POP_COMMAND(4);
1547
0
            if( !p_cea708->p_cw->b_defined )
1548
0
            {
1549
0
                POP_ARGS(4);
1550
0
                break;
1551
0
            }
1552
0
            v = cea708_input_buffer_get( ib );
1553
0
            p_cea708->p_cw->style.fill_opacity = v >> 6;
1554
0
            p_cea708->p_cw->style.fill_color_color = v & 0x3F;
1555
0
            v = cea708_input_buffer_get( ib );
1556
0
            p_cea708->p_cw->style.border_color_color = v & 0x3F;
1557
0
            p_cea708->p_cw->style.border_type = v >> 6;
1558
0
            v = cea708_input_buffer_get( ib );
1559
0
            p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5);
1560
0
            p_cea708->p_cw->style.b_word_wrap = v & 0x40;
1561
0
            p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03;
1562
0
            p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03;
1563
0
            p_cea708->p_cw->style.justify = v & 0x03;
1564
0
            v = cea708_input_buffer_get( ib );
1565
0
            p_cea708->p_cw->style.effect_speed = v >> 4;
1566
0
            p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03;
1567
0
            p_cea708->p_cw->style.display_effect = v & 0x03;
1568
0
            Debug(printf("[SWA]"));
1569
0
            break;
1570
1571
0
        default:
1572
0
            if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 )
1573
0
            {
1574
0
                POP_COMMAND();
1575
0
                Debug(printf("[CW%d]", code - CEA708_C1_CW0));
1576
0
                if( p_cea708->window[code - CEA708_C1_CW0].b_defined )
1577
0
                    p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0];
1578
0
            }
1579
0
            else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 )
1580
0
            {
1581
0
                REQUIRE_ARGS_AND_POP_COMMAND(6);
1582
0
                Debug(printf("[DF%d]", code - CEA708_C1_DF0));
1583
                /* also sets current window */
1584
0
                p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0];
1585
0
                v = cea708_input_buffer_get( ib );
1586
0
                if( p_cea708->p_cw->b_defined &&
1587
0
                   !p_cea708->p_cw->b_visible != !(v & 0x20) )
1588
0
                    i_ret |= CEA708_STATUS_OUTPUT;
1589
0
                p_cea708->p_cw->b_visible = v & 0x20;
1590
0
                p_cea708->p_cw->b_row_lock = v & 0x10;
1591
0
                p_cea708->p_cw->b_column_lock = v & 0x08;
1592
0
                p_cea708->p_cw->i_priority = v & 0x07;
1593
0
                v = cea708_input_buffer_get( ib );
1594
0
                p_cea708->p_cw->b_relative = v & 0x80;
1595
0
                p_cea708->p_cw->i_anchor_offset_v = v & 0x7F;
1596
0
                v = cea708_input_buffer_get( ib );
1597
0
                p_cea708->p_cw->i_anchor_offset_h = v;
1598
0
                v = cea708_input_buffer_get( ib );
1599
0
                p_cea708->p_cw->anchor_point = v >> 4;
1600
0
                p_cea708->p_cw->i_row_count = (v & 0x0F) + 1;
1601
0
                v = cea708_input_buffer_get( ib );
1602
0
                p_cea708->p_cw->i_col_count = v & 0x3F;
1603
0
                v = cea708_input_buffer_get( ib );
1604
                /* zero values style set on init, avoid dealing with updt case */
1605
0
                i = (v >> 3) & 0x07; /* Window style id */
1606
0
                if( i > 0 )
1607
0
                    p_cea708->p_cw->style = cea708_default_window_styles[i-1];
1608
0
                else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */
1609
0
                    p_cea708->p_cw->style = cea708_default_window_styles[0];
1610
0
                i = v & 0x07; /* Pen style id */
1611
0
                if( i > 0 )
1612
0
                    p_cea708->p_cw->pen = cea708_default_pen_styles[i-1];
1613
0
                else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */
1614
0
                    p_cea708->p_cw->pen = cea708_default_pen_styles[0];
1615
0
                p_cea708->p_cw->b_defined = true;
1616
0
            }
1617
0
            else
1618
0
            {
1619
0
                Debug(printf("{%2.2x}", code));
1620
0
                POP_COMMAND();
1621
0
            }
1622
0
    }
1623
1624
0
    return i_ret;
1625
0
}
1626
1627
static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 )
1628
0
{
1629
0
    cea708_input_buffer_t *ib = &p_cea708->input_buffer;
1630
0
    POP_COMMAND();
1631
1632
0
    if( !p_cea708->p_cw->b_defined )
1633
0
        return CEA708_STATUS_OK;
1634
1635
0
    uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6,
1636
0
                       0x80 | (code & 0x3f),
1637
0
                       0, 0};
1638
1639
0
    CEA708_Window_Write( utf8, p_cea708->p_cw );
1640
0
    p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1641
1642
0
    return CEA708_STATUS_OK;
1643
0
}
1644
1645
static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 )
1646
0
{
1647
0
    if( !p_cea708->p_cw->b_defined )
1648
0
        return CEA708_STATUS_OK;
1649
1650
0
    uint8_t out[4] = { '?', 0, 0, 0 };
1651
0
    static const struct {
1652
0
        uint8_t c;
1653
0
        uint8_t utf8[4];
1654
0
    } code2utf8[] = {
1655
        /* G2 */
1656
0
        { 0x20,     { 0x20 } },// transparent space [*** will need special handling]
1657
0
        { 0x21,     { 0x20 } },// non breaking transparent space [*** will need special handling]
1658
0
        { 0x25,     { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS
1659
0
        { 0x2a,     { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON
1660
0
        { 0x2c,     { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE
1661
0
        { 0x30,     { 0xe2,0x96,0x88 } },// FULL BLOCK
1662
0
        { 0x31,     { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK
1663
0
        { 0x32,     { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK
1664
0
        { 0x33,     { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK
1665
0
        { 0x34,     { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK
1666
0
        { 0x35,     { 0xe2,0x80,0xa2 } },// BULLET
1667
0
        { 0x39,     { 0xe2,0x84,0xa2 } },// Trademark symbol (TM)
1668
0
        { 0x3a,     { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON
1669
0
        { 0x3c,     { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE
1670
0
        { 0x3d,     { 0xe2,0x84,0xa0 } },// SERVICE MARK
1671
0
        { 0x3f,     { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS
1672
0
        { 0x76,     { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH
1673
0
        { 0x77,     { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS
1674
0
        { 0x78,     { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS
1675
0
        { 0x79,     { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS
1676
0
        { 0x7a,     { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL
1677
0
        { 0x7b,     { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT
1678
0
        { 0x7c,     { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT
1679
0
        { 0x7d,     { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL
1680
0
        { 0x7e,     { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT
1681
0
        { 0x7f,     { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT
1682
        /* G3 */
1683
0
        { 0xa0,     { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C)
1684
0
    };
1685
1686
0
    for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ )
1687
0
    {
1688
0
        if( code2utf8[i].c == code )
1689
0
        {
1690
0
            memcpy( out, code2utf8[i].utf8, 4 );
1691
0
            if(out[0] < 0xf0)
1692
0
            {
1693
0
                if(out[0] < 0x80)
1694
0
                    out[1] = 0;
1695
0
                else if(out[0] < 0xe0)
1696
0
                    out[2] = 0;
1697
0
                else
1698
0
                    out[3] = 0;
1699
0
            }
1700
0
            break;
1701
0
        }
1702
0
    }
1703
1704
0
    CEA708_Window_Write( out, p_cea708->p_cw );
1705
1706
0
    p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1707
1708
0
    return CEA708_STATUS_OK;
1709
0
}
1710
1711
static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 )
1712
0
{
1713
0
    if( !p_cea708->p_cw->b_defined )
1714
0
        return CEA708_STATUS_OK;
1715
1716
0
    uint8_t out[4] = { '?', 0, 0, 0 };
1717
1718
    /* adapted from codepoint conversion from strings.h */
1719
0
    if( ucs2 <= 0x7F )
1720
0
    {
1721
0
        out[0] = ucs2;
1722
0
    }
1723
0
    else if( ucs2 <= 0x7FF )
1724
0
    {
1725
0
        out[0] = 0xC0 |  (ucs2 >>  6);
1726
0
        out[1] = 0x80 |  (ucs2        & 0x3F);
1727
0
    }
1728
0
    else
1729
0
    {
1730
0
        out[0] = 0xE0 |  (ucs2 >> 12);
1731
0
        out[1] = 0x80 | ((ucs2 >>  6) & 0x3F);
1732
0
        out[2] = 0x80 |  (ucs2        & 0x3F);
1733
0
    }
1734
1735
0
    CEA708_Window_Write( out, p_cea708->p_cw );
1736
1737
0
    p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible;
1738
1739
0
    return CEA708_STATUS_OK;
1740
0
}
1741
1742
static void CEA708_Decode_ServiceBuffer( cea708_t *h )
1743
0
{
1744
0
    for( ;; )
1745
0
    {
1746
0
        const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer );
1747
0
        if( i_in == 0 )
1748
0
            break;
1749
1750
0
        int i_ret;
1751
0
        uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 );
1752
1753
0
        if( c < 0x20 )
1754
0
            i_ret = CEA708_Decode_C0( c, h );
1755
0
        else if( c <= 0x7F )
1756
0
            i_ret = CEA708_Decode_G0( c, h );
1757
0
        else if( c <= 0x9F )
1758
0
            i_ret = CEA708_Decode_C1( c, h );
1759
0
        else
1760
0
            i_ret = CEA708_Decode_G1( c, h );
1761
1762
0
        if( i_ret & CEA708_STATUS_OUTPUT )
1763
0
            CEA708_Output( h );
1764
1765
0
        if( i_ret & CEA708_STATUS_STARVING )
1766
0
            break;
1767
1768
        /* Update internal clock */
1769
0
        const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer );
1770
0
        if( i_consumed )
1771
0
            h->i_clock += vlc_tick_from_samples(1, 9600) * i_consumed;
1772
0
    }
1773
0
}
1774
1775
void CEA708_Decoder_Push( cea708_t *h, vlc_tick_t i_time,
1776
                          const uint8_t *p_data, size_t i_data )
1777
0
{
1778
    /* Set new buffer start time */
1779
0
    h->i_clock = i_time;
1780
1781
0
    for( size_t i=0; i<i_data; )
1782
0
    {
1783
        /* Never push more than buffer */
1784
0
        size_t i_push = cea708_input_buffer_remain(&h->input_buffer);
1785
0
        if( (i_data - i) < i_push )
1786
0
            i_push = (i_data - i);
1787
0
        else
1788
0
            h->suspended_deadline = VLC_TICK_INVALID; /* Full buffer cancels pause */
1789
1790
0
        for( size_t j=0; j<i_push; j++ )
1791
0
        {
1792
0
            uint8_t byte = p_data[i+j];
1793
0
            cea708_input_buffer_add( &h->input_buffer, byte );
1794
0
        }
1795
1796
0
        if( h->suspended_deadline != VLC_TICK_INVALID )
1797
0
        {
1798
            /* Decoding is paused */
1799
0
            if ( h->suspended_deadline > h->i_clock )
1800
0
            {
1801
                /* Increase internal clock */
1802
0
                if( i_push )
1803
0
                    h->i_clock += vlc_tick_from_samples(1, 1200) * i_push;
1804
0
                continue;
1805
0
            }
1806
0
            h->suspended_deadline = VLC_TICK_INVALID;
1807
0
        }
1808
1809
        /* Decode Buffer */
1810
0
        CEA708_Decode_ServiceBuffer( h );
1811
1812
0
        i += i_push;
1813
0
    }
1814
0
}