/src/vlc/modules/codec/cc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * cc.c : CC 608/708 subtitles decoder |
3 | | ***************************************************************************** |
4 | | * Copyright © 2007-2011 Laurent Aimar, VLC authors and VideoLAN |
5 | | * 2011-2016 VLC authors and VideoLAN |
6 | | * 2016-2017 VideoLabs, VLC authors and VideoLAN |
7 | | * |
8 | | * Authors: Laurent Aimar < fenrir # via.ecp.fr> |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | /***************************************************************************** |
26 | | * Preamble |
27 | | *****************************************************************************/ |
28 | | /* The EIA 608 decoder part has been initially based on ccextractor (GPL) |
29 | | * and rewritten */ |
30 | | |
31 | | #ifdef HAVE_CONFIG_H |
32 | | # include "config.h" |
33 | | #endif |
34 | | |
35 | | #include <assert.h> |
36 | | |
37 | | #include <vlc_common.h> |
38 | | #include <vlc_plugin.h> |
39 | | #include <vlc_codec.h> |
40 | | #include <vlc_charset.h> |
41 | | |
42 | | #include "substext.h" |
43 | | #include "cea708.h" |
44 | | |
45 | | #if 0 |
46 | | #define Debug(code) code |
47 | | #else |
48 | | #define Debug(code) |
49 | | #endif |
50 | | |
51 | | /***************************************************************************** |
52 | | * Module descriptor. |
53 | | *****************************************************************************/ |
54 | | static int Open ( vlc_object_t * ); |
55 | | static void Close( vlc_object_t * ); |
56 | | |
57 | | #define OPAQUE_TEXT N_("Opacity") |
58 | | #define OPAQUE_LONGTEXT N_("Setting to true " \ |
59 | | "makes the text to be boxed and maybe easier to read." ) |
60 | | |
61 | 104 | vlc_module_begin () |
62 | 52 | set_shortname( N_("CC 608/708")) |
63 | 52 | set_description( N_("Closed Captions decoder") ) |
64 | 52 | set_capability( "spu decoder", 50 ) |
65 | 52 | set_subcategory( SUBCAT_INPUT_SCODEC ) |
66 | 52 | set_callbacks( Open, Close ) |
67 | | |
68 | 52 | add_bool( "cc-opaque", true, |
69 | 52 | OPAQUE_TEXT, OPAQUE_LONGTEXT ) |
70 | 52 | vlc_module_end () |
71 | | |
72 | | /***************************************************************************** |
73 | | * Local prototypes |
74 | | *****************************************************************************/ |
75 | | typedef enum |
76 | | { |
77 | | EIA608_MODE_POPUP = 0, |
78 | | EIA608_MODE_ROLLUP_2 = 1, |
79 | | EIA608_MODE_ROLLUP_3 = 2, |
80 | | EIA608_MODE_ROLLUP_4 = 3, |
81 | | EIA608_MODE_PAINTON = 4, |
82 | | EIA608_MODE_TEXT = 5 |
83 | | } eia608_mode_t; |
84 | | |
85 | | typedef enum |
86 | | { |
87 | | EIA608_COLOR_WHITE = 0, |
88 | | EIA608_COLOR_GREEN = 1, |
89 | | EIA608_COLOR_BLUE = 2, |
90 | | EIA608_COLOR_CYAN = 3, |
91 | | EIA608_COLOR_RED = 4, |
92 | | EIA608_COLOR_YELLOW = 5, |
93 | | EIA608_COLOR_MAGENTA = 6, |
94 | | EIA608_COLOR_USERDEFINED = 7 |
95 | | } eia608_color_t; |
96 | | |
97 | | typedef enum |
98 | | { |
99 | | EIA608_FONT_REGULAR = 0x00, |
100 | | EIA608_FONT_ITALICS = 0x01, |
101 | | EIA608_FONT_UNDERLINE = 0x02, |
102 | | EIA608_FONT_UNDERLINE_ITALICS = EIA608_FONT_UNDERLINE | EIA608_FONT_ITALICS |
103 | | } eia608_font_t; |
104 | | |
105 | 69.4k | #define EIA608_SCREEN_ROWS 15 |
106 | 2.79M | #define EIA608_SCREEN_COLUMNS 32 |
107 | | |
108 | 3.10k | #define EIA608_MARGIN 0.10f |
109 | 1.55k | #define EIA608_VISIBLE (1.0f - EIA608_MARGIN * 2) |
110 | 2.79k | #define FONT_TO_LINE_HEIGHT_RATIO 1.06 |
111 | | |
112 | | struct eia608_screen // A CC buffer |
113 | | { |
114 | | uint8_t characters[EIA608_SCREEN_ROWS][EIA608_SCREEN_COLUMNS+1]; |
115 | | eia608_color_t colors[EIA608_SCREEN_ROWS][EIA608_SCREEN_COLUMNS+1]; |
116 | | eia608_font_t fonts[EIA608_SCREEN_ROWS][EIA608_SCREEN_COLUMNS+1]; // Extra char at the end for a 0 |
117 | | int row_used[EIA608_SCREEN_ROWS]; // Any data in row? |
118 | | }; |
119 | | typedef struct eia608_screen eia608_screen; |
120 | | |
121 | | typedef enum |
122 | | { |
123 | | EIA608_STATUS_DEFAULT = 0x00, |
124 | | EIA608_STATUS_CHANGED = 0x01, /* current screen has been altered */ |
125 | | EIA608_STATUS_CAPTION_ENDED = 0x02, /* screen flip */ |
126 | | EIA608_STATUS_CAPTION_CLEARED = 0x04, /* active screen erased */ |
127 | | EIA608_STATUS_DISPLAY = EIA608_STATUS_CAPTION_CLEARED | EIA608_STATUS_CAPTION_ENDED, |
128 | | } eia608_status_t; |
129 | | |
130 | | static const struct { |
131 | | eia608_color_t i_color; |
132 | | eia608_font_t i_font; |
133 | | int i_column; |
134 | | } pac2_attribs[]= { |
135 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 0 }, |
136 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 0 }, |
137 | | { EIA608_COLOR_GREEN, EIA608_FONT_REGULAR, 0 }, |
138 | | { EIA608_COLOR_GREEN, EIA608_FONT_UNDERLINE, 0 }, |
139 | | { EIA608_COLOR_BLUE, EIA608_FONT_REGULAR, 0 }, |
140 | | { EIA608_COLOR_BLUE, EIA608_FONT_UNDERLINE, 0 }, |
141 | | { EIA608_COLOR_CYAN, EIA608_FONT_REGULAR, 0 }, |
142 | | { EIA608_COLOR_CYAN, EIA608_FONT_UNDERLINE, 0 }, |
143 | | { EIA608_COLOR_RED, EIA608_FONT_REGULAR, 0 }, |
144 | | { EIA608_COLOR_RED, EIA608_FONT_UNDERLINE, 0 }, |
145 | | { EIA608_COLOR_YELLOW, EIA608_FONT_REGULAR, 0 }, |
146 | | { EIA608_COLOR_YELLOW, EIA608_FONT_UNDERLINE, 0 }, |
147 | | { EIA608_COLOR_MAGENTA, EIA608_FONT_REGULAR, 0 }, |
148 | | { EIA608_COLOR_MAGENTA, EIA608_FONT_UNDERLINE, 0 }, |
149 | | { EIA608_COLOR_WHITE, EIA608_FONT_ITALICS, 0 }, |
150 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE_ITALICS, 0 }, |
151 | | |
152 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 0 }, |
153 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 0 }, |
154 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 4 }, |
155 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 4 }, |
156 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 8 }, |
157 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 8 }, |
158 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 12 }, |
159 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 12 }, |
160 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 16 }, |
161 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 16 }, |
162 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 20 }, |
163 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 20 }, |
164 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 24 }, |
165 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 24 }, |
166 | | { EIA608_COLOR_WHITE, EIA608_FONT_REGULAR, 28 }, |
167 | | { EIA608_COLOR_WHITE, EIA608_FONT_UNDERLINE, 28 } , |
168 | | }; |
169 | | |
170 | 1.37M | #define EIA608_COLOR_DEFAULT EIA608_COLOR_WHITE |
171 | | |
172 | | static const uint32_t rgi_eia608_colors[] = { |
173 | | 0xffffff, // white |
174 | | 0x00ff00, // green |
175 | | 0x0000ff, // blue |
176 | | 0x00ffff, // cyan |
177 | | 0xff0000, // red |
178 | | 0xffff00, // yellow |
179 | | 0xff00ff, // magenta |
180 | | 0xffffff, // user defined XXX we use white |
181 | | }; |
182 | | |
183 | | typedef struct |
184 | | { |
185 | | /* Current channel (used to reject packet without channel information) */ |
186 | | int i_channel; |
187 | | |
188 | | /* */ |
189 | | int i_screen; /* Displayed screen */ |
190 | | eia608_screen screen[2]; |
191 | | |
192 | | struct |
193 | | { |
194 | | int i_row; |
195 | | int i_column; |
196 | | } cursor; |
197 | | |
198 | | /* */ |
199 | | eia608_mode_t mode; |
200 | | eia608_color_t color; |
201 | | eia608_font_t font; |
202 | | int i_row_rollup; |
203 | | |
204 | | /* Last command pair (used to reject duplicated command) */ |
205 | | struct |
206 | | { |
207 | | uint8_t d1; |
208 | | uint8_t d2; |
209 | | } last; |
210 | | } eia608_t; |
211 | | |
212 | | static void Eia608Init( eia608_t * ); |
213 | | static eia608_status_t Eia608Parse( eia608_t *h, int i_channel_selected, const uint8_t data[2] ); |
214 | | static void Eia608FillUpdaterRegions( subtext_updater_sys_t *p_updater, eia608_t *h ); |
215 | | |
216 | | /* It will be enough up to 63 B frames, which is far too high for |
217 | | * broadcast environment */ |
218 | 10.1k | #define CC_MAX_REORDER_SIZE (64) |
219 | | typedef struct |
220 | | { |
221 | | int i_queue; |
222 | | block_t *p_queue; |
223 | | |
224 | | int i_field; |
225 | | int i_channel; |
226 | | |
227 | | int i_reorder_depth; |
228 | | |
229 | | cea708_demux_t *p_dtvcc; |
230 | | |
231 | | cea708_t *p_cea708; |
232 | | eia608_t *p_eia608; |
233 | | bool b_opaque; |
234 | | } decoder_sys_t; |
235 | | |
236 | | static int Decode( decoder_t *, block_t * ); |
237 | | static void Flush( decoder_t * ); |
238 | | |
239 | | static void DTVCC_ServiceData_Handler( void *priv, uint8_t i_sid, vlc_tick_t i_time, |
240 | | const uint8_t *p_data, size_t i_data ) |
241 | 0 | { |
242 | 0 | decoder_t *p_dec = priv; |
243 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
244 | | //msg_Err( p_dec, "DTVCC_ServiceData_Handler sid %d bytes %ld", i_sid, i_data ); |
245 | 0 | if( i_sid == 1 + p_dec->fmt_in->subs.cc.i_channel ) |
246 | 0 | CEA708_Decoder_Push( p_sys->p_cea708, i_time, p_data, i_data ); |
247 | 0 | } |
248 | | |
249 | | /***************************************************************************** |
250 | | * Open: probe the decoder and return score |
251 | | ***************************************************************************** |
252 | | * Tries to launch a decoder and return score so that the interface is able |
253 | | * to chose. |
254 | | *****************************************************************************/ |
255 | | static int Open( vlc_object_t *p_this ) |
256 | 10.6k | { |
257 | 10.6k | decoder_t *p_dec = (decoder_t*)p_this; |
258 | 10.6k | decoder_sys_t *p_sys; |
259 | | |
260 | 10.6k | if( ( p_dec->fmt_in->i_codec != VLC_CODEC_CEA608 || |
261 | 10.6k | p_dec->fmt_in->subs.cc.i_channel > 3 ) && |
262 | 10.6k | ( p_dec->fmt_in->i_codec != VLC_CODEC_CEA708 || |
263 | 9.55k | p_dec->fmt_in->subs.cc.i_channel > 63 ) ) |
264 | 9.55k | return VLC_EGENERIC; |
265 | | |
266 | 1.12k | p_dec->pf_decode = Decode; |
267 | 1.12k | p_dec->pf_flush = Flush; |
268 | | |
269 | | /* Allocate the memory needed to store the decoder's structure */ |
270 | 1.12k | p_dec->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); |
271 | 1.12k | if( p_sys == NULL ) |
272 | 0 | return VLC_ENOMEM; |
273 | | |
274 | 1.12k | if( p_dec->fmt_in->i_codec == VLC_CODEC_CEA608 ) |
275 | 1.12k | { |
276 | | /* 0 -> i_field = 0; i_channel = 1; |
277 | | 1 -> i_field = 0; i_channel = 2; |
278 | | 2 -> i_field = 1; i_channel = 1; |
279 | | 3 -> i_field = 1; i_channel = 2; */ |
280 | 1.12k | p_sys->i_field = p_dec->fmt_in->subs.cc.i_channel >> 1; |
281 | 1.12k | p_sys->i_channel = 1 + (p_dec->fmt_in->subs.cc.i_channel & 1); |
282 | | |
283 | 1.12k | p_sys->p_eia608 = malloc(sizeof(*p_sys->p_eia608)); |
284 | 1.12k | if( !p_sys->p_eia608 ) |
285 | 0 | { |
286 | 0 | free( p_sys ); |
287 | 0 | return VLC_ENOMEM; |
288 | 0 | } |
289 | 1.12k | Eia608Init( p_sys->p_eia608 ); |
290 | 1.12k | } |
291 | 1 | else |
292 | 1 | { |
293 | 1 | p_sys->p_dtvcc = CEA708_DTVCC_Demuxer_New( p_dec, DTVCC_ServiceData_Handler ); |
294 | 1 | if( !p_sys->p_dtvcc ) |
295 | 0 | { |
296 | 0 | free( p_sys ); |
297 | 0 | return VLC_ENOMEM; |
298 | 0 | } |
299 | | |
300 | 1 | p_sys->p_cea708 = CEA708_Decoder_New( p_dec ); |
301 | 1 | if( !p_sys->p_cea708 ) |
302 | 0 | { |
303 | 0 | CEA708_DTVCC_Demuxer_Release( p_sys->p_dtvcc ); |
304 | 0 | free( p_sys ); |
305 | 0 | return VLC_ENOMEM; |
306 | 0 | } |
307 | | |
308 | 1 | p_sys->i_channel = p_dec->fmt_in->subs.cc.i_channel; |
309 | 1 | } |
310 | | |
311 | 1.12k | p_sys->b_opaque = var_InheritBool( p_dec, "cc-opaque" ); |
312 | 1.12k | p_sys->i_reorder_depth = p_dec->fmt_in->subs.cc.i_reorder_depth; |
313 | | |
314 | 1.12k | p_dec->fmt_out.i_codec = VLC_CODEC_TEXT; |
315 | | |
316 | 1.12k | return VLC_SUCCESS; |
317 | 1.12k | } |
318 | | |
319 | | /***************************************************************************** |
320 | | * Flush: |
321 | | *****************************************************************************/ |
322 | | static void Flush( decoder_t *p_dec ) |
323 | 0 | { |
324 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
325 | |
|
326 | 0 | if( p_sys->p_eia608 ) |
327 | 0 | { |
328 | 0 | Eia608Init( p_sys->p_eia608 ); |
329 | 0 | } |
330 | 0 | else |
331 | 0 | { |
332 | 0 | CEA708_DTVCC_Demuxer_Flush( p_sys->p_dtvcc ); |
333 | 0 | CEA708_Decoder_Flush( p_sys->p_cea708 ); |
334 | 0 | } |
335 | |
|
336 | 0 | block_ChainRelease( p_sys->p_queue ); |
337 | 0 | p_sys->p_queue = NULL; |
338 | 0 | p_sys->i_queue = 0; |
339 | 0 | } |
340 | | |
341 | | /**************************************************************************** |
342 | | * Decode: the whole thing |
343 | | **************************************************************************** |
344 | | * |
345 | | ****************************************************************************/ |
346 | | static void Push( decoder_t *, block_t * ); |
347 | | static block_t *Pop( decoder_t *, bool ); |
348 | | static void Convert( decoder_t *, vlc_tick_t, const uint8_t *, size_t ); |
349 | | |
350 | | static bool DoDecode( decoder_t *p_dec, bool b_drain ) |
351 | 21.5k | { |
352 | 21.5k | block_t *p_block = Pop( p_dec, b_drain ); |
353 | 21.5k | if( !p_block ) |
354 | 16.4k | return false; |
355 | | |
356 | 5.09k | Convert( p_dec, p_block->i_pts, p_block->p_buffer, p_block->i_buffer ); |
357 | 5.09k | block_Release( p_block ); |
358 | | |
359 | 5.09k | return true; |
360 | 21.5k | } |
361 | | |
362 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
363 | 11.3k | { |
364 | 11.3k | decoder_sys_t *p_sys = p_dec->p_sys; |
365 | | |
366 | 11.3k | if( p_block ) |
367 | 5.09k | { |
368 | | /* Reset decoder if needed */ |
369 | 5.09k | if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) |
370 | 0 | { |
371 | | /* Drain */ |
372 | 0 | for( ; DoDecode( p_dec, true ) ; ); |
373 | 0 | if( p_sys->p_eia608 ) |
374 | 0 | { |
375 | 0 | Eia608Init( p_sys->p_eia608 ); |
376 | 0 | } |
377 | 0 | else |
378 | 0 | { |
379 | 0 | CEA708_DTVCC_Demuxer_Flush( p_sys->p_dtvcc ); |
380 | 0 | CEA708_Decoder_Flush( p_sys->p_cea708 ); |
381 | 0 | } |
382 | |
|
383 | 0 | if( (p_block->i_flags & BLOCK_FLAG_CORRUPTED) || p_block->i_buffer < 1 ) |
384 | 0 | { |
385 | 0 | block_Release( p_block ); |
386 | 0 | return VLCDEC_SUCCESS; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | /* XXX Cc captions data are OUT OF ORDER (because we receive them in the bitstream |
391 | | * order (ie ordered by video picture dts) instead of the display order. |
392 | | * We will simulate a simple IPB buffer scheme |
393 | | * and reorder with pts. |
394 | | * XXX it won't work with H264 which use non out of order B picture or MMCO */ |
395 | 5.09k | if( p_sys->i_reorder_depth == 0 ) |
396 | 5.08k | { |
397 | | /* Wait for a P and output all *previous* picture by pts order (for |
398 | | * hierarchical B frames) */ |
399 | 5.08k | if( (p_block->i_flags & BLOCK_FLAG_TYPE_B) == 0 ) |
400 | 5.08k | for( ; DoDecode( p_dec, true ); ); |
401 | 5.08k | } |
402 | | |
403 | 5.09k | Push( p_dec, p_block ); |
404 | 5.09k | } |
405 | | |
406 | 11.3k | const bool b_no_reorder = (p_dec->fmt_in->subs.cc.i_reorder_depth < 0); |
407 | 16.4k | for( ; DoDecode( p_dec, (p_block == NULL) || b_no_reorder ); ); |
408 | | |
409 | 11.3k | return VLCDEC_SUCCESS; |
410 | 11.3k | } |
411 | | |
412 | | /***************************************************************************** |
413 | | * CloseDecoder: clean up the decoder |
414 | | *****************************************************************************/ |
415 | | static void Close( vlc_object_t *p_this ) |
416 | 1.12k | { |
417 | 1.12k | decoder_t *p_dec = (decoder_t *)p_this; |
418 | 1.12k | decoder_sys_t *p_sys = p_dec->p_sys; |
419 | | |
420 | 1.12k | free( p_sys->p_eia608 ); |
421 | 1.12k | if( p_sys->p_cea708 ) |
422 | 1 | { |
423 | 1 | CEA708_Decoder_Release( p_sys->p_cea708 ); |
424 | 1 | CEA708_DTVCC_Demuxer_Release( p_sys->p_dtvcc ); |
425 | 1 | } |
426 | | |
427 | 1.12k | block_ChainRelease( p_sys->p_queue ); |
428 | 1.12k | free( p_sys ); |
429 | 1.12k | } |
430 | | |
431 | | /***************************************************************************** |
432 | | * |
433 | | *****************************************************************************/ |
434 | | static void Push( decoder_t *p_dec, block_t *p_block ) |
435 | 5.09k | { |
436 | 5.09k | decoder_sys_t *p_sys = p_dec->p_sys; |
437 | | |
438 | 5.09k | if( p_sys->i_queue >= CC_MAX_REORDER_SIZE ) |
439 | 0 | { |
440 | 0 | block_Release( Pop( p_dec, true ) ); |
441 | 0 | msg_Warn( p_dec, "Trashing a CC entry" ); |
442 | 0 | } |
443 | | |
444 | 5.09k | block_t **pp_block; |
445 | | /* find insertion point */ |
446 | 5.09k | for( pp_block = &p_sys->p_queue; *pp_block ; pp_block = &((*pp_block)->p_next) ) |
447 | 0 | { |
448 | 0 | if( p_block->i_pts == VLC_TICK_INVALID || (*pp_block)->i_pts == VLC_TICK_INVALID ) |
449 | 0 | continue; |
450 | 0 | if( p_block->i_pts < (*pp_block)->i_pts ) |
451 | 0 | { |
452 | 0 | if( p_sys->i_reorder_depth > 0 && |
453 | 0 | p_sys->i_queue < p_sys->i_reorder_depth && |
454 | 0 | pp_block == &p_sys->p_queue ) |
455 | 0 | { |
456 | 0 | msg_Info( p_dec, "Increasing reorder depth to %d", ++p_sys->i_reorder_depth ); |
457 | 0 | } |
458 | 0 | break; |
459 | 0 | } |
460 | 0 | } |
461 | | /* Insert, keeping a pts and/or fifo ordered list */ |
462 | 5.09k | p_block->p_next = *pp_block ? *pp_block : NULL; |
463 | 5.09k | *pp_block = p_block; |
464 | 5.09k | p_sys->i_queue++; |
465 | 5.09k | } |
466 | | |
467 | | static block_t *Pop( decoder_t *p_dec, bool b_forced ) |
468 | 21.5k | { |
469 | 21.5k | decoder_sys_t *p_sys = p_dec->p_sys; |
470 | 21.5k | block_t *p_block; |
471 | | |
472 | 21.5k | if( p_sys->i_queue == 0 ) |
473 | 11.3k | return NULL; |
474 | | |
475 | 10.1k | if( !b_forced && p_sys->i_queue < CC_MAX_REORDER_SIZE ) |
476 | 5.08k | { |
477 | 5.08k | if( p_sys->i_queue < p_sys->i_reorder_depth || p_sys->i_reorder_depth == 0 ) |
478 | 5.08k | return NULL; |
479 | 5.08k | } |
480 | | |
481 | | /* dequeue head */ |
482 | 5.09k | p_block = p_sys->p_queue; |
483 | 5.09k | p_sys->p_queue = p_block->p_next; |
484 | 5.09k | p_block->p_next = NULL; |
485 | 5.09k | p_sys->i_queue--; |
486 | | |
487 | 5.09k | return p_block; |
488 | 10.1k | } |
489 | | |
490 | | static subpicture_t *Subtitle( decoder_t *p_dec, eia608_t *h, vlc_tick_t i_pts ) |
491 | 1.55k | { |
492 | | //decoder_sys_t *p_sys = p_dec->p_sys; |
493 | 1.55k | subpicture_t *p_spu = NULL; |
494 | | |
495 | | /* We cannot display a subpicture with no date */ |
496 | 1.55k | if( i_pts == VLC_TICK_INVALID ) |
497 | 0 | return NULL; |
498 | | |
499 | | /* Create the subpicture unit */ |
500 | 1.55k | p_spu = decoder_NewSubpictureText( p_dec ); |
501 | 1.55k | if( !p_spu ) |
502 | 0 | return NULL; |
503 | | |
504 | 1.55k | p_spu->i_start = i_pts; |
505 | 1.55k | p_spu->i_stop = i_pts + VLC_TICK_FROM_SEC(10); /* 10s max */ |
506 | 1.55k | p_spu->b_ephemer = true; |
507 | | |
508 | 1.55k | subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys; |
509 | 1.55k | decoder_sys_t *p_dec_sys = p_dec->p_sys; |
510 | | |
511 | | /* Set first region defaults */ |
512 | | /* The "leavetext" alignment is a special mode where the subpicture |
513 | | region itself gets aligned, but the text inside it does not */ |
514 | 1.55k | p_spu_sys->region.b_absolute = false; p_spu_sys->region.b_in_window = false; |
515 | 1.55k | p_spu_sys->region.align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT; |
516 | 1.55k | p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT; |
517 | 1.55k | p_spu_sys->region.flags = UPDT_REGION_IGNORE_BACKGROUND | UPDT_REGION_USES_GRID_COORDINATES; |
518 | | |
519 | | /* Set style defaults (will be added to segments if none set) */ |
520 | 1.55k | p_spu_sys->p_default_style->i_style_flags |= STYLE_MONOSPACED; |
521 | 1.55k | if( p_dec_sys->b_opaque ) |
522 | 1.55k | { |
523 | 1.55k | p_spu_sys->p_default_style->i_background_alpha = STYLE_ALPHA_OPAQUE; |
524 | 1.55k | p_spu_sys->p_default_style->i_features |= STYLE_HAS_BACKGROUND_ALPHA; |
525 | 1.55k | p_spu_sys->p_default_style->i_style_flags |= STYLE_BACKGROUND; |
526 | 1.55k | } |
527 | 1.55k | p_spu_sys->margin_ratio = EIA608_MARGIN; |
528 | 1.55k | p_spu_sys->p_default_style->i_font_color = rgi_eia608_colors[EIA608_COLOR_DEFAULT]; |
529 | | /* FCC defined "safe area" for EIA-608 captions is 80% of the height of the display */ |
530 | 1.55k | p_spu_sys->p_default_style->f_font_relsize = EIA608_VISIBLE * 100 / EIA608_SCREEN_ROWS / |
531 | 1.55k | FONT_TO_LINE_HEIGHT_RATIO; |
532 | 1.55k | p_spu_sys->p_default_style->i_features |= (STYLE_HAS_FONT_COLOR | STYLE_HAS_FLAGS); |
533 | | |
534 | 1.55k | Eia608FillUpdaterRegions( p_spu_sys, h ); |
535 | | |
536 | 1.55k | return p_spu; |
537 | 1.55k | } |
538 | | |
539 | | static void Convert( decoder_t *p_dec, vlc_tick_t i_pts, |
540 | | const uint8_t *p_buffer, size_t i_buffer ) |
541 | 5.09k | { |
542 | 5.09k | decoder_sys_t *p_sys = p_dec->p_sys; |
543 | | |
544 | 5.09k | unsigned i_ticks = 0; |
545 | 47.4k | while( i_buffer >= 3 ) |
546 | 42.4k | { |
547 | 42.4k | if( (p_buffer[0] & 0x04) /* Valid bit */ ) |
548 | 42.4k | { |
549 | 42.4k | const vlc_tick_t i_spupts = i_pts + vlc_tick_from_samples(i_ticks, 1200/3); |
550 | | /* Mask off the specific i_field bit, else some sequences can be lost. */ |
551 | 42.4k | if ( p_sys->p_eia608 && |
552 | 42.4k | (p_buffer[0] & 0x03) == p_sys->i_field ) |
553 | 21.2k | { |
554 | 21.2k | eia608_status_t i_status = Eia608Parse( p_sys->p_eia608, |
555 | 21.2k | p_sys->i_channel, &p_buffer[1] ); |
556 | | |
557 | | /* a caption is ready or removed, process its screen */ |
558 | | /* |
559 | | * In case of rollup/painton with 1 packet/frame, we need |
560 | | * to update on Changed status. |
561 | | * Batch decoding might be incorrect if those in |
562 | | * large number of commands (mp4, ...) then. |
563 | | * see CEAv1.2zero.trp tests */ |
564 | 21.2k | if( i_status & (EIA608_STATUS_DISPLAY | EIA608_STATUS_CHANGED) ) |
565 | 1.55k | { |
566 | 1.55k | Debug(printf("\n")); |
567 | 1.55k | subpicture_t *p_spu = Subtitle( p_dec, p_sys->p_eia608, i_spupts ); |
568 | 1.55k | if( p_spu ) |
569 | 1.55k | decoder_QueueSub( p_dec, p_spu ); |
570 | 1.55k | } |
571 | 21.2k | } |
572 | 21.1k | else if( p_sys->p_cea708 && (p_buffer[0] & 0x03) >= 2 ) |
573 | 0 | { |
574 | 0 | CEA708_DTVCC_Demuxer_Push( p_sys->p_dtvcc, i_spupts, p_buffer ); |
575 | 0 | } |
576 | 42.4k | } |
577 | | |
578 | 42.4k | i_ticks++; |
579 | | |
580 | 42.4k | i_buffer -= 3; |
581 | 42.4k | p_buffer += 3; |
582 | 42.4k | } |
583 | 5.09k | } |
584 | | |
585 | | |
586 | | /***************************************************************************** |
587 | | * |
588 | | *****************************************************************************/ |
589 | | static void Eia608Cursor( eia608_t *h, int dx ) |
590 | 4.68k | { |
591 | 4.68k | h->cursor.i_column += dx; |
592 | 4.68k | if( h->cursor.i_column < 0 ) |
593 | 0 | h->cursor.i_column = 0; |
594 | 4.68k | else if( h->cursor.i_column > EIA608_SCREEN_COLUMNS-1 ) |
595 | 0 | h->cursor.i_column = EIA608_SCREEN_COLUMNS-1; |
596 | 4.68k | } |
597 | | static void Eia608ClearScreenRowX( eia608_t *h, int i_screen, int i_row, int x ) |
598 | 41.5k | { |
599 | 41.5k | eia608_screen *screen = &h->screen[i_screen]; |
600 | | |
601 | 41.5k | if( x == 0 ) |
602 | 41.5k | { |
603 | 41.5k | screen->row_used[i_row] = false; |
604 | 41.5k | } |
605 | 0 | else |
606 | 0 | { |
607 | 0 | screen->row_used[i_row] = false; |
608 | 0 | for( int i = 0; i < x; i++ ) |
609 | 0 | { |
610 | 0 | if( screen->characters[i_row][i] != ' ' || |
611 | 0 | screen->colors[i_row][i] != EIA608_COLOR_DEFAULT || |
612 | 0 | screen->fonts[i_row][i] != EIA608_FONT_REGULAR ) |
613 | 0 | { |
614 | 0 | screen->row_used[i_row] = true; |
615 | 0 | break; |
616 | 0 | } |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | 1.41M | for( ; x < EIA608_SCREEN_COLUMNS+1; x++ ) |
621 | 1.37M | { |
622 | 1.37M | screen->characters[i_row][x] = x < EIA608_SCREEN_COLUMNS ? ' ' : '\0'; |
623 | 1.37M | screen->colors[i_row][x] = EIA608_COLOR_DEFAULT; |
624 | 1.37M | screen->fonts[i_row][x] = EIA608_FONT_REGULAR; |
625 | 1.37M | } |
626 | 41.5k | } |
627 | | |
628 | | static void Eia608ClearScreenRow( eia608_t *h, int i_screen, int i_row ) |
629 | 41.5k | { |
630 | 41.5k | Eia608ClearScreenRowX( h, i_screen, i_row, 0 ); |
631 | 41.5k | } |
632 | | |
633 | | static void Eia608ClearScreen( eia608_t *h, int i_screen ) |
634 | 2.54k | { |
635 | 40.7k | for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) |
636 | 38.1k | Eia608ClearScreenRow( h, i_screen, i ); |
637 | 2.54k | } |
638 | | |
639 | | static int Eia608GetWritingScreenIndex( eia608_t *h ) |
640 | 4.98k | { |
641 | 4.98k | switch( h->mode ) |
642 | 4.98k | { |
643 | 2.50k | case EIA608_MODE_POPUP: // Non displayed screen |
644 | 2.50k | return 1 - h->i_screen; |
645 | | |
646 | 0 | case EIA608_MODE_ROLLUP_2: // Displayed screen |
647 | 2.43k | case EIA608_MODE_ROLLUP_3: |
648 | 2.43k | case EIA608_MODE_ROLLUP_4: |
649 | 2.48k | case EIA608_MODE_PAINTON: |
650 | 2.48k | return h->i_screen; |
651 | 0 | default: |
652 | | /* It cannot happen, else it is a bug */ |
653 | 0 | vlc_assert_unreachable(); |
654 | 0 | return 0; |
655 | 4.98k | } |
656 | 4.98k | } |
657 | | |
658 | | static void Eia608EraseScreen( eia608_t *h, bool b_displayed ) |
659 | 293 | { |
660 | 293 | Eia608ClearScreen( h, b_displayed ? h->i_screen : (1-h->i_screen) ); |
661 | 293 | } |
662 | | |
663 | | static void Eia608Write( eia608_t *h, const uint8_t c ) |
664 | 4.67k | { |
665 | 4.67k | const int i_row = h->cursor.i_row; |
666 | 4.67k | const int i_column = h->cursor.i_column; |
667 | 4.67k | eia608_screen *screen; |
668 | | |
669 | 4.67k | if( h->mode == EIA608_MODE_TEXT ) |
670 | 0 | return; |
671 | | |
672 | 4.67k | screen = &h->screen[Eia608GetWritingScreenIndex( h )]; |
673 | | |
674 | 4.67k | screen->characters[i_row][i_column] = c; |
675 | 4.67k | screen->colors[i_row][i_column] = h->color; |
676 | 4.67k | screen->fonts[i_row][i_column] = h->font; |
677 | 4.67k | screen->row_used[i_row] = true; |
678 | 4.67k | Eia608Cursor( h, 1 ); |
679 | 4.67k | } |
680 | | static void Eia608Erase( eia608_t *h ) |
681 | 14 | { |
682 | 14 | const int i_row = h->cursor.i_row; |
683 | 14 | const int i_column = h->cursor.i_column - 1; |
684 | 14 | eia608_screen *screen; |
685 | | |
686 | 14 | if( h->mode == EIA608_MODE_TEXT ) |
687 | 14 | return; |
688 | 0 | if( i_column < 0 ) |
689 | 0 | return; |
690 | | |
691 | 0 | screen = &h->screen[Eia608GetWritingScreenIndex( h )]; |
692 | | |
693 | | /* FIXME do we need to reset row_used/colors/font ? */ |
694 | 0 | screen->characters[i_row][i_column] = ' '; |
695 | 0 | Eia608Cursor( h, -1 ); |
696 | 0 | } |
697 | | static void Eia608EraseToEndOfRow( eia608_t *h ) |
698 | 0 | { |
699 | 0 | if( h->mode == EIA608_MODE_TEXT ) |
700 | 0 | return; |
701 | | |
702 | 0 | Eia608ClearScreenRowX( h, Eia608GetWritingScreenIndex( h ), h->cursor.i_row, h->cursor.i_column ); |
703 | 0 | } |
704 | | |
705 | | static void Eia608RollUp( eia608_t *h ) |
706 | 306 | { |
707 | 306 | if( h->mode == EIA608_MODE_TEXT ) |
708 | 0 | return; |
709 | | |
710 | 306 | const int i_screen = Eia608GetWritingScreenIndex( h ); |
711 | 306 | eia608_screen *screen = &h->screen[i_screen]; |
712 | | |
713 | 306 | int keep_lines; |
714 | | |
715 | | /* Window size */ |
716 | 306 | if( h->mode == EIA608_MODE_ROLLUP_2 ) |
717 | 0 | keep_lines = 2; |
718 | 306 | else if( h->mode == EIA608_MODE_ROLLUP_3 ) |
719 | 280 | keep_lines = 3; |
720 | 26 | else if( h->mode == EIA608_MODE_ROLLUP_4 ) |
721 | 0 | keep_lines = 4; |
722 | 26 | else |
723 | 26 | return; |
724 | | |
725 | | /* Reset the cursor */ |
726 | 280 | h->cursor.i_column = 0; |
727 | | |
728 | | /* Erase lines above our window */ |
729 | 3.36k | for( int i = 0; i < h->cursor.i_row - keep_lines; i++ ) |
730 | 3.08k | Eia608ClearScreenRow( h, i_screen, i ); |
731 | | |
732 | | /* Move up */ |
733 | 840 | for( int i = 0; i < keep_lines-1; i++ ) |
734 | 560 | { |
735 | 560 | const int i_row = h->cursor.i_row - keep_lines + i + 1; |
736 | 560 | if( i_row < 0 ) |
737 | 0 | continue; |
738 | 560 | assert( i_row+1 < EIA608_SCREEN_ROWS ); |
739 | 560 | memcpy( screen->characters[i_row], screen->characters[i_row+1], sizeof(*screen->characters) ); |
740 | 560 | memcpy( screen->colors[i_row], screen->colors[i_row+1], sizeof(*screen->colors) ); |
741 | 560 | memcpy( screen->fonts[i_row], screen->fonts[i_row+1], sizeof(*screen->fonts) ); |
742 | 560 | screen->row_used[i_row] = screen->row_used[i_row+1]; |
743 | 560 | } |
744 | | /* Reset current row */ |
745 | 280 | Eia608ClearScreenRow( h, i_screen, h->cursor.i_row ); |
746 | 280 | } |
747 | | static void Eia608ParseChannel( eia608_t *h, const uint8_t d[2] ) |
748 | 19.2k | { |
749 | | /* Check odd parity */ |
750 | 19.2k | static const int p4[16] = { |
751 | 19.2k | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 |
752 | 19.2k | }; |
753 | 19.2k | if( p4[d[0] & 0xf] == p4[d[0] >> 4] || |
754 | 19.2k | p4[d[1] & 0xf] == p4[ d[1] >> 4] ) |
755 | 231 | { |
756 | 231 | h->i_channel = -1; |
757 | 231 | return; |
758 | 231 | } |
759 | | |
760 | | /* */ |
761 | 18.9k | const int d1 = d[0] & 0x7f; |
762 | 18.9k | if( d1 >= 0x10 && d1 <= 0x1f ) |
763 | 4.18k | h->i_channel = 1 + ((d1 & 0x08) != 0); |
764 | 14.8k | else if( d1 < 0x10 ) |
765 | 3.12k | h->i_channel = 3; |
766 | 18.9k | } |
767 | | static eia608_status_t Eia608ParseTextAttribute( eia608_t *h, uint8_t d2 ) |
768 | 0 | { |
769 | 0 | const int i_index = d2 - 0x20; |
770 | 0 | assert( d2 >= 0x20 && d2 <= 0x2f ); |
771 | | |
772 | 0 | Debug(printf("[TA %d]", i_index)); |
773 | 0 | h->color = pac2_attribs[i_index].i_color; |
774 | 0 | h->font = pac2_attribs[i_index].i_font; |
775 | 0 | Eia608Cursor( h, 1 ); |
776 | |
|
777 | 0 | return EIA608_STATUS_DEFAULT; |
778 | 0 | } |
779 | | static eia608_status_t Eia608ParseSingle( eia608_t *h, const uint8_t dx ) |
780 | 4.67k | { |
781 | 4.67k | assert( dx >= 0x20 ); |
782 | 4.67k | Eia608Write( h, dx ); |
783 | 4.67k | return EIA608_STATUS_CHANGED; |
784 | 4.67k | } |
785 | | static eia608_status_t Eia608ParseDouble( eia608_t *h, uint8_t d2 ) |
786 | 2 | { |
787 | 2 | assert( d2 >= 0x30 && d2 <= 0x3f ); |
788 | 2 | Debug(printf("\033[0;33m%s\033[0m", d2 + 0x50)); |
789 | 2 | Eia608Write( h, d2 + 0x50 ); /* We use characters 0x80...0x8f */ |
790 | 2 | return EIA608_STATUS_CHANGED; |
791 | 2 | } |
792 | | static eia608_status_t Eia608ParseExtended( eia608_t *h, uint8_t d1, uint8_t d2 ) |
793 | 0 | { |
794 | 0 | assert( d2 >= 0x20 && d2 <= 0x3f ); |
795 | 0 | assert( d1 == 0x12 || d1 == 0x13 ); |
796 | 0 | if( d1 == 0x12 ) |
797 | 0 | d2 += 0x70; /* We use characters 0x90-0xaf */ |
798 | 0 | else |
799 | 0 | d2 += 0x90; /* We use characters 0xb0-0xcf */ |
800 | |
|
801 | 0 | Debug(printf("[EXT %x->'%c']", d2, d2)); |
802 | | /* The extended characters replace the previous one with a more |
803 | | * advanced one */ |
804 | 0 | Eia608Cursor( h, -1 ); |
805 | 0 | Eia608Write( h, d2 ); |
806 | 0 | return EIA608_STATUS_CHANGED; |
807 | 0 | } |
808 | | static eia608_status_t Eia608ParseCommand0x14( eia608_t *h, uint8_t d2 ) |
809 | 633 | { |
810 | 633 | eia608_status_t i_status = EIA608_STATUS_DEFAULT; |
811 | 633 | eia608_mode_t proposed_mode; |
812 | | |
813 | 633 | switch( d2 ) |
814 | 633 | { |
815 | 0 | case 0x20: /* Resume caption loading */ |
816 | 0 | Debug(printf("[RCL]")); |
817 | 0 | h->mode = EIA608_MODE_POPUP; |
818 | 0 | break; |
819 | 14 | case 0x21: /* Backspace */ |
820 | 14 | Debug(printf("[BS]")); |
821 | 14 | Eia608Erase( h ); |
822 | 14 | i_status = EIA608_STATUS_CHANGED; |
823 | 14 | break; |
824 | 0 | case 0x22: /* Reserved */ |
825 | 0 | case 0x23: |
826 | 0 | Debug(printf("[ALARM %d]", d2 - 0x22)); |
827 | 0 | break; |
828 | 0 | case 0x24: /* Delete to end of row */ |
829 | 0 | Debug(printf("[DER]")); |
830 | 0 | Eia608EraseToEndOfRow( h ); |
831 | 0 | break; |
832 | 0 | case 0x25: /* Rollup 2 */ |
833 | 296 | case 0x26: /* Rollup 3 */ |
834 | 296 | case 0x27: /* Rollup 4 */ |
835 | 296 | Debug(printf("[RU%d]", d2 - 0x23)); |
836 | 296 | if( h->mode == EIA608_MODE_POPUP || h->mode == EIA608_MODE_PAINTON ) |
837 | 142 | { |
838 | 142 | Eia608EraseScreen( h, true ); |
839 | 142 | Eia608EraseScreen( h, false ); |
840 | 142 | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_CLEARED; |
841 | 142 | } |
842 | | |
843 | 296 | if( d2 == 0x25 ) |
844 | 0 | proposed_mode = EIA608_MODE_ROLLUP_2; |
845 | 296 | else if( d2 == 0x26 ) |
846 | 296 | proposed_mode = EIA608_MODE_ROLLUP_3; |
847 | 0 | else |
848 | 0 | proposed_mode = EIA608_MODE_ROLLUP_4; |
849 | | |
850 | 296 | if ( proposed_mode != h->mode ) |
851 | 142 | { |
852 | 142 | h->mode = proposed_mode; |
853 | 142 | h->cursor.i_column = 0; |
854 | 142 | h->cursor.i_row = h->i_row_rollup; |
855 | 142 | } |
856 | 296 | break; |
857 | 0 | case 0x28: /* Flash on */ |
858 | 0 | Debug(printf("[FON]")); |
859 | | /* TODO */ |
860 | 0 | break; |
861 | 3 | case 0x29: /* Resume direct captionning */ |
862 | 3 | Debug(printf("[RDC]")); |
863 | 3 | h->mode = EIA608_MODE_PAINTON; |
864 | 3 | break; |
865 | 0 | case 0x2a: /* Text restart */ |
866 | 0 | Debug(printf("[TR]")); |
867 | | /* TODO */ |
868 | 0 | break; |
869 | | |
870 | 3 | case 0x2b: /* Resume text display */ |
871 | 3 | Debug(printf("[RTD]")); |
872 | 3 | h->mode = EIA608_MODE_TEXT; |
873 | 3 | break; |
874 | | |
875 | 4 | case 0x2c: /* Erase displayed memory */ |
876 | 4 | Debug(printf("[EDM]")); |
877 | 4 | Eia608EraseScreen( h, true ); |
878 | 4 | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_CLEARED; |
879 | 4 | break; |
880 | 306 | case 0x2d: /* Carriage return */ |
881 | 306 | Debug(printf("[CR]")); |
882 | 306 | Eia608RollUp(h); |
883 | 306 | i_status = EIA608_STATUS_CHANGED; |
884 | 306 | break; |
885 | 5 | case 0x2e: /* Erase non displayed memory */ |
886 | 5 | Debug(printf("[ENM]")); |
887 | 5 | Eia608EraseScreen( h, false ); |
888 | 5 | break; |
889 | 2 | case 0x2f: /* End of caption (flip screen if not paint on) */ |
890 | 2 | Debug(printf("[EOC]")); |
891 | 2 | if( h->mode != EIA608_MODE_PAINTON ) |
892 | 0 | h->i_screen = 1 - h->i_screen; |
893 | 2 | h->mode = EIA608_MODE_POPUP; |
894 | 2 | h->cursor.i_column = 0; |
895 | 2 | h->cursor.i_row = 0; |
896 | 2 | h->color = EIA608_COLOR_DEFAULT; |
897 | 2 | h->font = EIA608_FONT_REGULAR; |
898 | 2 | i_status = EIA608_STATUS_CHANGED | EIA608_STATUS_CAPTION_ENDED; |
899 | 2 | break; |
900 | 633 | } |
901 | 633 | return i_status; |
902 | 633 | } |
903 | | static bool Eia608ParseCommand0x17( eia608_t *h, uint8_t d2 ) |
904 | 5 | { |
905 | 5 | switch( d2 ) |
906 | 5 | { |
907 | 0 | case 0x21: /* Tab offset 1 */ |
908 | 5 | case 0x22: /* Tab offset 2 */ |
909 | 5 | case 0x23: /* Tab offset 3 */ |
910 | 5 | Debug(printf("[TO%d]", d2 - 0x20)); |
911 | 5 | Eia608Cursor( h, d2 - 0x20 ); |
912 | 5 | break; |
913 | 5 | } |
914 | 5 | return false; |
915 | 5 | } |
916 | | static bool Eia608ParsePac( eia608_t *h, uint8_t d1, uint8_t d2 ) |
917 | 511 | { |
918 | 511 | static const int pi_row[] = { |
919 | 511 | 11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10 |
920 | 511 | }; |
921 | 511 | const int i_row_index = ( (d1<<1) & 0x0e) | ( (d2>>5) & 0x01 ); |
922 | | |
923 | 511 | Debug(printf("[PAC,%d]", i_row_index)); |
924 | 511 | assert( d2 >= 0x40 && d2 <= 0x7f ); |
925 | | |
926 | 511 | if( pi_row[i_row_index] <= 0 ) |
927 | 0 | return false; |
928 | | |
929 | | /* Row */ |
930 | 511 | if( h->mode != EIA608_MODE_TEXT ) |
931 | 457 | h->cursor.i_row = pi_row[i_row_index] - 1; |
932 | 511 | h->i_row_rollup = pi_row[i_row_index] - 1; |
933 | | /* Column */ |
934 | 511 | if( d2 >= 0x60 ) |
935 | 451 | d2 -= 0x60; |
936 | 60 | else if( d2 >= 0x40 ) |
937 | 60 | d2 -= 0x40; |
938 | 511 | h->cursor.i_column = pac2_attribs[d2].i_column; |
939 | 511 | h->color = pac2_attribs[d2].i_color; |
940 | 511 | h->font = pac2_attribs[d2].i_font; |
941 | | |
942 | 511 | return false; |
943 | 511 | } |
944 | | |
945 | | static eia608_status_t Eia608ParseData( eia608_t *h, uint8_t d1, uint8_t d2 ) |
946 | 3.56k | { |
947 | 3.56k | eia608_status_t i_status = EIA608_STATUS_DEFAULT; |
948 | | |
949 | 3.56k | if( d1 >= 0x18 && d1 <= 0x1f ) |
950 | 0 | d1 -= 8; |
951 | | |
952 | 3.56k | #define ON( d2min, d2max, cmd ) do { if( d2 >= d2min && d2 <= d2max ) i_status = cmd; } while(0) |
953 | 3.56k | switch( d1 ) |
954 | 3.56k | { |
955 | 5 | case 0x11: |
956 | 5 | ON( 0x20, 0x2f, Eia608ParseTextAttribute( h, d2 ) ); |
957 | 5 | ON( 0x30, 0x3f, Eia608ParseDouble( h, d2 ) ); |
958 | 5 | break; |
959 | 0 | case 0x12: case 0x13: |
960 | 0 | ON( 0x20, 0x3f, Eia608ParseExtended( h, d1, d2 ) ); |
961 | 0 | break; |
962 | 1.14k | case 0x14: case 0x15: |
963 | 1.14k | ON( 0x20, 0x2f, Eia608ParseCommand0x14( h, d2 ) ); |
964 | 1.14k | break; |
965 | 5 | case 0x17: |
966 | 5 | ON( 0x21, 0x23, Eia608ParseCommand0x17( h, d2 ) ); |
967 | 5 | ON( 0x2e, 0x2f, Eia608ParseTextAttribute( h, d2 ) ); |
968 | 5 | break; |
969 | 3.56k | } |
970 | 3.56k | if( d1 == 0x10 ) |
971 | 0 | ON( 0x40, 0x5f, Eia608ParsePac( h, d1, d2 ) ); |
972 | 3.56k | else if( d1 >= 0x11 && d1 <= 0x17 ) |
973 | 1.15k | ON( 0x40, 0x7f, Eia608ParsePac( h, d1, d2 ) ); |
974 | 3.56k | #undef ON |
975 | 3.56k | if( d1 >= 0x20 ) |
976 | 2.41k | { |
977 | 2.41k | Debug(printf("\033[0;33m%c", d1)); |
978 | 2.41k | i_status = Eia608ParseSingle( h, d1 ); |
979 | 2.41k | if( d2 >= 0x20 ) |
980 | 2.26k | { |
981 | 2.26k | Debug(printf("%c", d2)); |
982 | 2.26k | i_status |= Eia608ParseSingle( h, d2 ); |
983 | 2.26k | } |
984 | 2.41k | Debug(printf("\033[0m")); |
985 | 2.41k | } |
986 | | |
987 | | /* Ignore changes occurring to doublebuffer */ |
988 | 3.56k | if( h->mode == EIA608_MODE_POPUP && i_status == EIA608_STATUS_CHANGED ) |
989 | 1.33k | i_status = EIA608_STATUS_DEFAULT; |
990 | | |
991 | 3.56k | return i_status; |
992 | 3.56k | } |
993 | | |
994 | | static void Eia608TextUtf8( char *psz_utf8, uint8_t c ) // Returns number of bytes used |
995 | 14.2k | { |
996 | 185k | #define E1(c,u) { c, { u, '\0' } } |
997 | 971k | #define E2(c,u1,u2) { c, { u1, u2, '\0' } } |
998 | 128k | #define E3(c,u1,u2,u3) { c, { u1, u2, u3, '\0' } } |
999 | 14.2k | static const struct { |
1000 | 14.2k | uint8_t c; |
1001 | 14.2k | char utf8[3+1]; |
1002 | 14.2k | } c2utf8[] = { |
1003 | | // Regular line-21 character set, mostly ASCII except these exceptions |
1004 | 14.2k | E2( 0x2a, 0xc3,0xa1), // lowercase a, acute accent |
1005 | 14.2k | E2( 0x5c, 0xc3,0xa9), // lowercase e, acute accent |
1006 | 14.2k | E2( 0x5e, 0xc3,0xad), // lowercase i, acute accent |
1007 | 14.2k | E2( 0x5f, 0xc3,0xb3), // lowercase o, acute accent |
1008 | 14.2k | E2( 0x60, 0xc3,0xba), // lowercase u, acute accent |
1009 | 14.2k | E2( 0x7b, 0xc3,0xa7), // lowercase c with cedilla |
1010 | 14.2k | E2( 0x7c, 0xc3,0xb7), // division symbol |
1011 | 14.2k | E2( 0x7d, 0xc3,0x91), // uppercase N tilde |
1012 | 14.2k | E2( 0x7e, 0xc3,0xb1), // lowercase n tilde |
1013 | | // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS |
1014 | | // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F |
1015 | 14.2k | E2( 0x80, 0xc2,0xae), // Registered symbol (R) |
1016 | 14.2k | E2( 0x81, 0xc2,0xb0), // degree sign |
1017 | 14.2k | E2( 0x82, 0xc2,0xbd), // 1/2 symbol |
1018 | 14.2k | E2( 0x83, 0xc2,0xbf), // Inverted (open) question mark |
1019 | 14.2k | E3( 0x84, 0xe2,0x84,0xa2), // Trademark symbol (TM) |
1020 | 14.2k | E2( 0x85, 0xc2,0xa2), // Cents symbol |
1021 | 14.2k | E2( 0x86, 0xc2,0xa3), // Pounds sterling |
1022 | 14.2k | E3( 0x87, 0xe2,0x99,0xaa), // Music note |
1023 | 14.2k | E2( 0x88, 0xc3,0xa0), // lowercase a, grave accent |
1024 | 14.2k | E2( 0x89, 0xc2,0xa0), // transparent space |
1025 | 14.2k | E2( 0x8a, 0xc3,0xa8), // lowercase e, grave accent |
1026 | 14.2k | E2( 0x8b, 0xc3,0xa2), // lowercase a, circumflex accent |
1027 | 14.2k | E2( 0x8c, 0xc3,0xaa), // lowercase e, circumflex accent |
1028 | 14.2k | E2( 0x8d, 0xc3,0xae), // lowercase i, circumflex accent |
1029 | 14.2k | E2( 0x8e, 0xc3,0xb4), // lowercase o, circumflex accent |
1030 | 14.2k | E2( 0x8f, 0xc3,0xbb), // lowercase u, circumflex accent |
1031 | | // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS |
1032 | | // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F |
1033 | 14.2k | E2( 0x90, 0xc3,0x81), // capital letter A with acute |
1034 | 14.2k | E2( 0x91, 0xc3,0x89), // capital letter E with acute |
1035 | 14.2k | E2( 0x92, 0xc3,0x93), // capital letter O with acute |
1036 | 14.2k | E2( 0x93, 0xc3,0x9a), // capital letter U with acute |
1037 | 14.2k | E2( 0x94, 0xc3,0x9c), // capital letter U with diaeresis |
1038 | 14.2k | E2( 0x95, 0xc3,0xbc), // lowercase letter U with diaeresis |
1039 | 14.2k | E1( 0x96, 0x27), // apostrophe |
1040 | 14.2k | E2( 0x97, 0xc2,0xa1), // inverted exclamation mark |
1041 | 14.2k | E1( 0x98, 0x2a), // asterisk |
1042 | 14.2k | E1( 0x99, 0x27), // apostrophe (yes, duped). See CCADI source code. |
1043 | 14.2k | E1( 0x9a, 0x2d), // hyphen-minus |
1044 | 14.2k | E2( 0x9b, 0xc2,0xa9), // copyright sign |
1045 | 14.2k | E3( 0x9c, 0xe2,0x84,0xa0), // Service mark |
1046 | 14.2k | E1( 0x9d, 0x2e), // Full stop (.) |
1047 | 14.2k | E3( 0x9e, 0xe2,0x80,0x9c), // Quotation mark |
1048 | 14.2k | E3( 0x9f, 0xe2,0x80,0x9d), // Quotation mark |
1049 | 14.2k | E2( 0xa0, 0xc3,0x80), // uppercase A, grave accent |
1050 | 14.2k | E2( 0xa1, 0xc3,0x82), // uppercase A, circumflex |
1051 | 14.2k | E2( 0xa2, 0xc3,0x87), // uppercase C with cedilla |
1052 | 14.2k | E2( 0xa3, 0xc3,0x88), // uppercase E, grave accent |
1053 | 14.2k | E2( 0xa4, 0xc3,0x8a), // uppercase E, circumflex |
1054 | 14.2k | E2( 0xa5, 0xc3,0x8b), // capital letter E with diaeresis |
1055 | 14.2k | E2( 0xa6, 0xc3,0xab), // lowercase letter e with diaeresis |
1056 | 14.2k | E2( 0xa7, 0xc3,0x8e), // uppercase I, circumflex |
1057 | 14.2k | E2( 0xa8, 0xc3,0x8f), // uppercase I, with diaeresis |
1058 | 14.2k | E2( 0xa9, 0xc3,0xaf), // lowercase i, with diaeresis |
1059 | 14.2k | E2( 0xaa, 0xc3,0x94), // uppercase O, circumflex |
1060 | 14.2k | E2( 0xab, 0xc3,0x99), // uppercase U, grave accent |
1061 | 14.2k | E2( 0xac, 0xc3,0xb9), // lowercase u, grave accent |
1062 | 14.2k | E2( 0xad, 0xc3,0x9b), // uppercase U, circumflex |
1063 | 14.2k | E2( 0xae, 0xc2,0xab), // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK |
1064 | 14.2k | E2( 0xaf, 0xc2,0xbb), // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK |
1065 | | // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS |
1066 | | // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F |
1067 | 14.2k | E2( 0xb0, 0xc3,0x83), // Uppercase A, tilde |
1068 | 14.2k | E2( 0xb1, 0xc3,0xa3), // Lowercase a, tilde |
1069 | 14.2k | E2( 0xb2, 0xc3,0x8d), // Uppercase I, acute accent |
1070 | 14.2k | E2( 0xb3, 0xc3,0x8c), // Uppercase I, grave accent |
1071 | 14.2k | E2( 0xb4, 0xc3,0xac), // Lowercase i, grave accent |
1072 | 14.2k | E2( 0xb5, 0xc3,0x92), // Uppercase O, grave accent |
1073 | 14.2k | E2( 0xb6, 0xc3,0xb2), // Lowercase o, grave accent |
1074 | 14.2k | E2( 0xb7, 0xc3,0x95), // Uppercase O, tilde |
1075 | 14.2k | E2( 0xb8, 0xc3,0xb5), // Lowercase o, tilde |
1076 | 14.2k | E1( 0xb9, 0x7b), // Open curly brace |
1077 | 14.2k | E1( 0xba, 0x7d), // Closing curly brace |
1078 | 14.2k | E1( 0xbb, 0x5c), // Backslash |
1079 | 14.2k | E1( 0xbc, 0x5e), // Caret |
1080 | 14.2k | E1( 0xbd, 0x5f), // Underscore |
1081 | 14.2k | E2( 0xbe, 0xc2,0xa6), // Pipe (broken bar) |
1082 | 14.2k | E1( 0xbf, 0x7e), // Tilde (utf8 code unsure) |
1083 | 14.2k | E2( 0xc0, 0xc3,0x84), // Uppercase A, umlaut |
1084 | 14.2k | E2( 0xc1, 0xc3,0xa4), // Lowercase A, umlaut |
1085 | 14.2k | E2( 0xc2, 0xc3,0x96), // Uppercase O, umlaut |
1086 | 14.2k | E2( 0xc3, 0xc3,0xb6), // Lowercase o, umlaut |
1087 | 14.2k | E2( 0xc4, 0xc3,0x9f), // Esszett (sharp S) |
1088 | 14.2k | E2( 0xc5, 0xc2,0xa5), // Yen symbol |
1089 | 14.2k | E2( 0xc6, 0xc2,0xa4), // Currency symbol |
1090 | 14.2k | E1( 0xc7, 0x7c), // Vertical bar |
1091 | 14.2k | E2( 0xc8, 0xc3,0x85), // Uppercase A, ring |
1092 | 14.2k | E2( 0xc9, 0xc3,0xa5), // Lowercase A, ring |
1093 | 14.2k | E2( 0xca, 0xc3,0x98), // Uppercase O, slash |
1094 | 14.2k | E2( 0xcb, 0xc3,0xb8), // Lowercase o, slash |
1095 | 14.2k | E3( 0xcc, 0xe2,0x8c,0x9c), // Upper left corner |
1096 | 14.2k | E3( 0xcd, 0xe2,0x8c,0x9d), // Upper right corner |
1097 | 14.2k | E3( 0xce, 0xe2,0x8c,0x9e), // Lower left corner |
1098 | 14.2k | E3( 0xcf, 0xe2,0x8c,0x9f), // Lower right corner |
1099 | | |
1100 | 14.2k | E1(0,0) |
1101 | 14.2k | }; |
1102 | 14.2k | #undef E3 |
1103 | 14.2k | #undef E2 |
1104 | 14.2k | #undef E1 |
1105 | | |
1106 | 1.29M | for( size_t i = 0; i < ARRAY_SIZE(c2utf8) ; i++ ) |
1107 | 1.28M | if( c2utf8[i].c == c ) { |
1108 | 72 | strcpy( psz_utf8, c2utf8[i].utf8 ); |
1109 | 72 | return; |
1110 | 72 | } |
1111 | | |
1112 | 14.2k | psz_utf8[0] = c < 0x80 ? c : '?'; /* Normal : Unsupported */ |
1113 | 14.2k | psz_utf8[1] = '\0'; |
1114 | 14.2k | } |
1115 | | |
1116 | | static void Eia608Strlcat( char *d, const char *s, int i_max ) |
1117 | 14.2k | { |
1118 | 14.2k | if( i_max > 1 ) |
1119 | 14.2k | strncat( d, s, i_max-1 - strnlen(d, i_max-1)); |
1120 | 14.2k | if( i_max > 0 ) |
1121 | 14.2k | d[i_max-1] = '\0'; |
1122 | 14.2k | } |
1123 | | |
1124 | 14.2k | #define CAT(t) Eia608Strlcat( psz_text, t, i_text_max ) |
1125 | | |
1126 | | static text_segment_t * Eia608TextLine( struct eia608_screen *screen, int i_row ) |
1127 | 1.83k | { |
1128 | 1.83k | const uint8_t *p_char = screen->characters[i_row]; |
1129 | 1.83k | const eia608_color_t *p_color = screen->colors[i_row]; |
1130 | 1.83k | const eia608_font_t *p_font = screen->fonts[i_row]; |
1131 | 1.83k | int i_start; |
1132 | 1.83k | int i_end; |
1133 | 1.83k | int x; |
1134 | 1.83k | eia608_color_t prev_color = EIA608_COLOR_DEFAULT; |
1135 | 1.83k | eia608_font_t prev_font = EIA608_FONT_REGULAR; |
1136 | | |
1137 | 1.83k | char utf8[4]; |
1138 | 1.83k | const unsigned i_text_max = 4 * EIA608_SCREEN_COLUMNS + 1; |
1139 | 1.83k | char psz_text[i_text_max + 1]; |
1140 | 1.83k | psz_text[0] = '\0'; |
1141 | | |
1142 | | /* Search the start */ |
1143 | 1.83k | i_start = 0; |
1144 | | |
1145 | | /* Convert leading spaces to non-breaking so that they don't get |
1146 | | stripped by the RenderHtml routine as regular whitespace */ |
1147 | 1.88k | while( i_start < EIA608_SCREEN_COLUMNS && p_char[i_start] == ' ' ) { |
1148 | 52 | Eia608TextUtf8( utf8, 0x89 ); |
1149 | 52 | CAT( utf8 ); |
1150 | 52 | i_start++; |
1151 | 52 | } |
1152 | | |
1153 | | /* Search the end */ |
1154 | 1.83k | i_end = EIA608_SCREEN_COLUMNS-1; |
1155 | 46.1k | while( i_end > i_start && p_char[i_end] == ' ' ) |
1156 | 44.2k | i_end--; |
1157 | | |
1158 | | /* */ |
1159 | 1.83k | if( i_start > i_end ) /* Nothing to render */ |
1160 | 0 | return NULL; |
1161 | | |
1162 | 1.83k | text_segment_t *p_segment, *p_segments_head = p_segment = text_segment_New( NULL ); |
1163 | 1.83k | if(!p_segment) |
1164 | 0 | return NULL; |
1165 | | |
1166 | 1.83k | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
1167 | 1.83k | if(!p_segment->style) |
1168 | 0 | { |
1169 | 0 | text_segment_Delete(p_segment); |
1170 | 0 | return NULL; |
1171 | 0 | } |
1172 | | /* Ensure we get a monospaced font (required for accurate positioning */ |
1173 | 1.83k | p_segment->style->i_style_flags |= STYLE_MONOSPACED; |
1174 | | |
1175 | 16.0k | for( x = i_start; x <= i_end; x++ ) |
1176 | 14.2k | { |
1177 | 14.2k | eia608_color_t color = p_color[x]; |
1178 | 14.2k | eia608_font_t font = p_font[x]; |
1179 | | |
1180 | 14.2k | if(font != prev_font || color != prev_color) |
1181 | 26 | { |
1182 | 26 | EnsureUTF8(psz_text); |
1183 | 26 | p_segment->psz_text = strdup(psz_text); |
1184 | 26 | psz_text[0] = '\0'; |
1185 | 26 | p_segment->p_next = text_segment_New( NULL ); |
1186 | 26 | p_segment = p_segment->p_next; |
1187 | 26 | if(!p_segment) |
1188 | 0 | return p_segments_head; |
1189 | | |
1190 | 26 | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
1191 | 26 | if(!p_segment->style) |
1192 | 0 | { |
1193 | 0 | text_segment_Delete(p_segment); |
1194 | 0 | return p_segments_head; |
1195 | 0 | } |
1196 | 26 | p_segment->style->i_style_flags |= STYLE_MONOSPACED; |
1197 | | |
1198 | | /* start segment with new style */ |
1199 | 26 | if(font & EIA608_FONT_ITALICS) |
1200 | 0 | { |
1201 | 0 | p_segment->style->i_style_flags |= STYLE_ITALIC; |
1202 | 0 | p_segment->style->i_features |= STYLE_HAS_FLAGS; |
1203 | 0 | } |
1204 | 26 | if(font & EIA608_FONT_UNDERLINE) |
1205 | 0 | { |
1206 | 0 | p_segment->style->i_style_flags |= STYLE_UNDERLINE; |
1207 | 0 | p_segment->style->i_features |= STYLE_HAS_FLAGS; |
1208 | 0 | } |
1209 | | |
1210 | 26 | if(color != EIA608_COLOR_DEFAULT) |
1211 | 26 | { |
1212 | 26 | p_segment->style->i_font_color = rgi_eia608_colors[color]; |
1213 | 26 | p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; |
1214 | 26 | } |
1215 | 26 | } |
1216 | | |
1217 | 14.2k | Eia608TextUtf8( utf8, p_char[x] ); |
1218 | 14.2k | CAT( utf8 ); |
1219 | | |
1220 | | /* */ |
1221 | 14.2k | prev_font = font; |
1222 | 14.2k | prev_color = color; |
1223 | 14.2k | } |
1224 | | |
1225 | 1.83k | #undef CAT |
1226 | | |
1227 | 1.83k | if( p_segment ) |
1228 | 1.83k | { |
1229 | 1.83k | assert(!p_segment->psz_text); // shouldn't happen |
1230 | 1.83k | EnsureUTF8(psz_text); |
1231 | 1.83k | p_segment->psz_text = strdup(psz_text); |
1232 | 1.83k | } |
1233 | | |
1234 | 1.83k | return p_segments_head; |
1235 | 1.83k | } |
1236 | | |
1237 | | static void Eia608FillUpdaterRegions( subtext_updater_sys_t *p_updater, eia608_t *h ) |
1238 | 1.55k | { |
1239 | 1.55k | struct eia608_screen *screen = &h->screen[h->i_screen]; |
1240 | 1.55k | substext_updater_region_t *p_region = &p_updater->region; |
1241 | 1.55k | text_segment_t **pp_last = &p_region->p_segments; |
1242 | 1.55k | bool b_newregion = false; |
1243 | | |
1244 | 24.8k | for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) |
1245 | 23.2k | { |
1246 | 23.2k | if( !screen->row_used[i] ) |
1247 | 21.4k | continue; |
1248 | | |
1249 | 1.83k | text_segment_t *p_segments = Eia608TextLine( screen, i ); |
1250 | 1.83k | if( p_segments ) |
1251 | 1.83k | { |
1252 | 1.83k | if( b_newregion ) |
1253 | 0 | { |
1254 | 0 | substext_updater_region_t *p_newregion; |
1255 | 0 | p_newregion = SubpictureUpdaterSysRegionNew(); |
1256 | 0 | if( !p_newregion ) |
1257 | 0 | { |
1258 | 0 | text_segment_ChainDelete( p_segments ); |
1259 | 0 | return; |
1260 | 0 | } |
1261 | | /* Copy defaults */ |
1262 | 0 | p_newregion->align = p_region->align; |
1263 | 0 | p_newregion->inner_align = p_region->inner_align; |
1264 | 0 | p_newregion->flags = p_region->flags; |
1265 | 0 | SubpictureUpdaterSysRegionAdd( p_region, p_newregion ); |
1266 | 0 | p_region = p_newregion; |
1267 | 0 | pp_last = &p_region->p_segments; |
1268 | 0 | b_newregion = false; |
1269 | 0 | } |
1270 | | |
1271 | 1.83k | if( p_region->p_segments == NULL ) /* First segment in the [new] region */ |
1272 | 1.24k | { |
1273 | 1.24k | p_region->origin.y = (float) i /* start line number */ |
1274 | 1.24k | / (EIA608_SCREEN_ROWS * FONT_TO_LINE_HEIGHT_RATIO); |
1275 | 1.24k | p_region->flags |= UPDT_REGION_ORIGIN_Y_IS_RATIO; |
1276 | 1.24k | } |
1277 | 582 | else /* Insert line break between region lines */ |
1278 | 582 | { |
1279 | 582 | *pp_last = text_segment_New( "\n" ); |
1280 | 582 | if( *pp_last ) |
1281 | 582 | pp_last = &((*pp_last)->p_next); |
1282 | 582 | } |
1283 | | |
1284 | 1.83k | *pp_last = p_segments; |
1285 | 1.85k | do { pp_last = &((*pp_last)->p_next); } while ( *pp_last != NULL ); |
1286 | 1.83k | } |
1287 | 0 | else |
1288 | 0 | { |
1289 | 0 | b_newregion = !!p_region->p_segments; |
1290 | 0 | } |
1291 | 1.83k | } |
1292 | 1.55k | } |
1293 | | |
1294 | | /* */ |
1295 | | static void Eia608Init( eia608_t *h ) |
1296 | 1.12k | { |
1297 | 1.12k | memset( h, 0, sizeof(*h) ); |
1298 | | |
1299 | | /* */ |
1300 | 1.12k | h->i_channel = -1; |
1301 | | |
1302 | 1.12k | h->i_screen = 0; |
1303 | 1.12k | Eia608ClearScreen( h, 0 ); |
1304 | 1.12k | Eia608ClearScreen( h, 1 ); |
1305 | | |
1306 | | /* Cursor for writing text */ |
1307 | 1.12k | h->cursor.i_column = 0; |
1308 | 1.12k | h->cursor.i_row = 0; |
1309 | | |
1310 | 1.12k | h->last.d1 = 0x00; |
1311 | 1.12k | h->last.d2 = 0x00; |
1312 | 1.12k | h->mode = EIA608_MODE_POPUP; |
1313 | 1.12k | h->color = EIA608_COLOR_DEFAULT; |
1314 | 1.12k | h->font = EIA608_FONT_REGULAR; |
1315 | 1.12k | h->i_row_rollup = EIA608_SCREEN_ROWS-1; |
1316 | 1.12k | } |
1317 | | static eia608_status_t Eia608Parse( eia608_t *h, int i_channel_selected, const uint8_t data[2] ) |
1318 | 21.2k | { |
1319 | 21.2k | const uint8_t d1 = data[0] & 0x7f; /* Removed parity bit */ |
1320 | 21.2k | const uint8_t d2 = data[1] & 0x7f; |
1321 | 21.2k | eia608_status_t i_screen_status = EIA608_STATUS_DEFAULT; |
1322 | | |
1323 | 21.2k | if( d1 == 0 && d2 == 0 ) |
1324 | 2.04k | return EIA608_STATUS_DEFAULT; /* Ignore padding (parity check are sometimes invalid on them) */ |
1325 | | |
1326 | 19.2k | Eia608ParseChannel( h, data ); |
1327 | 19.2k | if( h->i_channel != i_channel_selected ) |
1328 | 14.6k | return false; |
1329 | | //fprintf( stderr, "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC %x %x\n", data[0], data[1] ); |
1330 | | |
1331 | 4.56k | if( d1 >= 0x10 ) |
1332 | 4.56k | { |
1333 | 4.56k | if( d1 >= 0x20 || |
1334 | 4.56k | d1 != h->last.d1 || d2 != h->last.d2 ) /* Command codes can be repeated */ |
1335 | 3.56k | i_screen_status = Eia608ParseData( h, d1,d2 ); |
1336 | | |
1337 | 4.56k | h->last.d1 = d1; |
1338 | 4.56k | h->last.d2 = d2; |
1339 | 4.56k | } |
1340 | 0 | else if( ( d1 >= 0x01 && d1 <= 0x0E ) || d1 == 0x0F ) |
1341 | 0 | { |
1342 | | /* XDS block / End of XDS block */ |
1343 | 0 | } |
1344 | 4.56k | return i_screen_status; |
1345 | 19.2k | } |