/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 | } |